diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000000000..a6c57f5fb2ffb --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +*.json diff --git a/.cmake-format.py b/.cmake-format.py index 9827eecd329c4..ae092bc09f363 100644 --- a/.cmake-format.py +++ b/.cmake-format.py @@ -66,12 +66,6 @@ "HEADERS": '*', } }, - "o2_target_man_page": { - "kwargs": { - "NAME": '+', - "SECTION": '*', - } - }, "add_root_dictionary": { "kwargs": { "LINKDEF": '+', diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000..30ad6d8f005b3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +--- +# Dependabot configuration +# Reference: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/async-auto-label.yml b/.github/workflows/async-auto-label.yml new file mode 100644 index 0000000000000..b0a17c7faba26 --- /dev/null +++ b/.github/workflows/async-auto-label.yml @@ -0,0 +1,17 @@ +--- +name: Apply requested async label + +'on': + issue_comment: + types: + - created + - edited + +permissions: {} + +jobs: + apply_async_labels: + name: Apply requested async label + uses: alisw/ali-bot/.github/workflows/async-auto-label.yml@master + permissions: + pull-requests: write # to update labels diff --git a/.github/workflows/async-list-label.yml b/.github/workflows/async-list-label.yml new file mode 100644 index 0000000000000..e0b4185c563b7 --- /dev/null +++ b/.github/workflows/async-list-label.yml @@ -0,0 +1,19 @@ +--- +name: Collect and print async labels + +'on': + pull_request_target: + types: + - opened + - reopened + branches: + - dev + +permissions: {} + +jobs: + list_async_labels: + name: Collect and print async labels + uses: alisw/ali-bot/.github/workflows/async-list-label.yml@master + permissions: + pull-requests: write # to update labels diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index a48b6993522eb..0f15301d4eed9 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -19,10 +19,6 @@ name: Clean PR checks # Warning: the check_* keys are magic and must consist of the string # "check_" followed by the applicable check name exactly. The # "description" field is only the human-readable label for the input. - 'check_build/O2/o2': - description: build/O2/o2 - type: boolean - default: true 'check_build/AliceO2/O2/o2/macOS': description: build/AliceO2/O2/o2/macOS type: boolean @@ -31,23 +27,24 @@ name: Clean PR checks description: build/AliceO2/O2/o2/macOS-arm type: boolean default: true - 'check_build/O2/fullCI': + 'check_build/O2/fullCI_slc9': description: build/O2/fullCI type: boolean default: true - 'check_build/O2/o2-cs8': - description: build/O2/o2-cs8 + 'check_build/O2/o2-dataflow-cs8': + description: build/O2/o2-dataflow-cs8 type: boolean default: true - 'check_build/O2/o2-dataflow': - description: build/O2/o2-dataflow + 'check_build/O2/o2/aarch64': + description: build/O2/o2/aarch64 type: boolean default: true - 'check_build/O2/o2-dataflow-cs8': - description: build/O2/o2-dataflow-cs8 + 'check_build/O2/o2_slc9': + description: build/O2/o2_slc9 type: boolean default: true + permissions: {} jobs: diff --git a/.github/workflows/code-transformations.yml b/.github/workflows/code-transformations.yml index 4b5e55fcc2941..35493afda94f5 100644 --- a/.github/workflows/code-transformations.yml +++ b/.github/workflows/code-transformations.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/datamodel-doc.yml b/.github/workflows/datamodel-doc.yml index 8b07cbed9d54b..3ba015631aec6 100644 --- a/.github/workflows/datamodel-doc.yml +++ b/.github/workflows/datamodel-doc.yml @@ -10,20 +10,20 @@ jobs: steps: - name: Checkout O2 - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: path: O2 persist-credentials: false - name: Checkout O2Physics - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: AliceO2Group/O2Physics path: O2Physics persist-credentials: false - name: Checkout documentation - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: repository: AliceO2Group/analysis-framework path: analysis-framework @@ -40,7 +40,7 @@ jobs: git checkout -B auto-datamodel-doc - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v6 with: python-version: 3.x @@ -48,6 +48,7 @@ jobs: run: | python3 -m pip install --user -U numpy nltk python3 -m nltk.downloader -d ~/nltk_data punkt + python3 -m nltk.downloader -d ~/nltk_data punkt_tab - name: Generate documentation run: exec bash -exo pipefail O2/scripts/datamodel-doc/update-datamodel.sh @@ -59,6 +60,7 @@ jobs: run: | # git diff --quiet exits with 1 if any tracked files have changed, and # with 0 otherwise. + set -e if git diff --quiet; then exit # Nothing has changed, so no need to send a PR. fi @@ -68,9 +70,10 @@ jobs: git push -f origin auto-datamodel-doc # Send pull request - # We need to use "hub" ourselves because alisw/pull-request gets + # We need to use "gh" ourselves because alisw/pull-request gets # confused when multiple repos are checked out. - hub pull-request -b AliceO2Group:master -h alibuild:auto-datamodel-doc \ - --no-edit --no-maintainer-edits -m 'Automatic data model update' \ - -m "This update to the data model documentation was automatically created from tonight's O2 dev branch." || - : # If the PR already exists, hub fails, but we've just force-pushed, so we don't need a new PR. + GH_TOKEN="$GITHUB_TOKEN" gh pr create -R AliceO2Group/analysis-framework -B master \ + --no-maintainer-edit -t 'Automatic data model update' -b "This update \ + to the data model documentation was automatically created from \ + tonight's O2 dev branch." || true + # If the PR already exists, hub fails, but we've just force-pushed, so we don't need a new PR. diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 38da67c793799..b1dbaf122b342 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -13,7 +13,7 @@ jobs: run: | sudo apt-get update -y sudo apt-get install -y doxygen doxygen-doc doxygen-latex doxygen-gui graphviz cmake - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 with: ref: "dev" persist-credentials: false diff --git a/.github/workflows/first-timer.yml b/.github/workflows/first-timer.yml index 20b7ee6a070a8..54334d109bd49 100644 --- a/.github/workflows/first-timer.yml +++ b/.github/workflows/first-timer.yml @@ -8,7 +8,7 @@ jobs: nag_first_timer: runs-on: ubuntu-latest steps: - - uses: actions/first-interaction@v1 + - uses: actions/first-interaction@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} pr-message: 'This seems to be your first PR. You will need a positive review in order for tests to start.' diff --git a/.github/workflows/pr-security-approval.yml b/.github/workflows/pr-security-approval.yml new file mode 100644 index 0000000000000..9ae70fdb9933d --- /dev/null +++ b/.github/workflows/pr-security-approval.yml @@ -0,0 +1,18 @@ +--- +name: Security approval + +'on': + pull_request_review: + types: + - edited + - submitted + +permissions: {} + +jobs: + clean: + name: Security approval + uses: alisw/ali-bot/.github/workflows/pr-security-approval.yml@master + permissions: + pull-requests: read # to get last commit for PR + statuses: write # for set-github-status diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 894ff2e0bb49b..2f692527ea5ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: branch=$(echo ${{ github.event.inputs.tag }}-patches | tr . - | sed -e's/-[0-9]*-patches$/-patches/') EOF id: decide_release_branch - - uses: actions/checkout@v3 + - uses: actions/checkout@v5 with: ref: "dev" - name: Tag branch (or create one before tagging if does not exists) diff --git a/.github/workflows/reports.yml b/.github/workflows/reports.yml index 9045b8b8c8031..5a04e56382fb3 100644 --- a/.github/workflows/reports.yml +++ b/.github/workflows/reports.yml @@ -17,12 +17,12 @@ jobs: if: github.repository == 'AliceO2Group/AliceO2' steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - uses: actions/checkout@v5 + - name: Set up Python 3.10 + uses: actions/setup-python@v6 with: - python-version: 3.7 - - uses: actions/cache@v2 + python-version: '3.10' + - uses: actions/cache@v5 name: Configure pip caching with: path: ~/.cache/pip @@ -81,7 +81,7 @@ jobs: run: | set -x mkdir -p doc/data - # We create new files once per month, mostly so that + # We create new files once per month, mostly so that # we can keep the query results small. It does not # matter if we get results from different months, # as what matters is how we merge them. @@ -96,7 +96,6 @@ jobs: # being published LAST_RELEASE="${{ github.event.inputs.LAST_RELEASE_DATE }}" MERGED_AFTER=${LAST_RELEASE:-$(date -v -14d +%Y-%m-%d)} - # Here we convert all the json files to per subsystem # logs, using the MERGED_AFTER date to further filter them. # Notice we can have duplicates in each file, @@ -106,7 +105,7 @@ jobs: for f in doc/data/*_prs.json; do for x in Algorithm Analysis Common DataFormats Detectors EventVisualisation Examples Framework Generators Steer Testing Utilities; do cat $f | jq ".repository.pullRequests.edges[].node | select(.files.edges[].node.path | test(\"$x\")) | del(.files) | select(.state == \"MERGED\" and .mergedAt >= \"${MERGED_AFTER}\")" > /tmp/${x}_prs.json - if [ ! X`jq -s length /tmp/${x}_prs.json` = X0 ]; then + if [ ! X`jq -s length /tmp/${x}_prs.json` = X0 ]; then cat /tmp/${x}_prs.json | jq -r '"- [#\(.number)](https://github.com/AliceO2Group/AliceO2/pull/\(.number)) \(.mergedAt | split("T")[0]): \(.title) by [@\(.author.login)](https://github.com/\(.author.login))"' | sort -u >> /tmp/${x}_prs.md fi done @@ -127,11 +126,6 @@ jobs: git config --global user.name "GitHub Action Bot" git commit -m "Updated README" -a || echo "No changes to commit" git push origin HEAD:changelog -f - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \ - hub pull-request -f -b dev -h changelog \ - --no-edit --no-maintainer-edits \ - 'Auto-generated changelog' \ - -m 'The following changelog has been automatically generated.' || - # If the PR already exists, the force-push will have updated it. - # It's fine if this step fails. - true + # If the PR already exists, the force-push will have updated it. + # It's fine if this step fails. + GH_TOKEN=${{ secrets.GITHUB_TOKEN }} gh pr create -B dev -H changelog -t 'Auto-generated changelog' -b 'The following changelog has been automatically generated.' || true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1f1387d4868ae..23f454aaca950 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v1 + - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: 'This PR did not have any update in the last 30 days. Is it still needed? Unless further action in will be closed in 5 days.' diff --git a/.gitignore b/.gitignore index a489daad80115..d58d1e151800b 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ compile_commands.json .vscode .ycm_extra_conf.py Session.vim +CMakeLists.txt.user # Datafiles gphysi.dat @@ -81,6 +82,9 @@ bazel-* # direnv .envrc +# git wrappers +.sl + # LSP support on macOS with vim .clangd DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h diff --git a/Algorithm/CMakeLists.txt b/Algorithm/CMakeLists.txt index b245562c7cc93..ed7a42a96e528 100644 --- a/Algorithm/CMakeLists.txt +++ b/Algorithm/CMakeLists.txt @@ -11,9 +11,6 @@ o2_add_header_only_library(Algorithm INTERFACE_LINK_LIBRARIES O2::Headers) -o2_target_man_page(Algorithm NAME Algorithm SECTION 3) -o2_target_man_page(Algorithm NAME algorithm_parser SECTION 3) - o2_add_test(o2formatparser SOURCES test/o2formatparser.cxx COMPONENT_NAME Algorithm diff --git a/Algorithm/doc/Algorithm.3.in b/Algorithm/doc/Algorithm.3.in deleted file mode 100644 index eaf618ee68da2..0000000000000 --- a/Algorithm/doc/Algorithm.3.in +++ /dev/null @@ -1,12 +0,0 @@ -.\" Alice O2 manpage for module Algorithm -.TH "AliceO2" 3 "17 Jan 2017" "1.0" "Algorithm man page" - -.SH NAME -AliceO2 - module -.B Algorithm - -.SH DESCRIPTION -A collection of generic algorithms for Alice O2 - -.SH SEE ALSO -algorithm_parser(3) diff --git a/Algorithm/doc/algorithm_parser.3.in b/Algorithm/doc/algorithm_parser.3.in deleted file mode 100644 index 98f45df279669..0000000000000 --- a/Algorithm/doc/algorithm_parser.3.in +++ /dev/null @@ -1,135 +0,0 @@ -.\" Alice O2 manpage for parser algorithms -.TH "AliceO2" 3 "17 Jan 2017" "1.0" "Algorithm Parser man page" - -.SH NAME -AliceO2 - module -.B Algorithm -- data parsers - -.SH SYNOPSIS -.B ForwardParser< -.I SomeHeaderType -, -.I SomeTrailerType -.B > - -.B ReverseParser< -.I SomeHeaderType -, -.I SomeTrailerType -.B > - -.SS Public types -.TP 2 -// a compound of header, data, and trailer -.B struct FrameInfo { - using PtrT = const PayloadType*; - const HeaderType* header = nullptr; - const TrailerType* trailer = nullptr; - PtrT payload = nullptr; - size_t length = 0; - -.B }; - -.TP 2 -.B using CheckHeaderFct = std::function; -alias for callback checking the header, return true if the object is a valid header -.TP 2 -.B using CheckTrailerFct = std::function; -alias for callback checking the trailer -.TP 2 -.B using GetFrameSizeFct = std::function; -alias for callback to get the complete frame size including header, trailer and the data -.TP 2 -.B using InsertFct = std::function; -function callback to insert/handle one frame into, sequentially called for all frames if the whole block has a valid format - -.SS Public member functions -.TP 2 -.B template -.B int parse(const InputType* \fIbuffer\fB, size_t \fIbufferSize\fB, CheckHeaderFct \fIcheckHeader\fB, CheckTrailerFct \fIcheckTrailer\fB, GetFrameSizeFct \fIgetFrameSize\fB, InsertFct \fIinsert\fB) - -.SS Public member variables -.TP 2 -.B static const size_t headOffset = typesize::size; -the length offset due to header -.TP 2 -.B static const size_t tailOffset = typesize::size; -the length offset due to trailer -.TP 2 -.B static const size_t totalOffset = headOffset + tailOffset; -total length offset due to header and trailer - -.SH DESCRIPTION -Template utilities for parsing of data sequences. Each entry in the sequence consist of a header, variable payload, and optionally a trailer. The three parts are collected in the FrameInfo structure for every entry. - -Callback functions for checking header and trailer integrity, getting length of the current frame and handling of a frame. - -.SS ForwardParser -The size is expected to be part of the header, parsing starts at beginning of buffer. -Trailer type can be void, which is also the default template parameter. That -allows to define a frame consisting of only header and data. - -.SS ReverseParser -The size is expected to be part of the trailer, the parsing is thus in reverse direction. Also the insert callback is called with the entries starting form the end of the buffer. -An easy extension can be to reverse the order of the inserts, meaning that the entries are read from the beginning. - -.SH EXAMPLES -.SS ReverseParser example -.EX -using SomeParser = ReverseParser; -SomeParser parser; -std::vector frames; -parser.parse(ptr, size, - [] (const typename SomeParser::HeaderType& h) { - // check the header - return true; - }, - [] (const typename SomeParser::TrailerType& t) { - // check the trailer - return true; - }, - [] (const typename SomeParser::TrailerType& t) { - // get the size of the frame including payload - // and header and trailer size, e.g. payload size - // from a trailer member - return t.payloadSize + SomeParser::totalOffset; - }, - [&frames] (typename SomeParser::FrameInfo& info) { - frames.emplace_back(info); - return true; - } - ) -.EE - -.SS ForwardParser example with frame consisting of header and payload -.EX -using SomeParser = ForwardParser; -SomeParser parser; -std::vector frames; -parser.parse(ptr, size, - [] (const typename SomeParser::HeaderType& h) { - // check the header - return true; - }, - [] (const typename SomeParser::HeaderType& h) { - // get the size of the frame including payload - // and header and trailer size, e.g. payload size - // from a header member - return h.payloadSize + SomeParser::totalOffset; - }, - [&frames] (typename SomeParser::FrameInfo& info) { - frames.emplace_back(info); - return true; - } - ) -.EE - -.SH BUGS, CONTRIBUTIONS -Please add an issue to -.UR https://github.com/AliceO2Group/AliceO2/issues -.UE - -.SH SEE ALSO -.UR https://github.com/AliceO2Group/AliceO2/blob/dev/Algorithm/include/Algorithm/Parser.h -.UE diff --git a/Algorithm/include/Algorithm/BitstreamReader.h b/Algorithm/include/Algorithm/BitstreamReader.h deleted file mode 100644 index 0a112183ab5ef..0000000000000 --- a/Algorithm/include/Algorithm/BitstreamReader.h +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef BITSTREAMREADER_H -#define BITSTREAMREADER_H - -/// @file BitstreamReader.h -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Utility class to provide bitstream access to an underlying resource - -#include -#include - -namespace o2 -{ -namespace algorithm -{ - -/// @class BitStreamReader -/// @brief Utility class to provide bitstream access to an underlying resource -/// -/// Allows to access bits of variable length, supports integral types and also -/// bitsets as target type. At the moment, the access is in direction MSB -> LSB. -/// -/// BitstreamReader reader(start, end); -/// while (reader.good() && not reader.eof()) { -/// // get an 8 bit value from the stream, moves the position -/// uint8_t ivalue; -/// reader.get(ivalue); -/// -/// // get a 13 bit bitset without moving the position -/// std::bitset<13> value; -/// reader.peek(value, value.size()); -/// // e.g. use 7 bits of the data -/// value >>= value.size() - 7; -/// // move position by the specific number of bits -/// reader.seek(7); -/// } -template -class BitstreamReader -{ - public: - using self_type = BitstreamReader; - // for the moment we simply use pointers, but with some traits this can be extended to - // containers - using value_type = BufferType; - using iterator = const value_type*; - static constexpr size_t value_size = sizeof(value_type) * 8; - BitstreamReader() = delete; - BitstreamReader(iterator start, iterator end) - : mStart(start), mEnd(end), mCurrent(mStart), mBitPosition(value_size) - { - } - ~BitstreamReader() = default; - - /// Check reader's state - /// @return true if not in error state - bool good() const - { - return mBitPosition > 0; - } - - /// Indicates end of data - /// @return true if end of resource is reached - bool eof() const - { - return mCurrent == mEnd && mBitPosition > 0; - } - - /// Reset the reader, start over at beginning - void reset() - { - mCurrent = mStart; - mBitPosition = value_size; - } - - /// Get the next N bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// TODO: this also works nicely for bitsets, but then the bitlength has to be specified - /// as template parameter, want to do a specific overload, but needs more work to catch - /// all cases. - /// @param v target variable passed by reference - /// @return number of poked bits - template - size_t peek(T& v) - { - static_assert(N <= sizeof(T) * 8); - return peek(v, N); - } - - /// Get the next n bits without moving the read position - /// if bitlength is smaller than the size of data type, result is aligned to LSB - /// @param v target variable passed by reference - /// @param bitlength number of bits to read - /// @return number of poked bits - template - size_t peek(T& v, size_t bitlength) - { - return peek(v, bitlength); - } - - /// Move read position - /// @param bitlength move count in number of bits - void seek(size_t bitlength) - { - while (good() && bitlength > 0 && mCurrent != mEnd) { - if (bitlength >= mBitPosition) { - bitlength -= mBitPosition; - mBitPosition = 0; - } else { - mBitPosition -= bitlength; - bitlength = 0; - } - if (mBitPosition == 0) { - mCurrent++; - mBitPosition = value_size; - } - } - - if (bitlength > 0) { - mBitPosition = 0; - } - } - - /// Get the next n bits and move the read position - template - T get() - { - T result; - peek(result); - seek(N); - return result; - } - - /// Get the next n and move the read position - template - T get(size_t bitlength = sizeof(T) * 8) - { - T result; - peek(result, bitlength); - seek(bitlength); - return result; - } - - /// @class Bits - /// @brief Helper class to get value of specified type which holds the number used bits - /// - /// The class holds both the extracted value access via peek method and the number of used - /// bits. The reader will be incremented when the object is destroyed. - /// The number of bits can be adjusted by using markUsed method - template - class Bits - { - public: - using field_type = FieldType; - static_assert(N <= sizeof(FieldType) * 8); - Bits() - : mParent(nullptr), mData(0), mLength(0) - { - } - Bits(ParentType* parent, FieldType&& data) - : mParent(parent), mData(std::move(data)), mLength(N) - { - } - Bits(Bits&& other) - : mParent(other.mParent), mData(std::move(other.mData)), mLength(other.mLength) - { - other.mParent = nullptr; - other.mLength = 0; - } - - ~Bits() - { - if (mParent) { - mParent->seek(mLength); - } - } - - auto& operator=(Bits&& other) - { - mParent = other.mParent; - mData = std::move(other.mData); - mLength = other.mLength; - other.mParent = nullptr; - other.mLength = 0; - - return *this; - } - - FieldType& operator*() - { - return mData; - } - - void markUsed(size_t length) - { - mLength = length; - } - - private: - ParentType* mParent; - FieldType mData; - size_t mLength; - }; - - /// Read an integral value from the stream - template ::value, int> = 0> - self_type& operator>>(T& target) - { - target = get(); - return *this; - } - - /// Read a bitstream value from the stream - template - self_type& operator>>(std::bitset& target) - { - target = get, N>(); - return *this; - } - - /// Read a Bits object from the stream - template - self_type& operator>>(Bits& target) - { - T bitfield; - peek(bitfield); - target = std::move(Bits(this, std::move(bitfield))); - return *this; - } - - private: - /// The internal peek method - template - size_t peek(T& result, size_t bitlength) - { - if constexpr (RuntimeCheck) { - // the runtime check is disabled if bitlength is derived at compile time - if (bitlength > sizeof(T) * 8) { - throw std::length_error(std::string("requested bit length ") + std::to_string(bitlength) + " does not fit size of result data type " + std::to_string(sizeof(T) * 8)); - } - } - result = 0; - size_t bitsToWrite = bitlength; - auto current = mCurrent; - auto bitsAvailable = mBitPosition; - while (bitsToWrite > 0 && current != mEnd) { - // extract available bits - value_type mask = ~value_type(0) >> (value_size - bitsAvailable); - if (bitsToWrite >= bitsAvailable) { - T value = (*current & mask) << (bitsToWrite - bitsAvailable); - result |= value; - bitsToWrite -= bitsAvailable; - bitsAvailable = 0; - } else { - value_type value = (*current & mask) >> (bitsAvailable - bitsToWrite); - result |= value; - bitsAvailable -= bitsToWrite; - bitsToWrite = 0; - } - if (bitsAvailable == 0) { - current++; - bitsAvailable = value_size; - } - } - - return bitlength - bitsToWrite; - } - - /// start of resource - iterator mStart; - /// end of resource - iterator mEnd; - /// current position in resource - iterator mCurrent; - /// bit position in current element - size_t mBitPosition; -}; -} // namespace algorithm -} // namespace o2 -#endif diff --git a/Algorithm/include/Algorithm/PageParser.h b/Algorithm/include/Algorithm/PageParser.h index e382fc318352e..3ca01d87bcba3 100644 --- a/Algorithm/include/Algorithm/PageParser.h +++ b/Algorithm/include/Algorithm/PageParser.h @@ -255,12 +255,12 @@ class PageParser return mElement; } // comparison - bool operator==(const SelfType& rh) + bool operator==(const SelfType& rh) const { return mPosition == rh.mPosition; } // comparison - bool operator!=(const SelfType& rh) + bool operator!=(const SelfType& rh) const { return mPosition != rh.mPosition; } diff --git a/Algorithm/test/pageparser.cxx b/Algorithm/test/pageparser.cxx index 14b24c670cfd6..7551c32d9d864 100644 --- a/Algorithm/test/pageparser.cxx +++ b/Algorithm/test/pageparser.cxx @@ -50,7 +50,7 @@ struct ClusterData { { } - bool operator==(const ClusterData& rhs) + bool operator==(const ClusterData& rhs) const { return clusterid == rhs.clusterid && x == rhs.x && y == rhs.y && z == rhs.z && e == rhs.e; } diff --git a/Algorithm/test/test_BitstreamReader.cxx b/Algorithm/test/test_BitstreamReader.cxx deleted file mode 100644 index 41e3b47f5f276..0000000000000 --- a/Algorithm/test/test_BitstreamReader.cxx +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file test_BitstreamReader.cxx -/// @author Matthias Richter -/// @since 2019-06-05 -/// @brief Test program for BitstreamReader utility - -#define BOOST_TEST_MODULE Algorithm BitstreamReader unit test -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include -#include -#include -#include -#include -#include -#include "../include/Algorithm/BitstreamReader.h" - -namespace o2 -{ -namespace algorithm -{ - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_basic) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - uint8_t value; - reader.peek(value); - // we use 7 bits of the data - value >>= 1; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - //std::cout << "value " << (int)value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(value == *reference); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_operator) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - { - decltype(reader)::Bits value; - reader >> value; - // we use 7 bits of the data - *value >>= 1; - value.markUsed(7); - //std::cout << "value " << (int)*value << " expected " << (int)*reference << std::endl; - BOOST_CHECK(*value == *reference); - } - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - ++reference; - } -} - -BOOST_AUTO_TEST_CASE(test_BitstreamReader_bitset) -{ - std::array data = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f'}; - std::array expected7bit = {0x32, 0x19, 0x2c, 0x16, 0x23, 0x09, 0x4a, 0x65, 0x33, 0x0}; - auto reference = expected7bit.begin(); - constexpr size_t totalBits = data.size() * sizeof(decltype(data)::value_type) * 8; - size_t bitsRead = 0; - - BitstreamReader reader(data.data(), data.data() + data.size()); - while (bitsRead < totalBits) { - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK(reader.eof() == false); - std::bitset<13> value; - reader.peek(value, value.size()); - // we use 7 bits of the data - value >>= value.size() - 7; - reader.seek(7); - bitsRead += 7; - // in the last call should there is not enough data - BOOST_CHECK(reader.good() == (bitsRead <= totalBits)); - BOOST_REQUIRE(reference != expected7bit.end()); - BOOST_CHECK_MESSAGE(value.to_ulong() == *reference, std::string("mismatch: value ") << value.to_ulong() << ", expected " << (int)*reference); - ++reference; - } - - reader.reset(); - std::bitset<16> aBitset; - reader >> aBitset; - BOOST_CHECK_MESSAGE(aBitset.to_ulong() == 0x6465, std::string("mismatch: value 0x") << std::hex << aBitset.to_ulong() << ", expected 0x6465"); -} - -} // namespace algorithm -} // namespace o2 diff --git a/CCDB/CMakeLists.txt b/CCDB/CMakeLists.txt index 27ebc39e57c41..691c3311e117c 100644 --- a/CCDB/CMakeLists.txt +++ b/CCDB/CMakeLists.txt @@ -21,6 +21,7 @@ o2_add_library(CCDB FairMQ::FairMQ libjalien::libjalienO2 LibUV::LibUV + O2::Framework TARGETVARNAME targetName) o2_target_root_dictionary(CCDB @@ -44,6 +45,11 @@ o2_add_executable(upload SOURCES src/UploadTool.cxx PUBLIC_LINK_LIBRARIES O2::CCDB) +o2_add_executable(cleansemaphores + COMPONENT_NAME ccdb + SOURCES src/CleanCCDBSemaphores.cxx + PUBLIC_LINK_LIBRARIES O2::CCDB) + o2_add_executable(downloadccdbfile COMPONENT_NAME ccdb SOURCES src/DownloadCCDBFile.cxx @@ -86,6 +92,12 @@ o2_add_test(CcdbDownloader PUBLIC_LINK_LIBRARIES O2::CCDB LABELS ccdb) +o2_add_test(CcdbApi-Headers + SOURCES test/testCcdbApiHeaders.cxx + COMPONENT_NAME ccdb + PUBLIC_LINK_LIBRARIES O2::CCDB + LABELS ccdb) + # extra CcdbApi test which dispatches to CCDBDownloader (tmp until full move done) #o2_add_test_command(NAME CcdbApi-MultiHandle # WORKING_DIRECTORY ${SIMTESTDIR} diff --git a/CCDB/README.md b/CCDB/README.md index ce8d9e19f7b27..1ae5f29dcf0e2 100644 --- a/CCDB/README.md +++ b/CCDB/README.md @@ -13,7 +13,7 @@ in circumstances of reduced or no network connectivity. There are currently 2 different kinds of store/retrieve functions, which we expect to unify in the immediate future: 2. `storeAsTFile/retrieveFromTFile` API serializing a `TObject` in a ROOT `TFile`. -3. A strongly-typed `storeAsTFileAny/retrieveFromTFileAny` API allowing to handle any type T +3. A strongly-typed `storeAsTFileAny/retrieveFromTFileAny` API allowing to handle any type T having a ROOT dictionary. We encourage to use this API by default. ## Central and local instances of the CCDB @@ -31,18 +31,18 @@ If you access the CCDB with a web browser, add `/browse` at the end of the URL t ```c++ // init CcdbApi api; -map metadata; // can be empty +std::map metadata; // can be empty api.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner auto deadpixels = new o2::FOO::DeadPixelMap(); api.storeAsTFileAny(deadpixels, "FOO/DeadPixels", metadata); // read like this (you have to specify the type) -auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata); -// read like this to get the headers as well, and thus the metadata attached to the object -map headers; -auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata /* constraint the objects retrieved to those matching the metadata */, -1 /* timestamp */, &headers /* the headers attached to the returned object */); +auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata); +// read like this to get the headers as well, and thus the metadata attached to the object +std::map headers; +auto deadpixelsback = api.retrieveFromTFileAny("FOO/DeadPixels", metadata /* constraint the objects retrieved to those matching the metadata */, -1 /* timestamp */, &headers /* the headers attached to the returned object */); // finally, use this method to retrieve only the headers (and thus the metadata) -std::map headers = f.api.retrieveHeaders("FOO/DeadPixels", f.metadata); +std::map headers = api.retrieveHeaders("FOO/DeadPixels", metadata); ``` * creating a local snapshot and fetching objects therefrom @@ -50,7 +50,7 @@ std::map headers = f.api.retrieveHeaders("FOO/DeadPixe ```c++ // init CcdbApi api; -map metadata; // can be empty +std::map metadata; // can be empty api.init("http://ccdb-test.cern.ch:8080"); // or http://localhost:8080 for a local installation // create a local snapshot of everthing in or below the FOO folder valid for timestamp 12345 api.snapshot("FOO", "/tmp/CCDBSnapshot/", 12345); @@ -85,7 +85,7 @@ user code. This class The class was written for the use-case of transport MC simulation. Typical usage should be like ```c++ -// setup manager once (at start of processing) +// setup manager once (at start of processing) auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL("http://ourccdbserverver.cern.ch"); mgr.setTimestamp(timestamp_which_we_want_to_anchor_to); @@ -111,6 +111,12 @@ This feature is useful to avoid using newer objects if the CCDB is updated in pa In cached mode, the manager can check that local objects are still valid by requiring `mgr.setLocalObjectValidityChecking(true)`, in this case a CCDB query is performed only if the cached object is no longer valid. +If you want the headers/metadata for the object retrieved from the CCDB there is an optional paramater to `BasicCCDBManager::getForTimeStamp`. These headers are also cached (when caching is enabled) and is updated when a CCDB query is sent. +```c++ +std::map headers; +mgr.getForTimeStamp(path, timstamp, metadata, &headers); +``` + ## Future ideas / todo: - [ ] offer improved error handling / exceptions @@ -129,26 +135,26 @@ A few prototypic command line tools are offered. These can be used in scriptable and facilitate the following tasks: 1. Upload and annotate a generic C++ object serialized in a ROOT file - + ```bash o2-ccdb-upload -f myRootFile.root --key histogram1 --path /Detector1/QA/ --meta "Description=Foo;Author=Person1;Uploader=Person2" ``` This will upload the object serialized in `myRootFile.root` under the key `histogram1`. Object will be put to the CCDB path `/Detector1/QA`. For full list of options see `o2-ccdb-upload --help`. - + 2. Download a CCDB object to a local ROOT file (including its meta information) - + ```bash o2-ccdb-downloadccdbfile --path /Detector1/QA/ --dest /tmp/CCDB --timestamp xxx ``` This will download the CCDB object under path given by `--path` to a directory given by `--dest` on the disc. (The final filename will be `/tmp/CCDB/Detector1/QA/snapshot.root` for the moment). All meta-information as well as the information associated to this query will be appended to the file. - + For full list of options see `o2-ccdb-downloadccdbfile --help`. - + 3. Inspect the content of a ROOT file and print summary about type of contained (CCDB) objects and its meta information - + ```bash o2-ccdb-inspectccdbfile filename ``` diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index af1c0831cfcba..fd0fe7aa6d05b 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -18,9 +18,14 @@ #include "CCDB/CcdbApi.h" #include "CCDB/CCDBTimeStampUtils.h" #include "CommonUtils/NameConf.h" +#include "Framework/DataTakingContext.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include +#include #include #include #include @@ -48,10 +53,20 @@ class CCDBManagerInstance std::string uuid; long startvalidity = 0; long endvalidity = -1; + long cacheValidFrom = 0; // time for which the object was cached + long cacheValidUntil = -1; // object is guaranteed to be valid till this time (modulo new updates) + size_t minSize = -1ULL; + size_t maxSize = 0; int queries = 0; int fetches = 0; int failures = 0; - bool isValid(long ts) { return ts < endvalidity && ts > startvalidity; } + std::map cacheOfHeaders; + bool isValid(long ts) { return ts < endvalidity && ts >= startvalidity; } + bool isCacheValid(long ts) + { + LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, ts < cacheValidUntil && ts >= cacheValidFrom); + return ts < cacheValidUntil && ts >= cacheValidFrom; + } void clear() { noCleanupPtr = nullptr; @@ -59,6 +74,7 @@ class CCDBManagerInstance uuid = ""; startvalidity = 0; endvalidity = -1; + cacheOfHeaders.clear(); } }; @@ -68,6 +84,7 @@ class CCDBManagerInstance CCDBManagerInstance(std::string const& path) : mCCDBAccessor{} { mCCDBAccessor.init(path); + mDeplMode = o2::framework::DefaultsHelpers::deploymentMode(); } /// set a URL to query from void setURL(const std::string& url); @@ -86,19 +103,31 @@ class CCDBManagerInstance /// query timestamp long getTimestamp() const { return mTimestamp; } - /// retrieve an object of type T from CCDB as stored under path and timestamp + /// retrieve an object of type T from CCDB as stored under path and timestamp. Optional to get the headers. template - T* getForTimeStamp(std::string const& path, long timestamp); + T* getForTimeStamp(std::string const& path, long timestamp, std::map* headers = nullptr); + + /// retrieve an object of type T from CCDB as stored under path and using the timestamp in the middle of the run + template + T* getForRun(std::string const& path, int runNumber, bool setRunMetadata = false); /// retrieve an object of type T from CCDB as stored under path, timestamp and metaData template - T* getSpecific(std::string const& path, long timestamp = -1, MD metaData = MD()) + T* getSpecific(std::string const& path, long timestamp = -1, MD metaData = MD(), std::map* headers = nullptr) { // TODO: add some error info/handling when failing mMetaData = metaData; - return getForTimeStamp(path, timestamp); + auto obj = getForTimeStamp(path, timestamp, headers); + return obj; } + /// retrieve an object of type T from CCDB as stored under path and using the timestamp in the middle of the run + metadata. The run number is provided separately to conform to typical analysis use (in which case metadata does not include runNumber) + template + T* getSpecificForRun(std::string const& path, int runNumber, MD const& metaData = MD()); + + /// detect online processing modes (i.e. CCDB objects may be updated in the lifetime of the manager) + bool isOnline() const { return mDeplMode == o2::framework::DeploymentMode::OnlineAUX || mDeplMode == o2::framework::DeploymentMode::OnlineDDS || mDeplMode == o2::framework::DeploymentMode::OnlineECS; } + /// retrieve an object of type T from CCDB as stored under path; will use the timestamp member template T* get(std::string const& path) @@ -106,6 +135,9 @@ class CCDBManagerInstance return getForTimeStamp(path, mTimestamp); } + // gain access to underlaying CCDB layer (to allow for more complex queries without need to reinit another API) + CcdbApi& getCCDBAccessor() { return mCCDBAccessor; } + bool isHostReachable() const { return mCCDBAccessor.isHostReachable(); } /// clear all entries in the cache @@ -132,7 +164,7 @@ class CCDBManagerInstance if (!isCachingEnabled()) { return false; } - return mCache[path].isValid(timestamp); + return (mCheckObjValidityEnabled && mCache[path].isValid(timestamp)) || mCache[path].isCacheValid(timestamp); // use stricter check } /// check if checks of object validity before CCDB query is enabled @@ -165,17 +197,19 @@ class CCDBManagerInstance void setFatalWhenNull(bool b) { mFatalWhenNull = b; } /// A convenience function for MC to fetch - /// valid start and end timestamps given an ALICE run number. - /// On error it fatals (if fatal == true) or else returns the pair -1, -1. - std::pair getRunDuration(int runnumber, bool fatal = true) const; - - /// A convenience function for MC to fetch - /// valid start and end timestamps given an ALICE run number. + /// valid start and end timestamps for recorded TF data given an ALICE run number. + /// In absence of STF/ETF fields in the RCT with fall back to CTP SOX/EOX then to + /// ECS SOR/EOR. /// On error it fatals (if fatal == true) or else returns the pair -1, -1. + std::pair getRunDuration(int runnumber, bool fatal = true); static std::pair getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal = true); - + static std::pair getRunDuration(const MD& headers); std::string getSummaryString() const; + size_t getFetchedSize() const { return mFetchedSize; } + + void report(bool longrep = false); + void endOfStream(); private: @@ -190,25 +224,27 @@ class CCDBManagerInstance bool mCanDefault = false; // whether default is ok --> useful for testing purposes done standalone/isolation bool mCachingEnabled = true; // whether caching is enabled bool mCheckObjValidityEnabled = false; // wether the validity of cached object is checked before proceeding to a CCDB API query + bool mFatalWhenNull = true; // if nullptr blob replies should be treated as fatal (can be set by user) long mCreatedNotAfter = 0; // upper limit for object creation timestamp (TimeMachine mode) - If-Not-After HTTP header long mCreatedNotBefore = 0; // lower limit for object creation timestamp (TimeMachine mode) - If-Not-Before HTTP header long mTimerMS = 0; // timer for queries - bool mFatalWhenNull = true; // if nullptr blob replies should be treated as fatal (can be set by user) + size_t mFetchedSize = 0; // total fetched size int mQueries = 0; // total number of object queries int mFetches = 0; // total number of succesful fetches from CCDB int mFailures = 0; // total number of failed fetches - + o2::framework::DeploymentMode mDeplMode; // O2 deployment mode ClassDefNV(CCDBManagerInstance, 1); }; template -T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) +T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp, std::map* headers) { + mHeaders.clear(); // we clear at the beginning; to allow to retrieve the header information in a subsequent call T* ptr = nullptr; mQueries++; auto start = std::chrono::system_clock::now(); if (!isCachingEnabled()) { - ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, nullptr, "", + ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, &mHeaders, "", mCreatedNotAfter ? std::to_string(mCreatedNotAfter) : "", mCreatedNotBefore ? std::to_string(mCreatedNotBefore) : ""); if (!ptr) { @@ -218,16 +254,38 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) mFailures++; } else { mFetches++; + auto sh = mHeaders.find("fileSize"); + if (sh != mHeaders.end()) { + size_t s = atol(sh->second.c_str()); + mFetchedSize += s; + } + } + + if (headers) { + *headers = mHeaders; } } else { auto& cached = mCache[path]; - if (mCheckObjValidityEnabled && cached.isValid(timestamp)) { - cached.queries++; + cached.queries++; + if ((!isOnline() && cached.isCacheValid(timestamp)) || (mCheckObjValidityEnabled && cached.isValid(timestamp))) { + // Give back the cached/saved headers + if (headers) { + *headers = cached.cacheOfHeaders; + } return reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); } ptr = mCCDBAccessor.retrieveFromTFileAny(path, mMetaData, timestamp, &mHeaders, cached.uuid, mCreatedNotAfter ? std::to_string(mCreatedNotAfter) : "", mCreatedNotBefore ? std::to_string(mCreatedNotBefore) : ""); + // update the cached headers + for (auto const& h : mHeaders) { + cached.cacheOfHeaders[h.first] = h.second; + } + // return the cached headers + if (headers) { + *headers = cached.cacheOfHeaders; + } + if (ptr) { // new object was shipped, old one (if any) is not valid anymore cached.fetches++; mFetches++; @@ -238,19 +296,42 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) cached.objPtr.reset(ptr); } cached.uuid = mHeaders["ETag"]; - try { // this conversion can throw, better to catch immediately - cached.startvalidity = std::stol(mHeaders["Valid-From"]); - cached.endvalidity = std::stol(mHeaders["Valid-Until"]); + + try { + if (mHeaders.find("Valid-From") != mHeaders.end()) { + cached.startvalidity = std::stol(mHeaders["Valid-From"]); + } else { + // if meta-information missing assume infinit validity + // (should happen only for locally created objects) + cached.startvalidity = 0; + } + if (mHeaders.find("Valid-Until") != mHeaders.end()) { + cached.endvalidity = std::stol(mHeaders["Valid-Until"]); + } else { + cached.endvalidity = std::numeric_limits::max(); + } + cached.cacheValidFrom = timestamp; } catch (std::exception const& e) { reportFatal("Failed to read validity from CCDB response (Valid-From : " + mHeaders["Valid-From"] + std::string(" Valid-Until: ") + mHeaders["Valid-Until"] + std::string(")")); } + auto sh = mHeaders.find("fileSize"); + if (sh != mHeaders.end()) { + size_t s = atol(sh->second.c_str()); + mFetchedSize += s; + cached.minSize = std::min(s, cached.minSize); + cached.maxSize = std::max(s, cached.minSize); + } } else if (mHeaders.count("Error")) { // in case of errors the pointer is 0 and headers["Error"] should be set cached.failures++; cached.clear(); // in case of any error clear cache for this object - } else { // the old object is valid - ptr = reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); } - mHeaders.clear(); + // the old object is valid, fetch cache end of validity + ptr = reinterpret_cast(cached.noCleanupPtr ? cached.noCleanupPtr : cached.objPtr.get()); + if (mHeaders.find("Cache-Valid-Until") != mHeaders.end()) { + cached.cacheValidUntil = std::stol(mHeaders["Cache-Valid-Until"]); + } else { + cached.cacheValidUntil = -1; + } mMetaData.clear(); if (!ptr) { if (mFatalWhenNull) { @@ -261,9 +342,37 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) } auto end = std::chrono::system_clock::now(); mTimerMS += std::chrono::duration_cast(end - start).count(); + auto *ref = o2::framework::ServiceRegistryRef::globalDeviceRef(); + if (ref && ref->active()) { + auto& stats = ref->get(); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_HIT, o2::framework::DataProcessingStats::Op::Set, (int64_t)mQueries - mFailures - mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_MISS, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_FAILURE, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFailures}); + } return ptr; } +template +T* CCDBManagerInstance::getForRun(std::string const& path, int runNumber, bool setRunMetadata) +{ + auto metaData = setRunMetadata ? MD{{"runNumber", std::to_string(runNumber)}} : MD{}; + mMetaData = metaData; + return getSpecificForRun(path, runNumber, metaData); +} + +template +T* CCDBManagerInstance::getSpecificForRun(std::string const& path, int runNumber, MD const& metaData) +{ + auto [start, stop] = getRunDuration(runNumber, mFatalWhenNull); + if (start < 0 || stop < 0) { + if (mFatalWhenNull) { + reportFatal(std::string("Failed to get run duration for run ") + std::to_string(runNumber) + std::string(" from CCDB")); + } + return nullptr; + } + return getSpecific(path, start / 2 + stop / 2, metaData); +} + class BasicCCDBManager : public CCDBManagerInstance { public: @@ -291,4 +400,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H diff --git a/CCDB/include/CCDB/CCDBDownloader.h b/CCDB/include/CCDB/CCDBDownloader.h index a8abb46dcff19..6c057a537a096 100644 --- a/CCDB/include/CCDB/CCDBDownloader.h +++ b/CCDB/include/CCDB/CCDBDownloader.h @@ -11,6 +11,10 @@ #ifndef O2_CCDBDOWNLOADER_H_ #define O2_CCDBDOWNLOADER_H_ +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#include "MemoryResources/MemoryResources.h" +#endif + #include #include #include @@ -21,6 +25,8 @@ #include #include #include +#include +#include typedef struct uv_loop_s uv_loop_t; typedef struct uv_timer_s uv_timer_t; @@ -29,11 +35,30 @@ typedef struct uv_signal_s uv_signal_t; typedef struct uv_async_s uv_async_t; typedef struct uv_handle_s uv_handle_t; -using namespace std; - namespace o2::ccdb { +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +struct HeaderObjectPair_t { + std::multimap header; + o2::pmr::vector* object = nullptr; + int counter = 0; +}; + +typedef struct DownloaderRequestData { + std::vector hosts; + std::vector locations; + std::string path; + long timestamp; + HeaderObjectPair_t hoPair; + std::map* headers; + std::string userAgent; + curl_slist* optionsList; + + std::function localContentCallback; +} DownloaderRequestData; +#endif + /* Some functions below aren't member functions of CCDBDownloader because both curl and libuv require callback functions which have to be either static or non-member. Because non-static functions are used in the functions below, they must be non-member. @@ -63,6 +88,11 @@ curl_socket_t opensocketCallback(void* clientp, curlsocktype purpose, struct cur */ void onUVClose(uv_handle_t* handle); +enum DownloaderErrorLevel { + MINOR, + SEVERE +}; + /// A class encapsulating and performing simple CURL requests in terms of a so-called CURL multi-handle. /// A multi-handle allows to use a connection pool (connection cache) in the CURL layer even /// with short-lived CURL easy-handles. Thereby the overhead of connection to servers can be @@ -75,29 +105,50 @@ class CCDBDownloader public: /** * Timer starts for each socket when its respective transfer finishes, and is stopped when another transfer starts for that handle. - * When the timer runs out it closes the socket. The period for which socket stays open is defined by socketTimoutMS. + * When the timer runs out it closes the socket. The period for which socket stays open is defined by socketTimeoutMS. */ std::unordered_map mSocketTimerMap; /** - * The UV loop which handles transfers. + * The UV loop which handles transfers. Can be created internally or provided through a constructor. */ uv_loop_t* mUVLoop; + /** + * Map used to store active uv_handles belonging to the CcdbDownloader. If internal uv_loop is used, then all uv_handles should be marked in this map. + */ std::unordered_map mHandleMap; - // ADD COMMENT /** * Time for which sockets will stay open after last download finishes */ - int mSocketTimeoutMS = 4000; + int mKeepaliveTimeoutMS = 100; + + /** + * Time for connection to start before it times out. + */ + int mConnectionTimeoutMS = 60000; + + /** + * Time for request to finish before it times out. + */ + int mRequestTimeoutMS = 300000; + + /** + * Head start of IPv6 in regards to IPv4. + */ + int mHappyEyeballsHeadstartMS = 500; /** * Max number of handles that can be used at the same time */ int mMaxHandlesInUse = 3; - // CCDBDownloader(uv_loop_t uv_loop); + /** + * Variable denoting whether an external or internal uv_loop is being used. + */ + bool mExternalLoop; + CCDBDownloader(uv_loop_t* uv_loop = nullptr); ~CCDBDownloader(); @@ -108,13 +159,6 @@ class CCDBDownloader */ CURLcode perform(CURL* handle); - /** - * Perform on a batch of handles. Callback will be exectuted in it's own thread after all handles finish their transfers. - * - * @param handles Handles to be performed on. - */ - std::vector asynchBatchPerformWithCallback(std::vector const& handles, bool* completionFlag, void (*cbFun)(void*), void* cbData); - /** * Perform on a batch of handles in a blocking manner. Has the same effect as calling curl_easy_perform() on all handles in the vector. * @param handleVector Handles to be performed on. @@ -122,11 +166,12 @@ class CCDBDownloader std::vector batchBlockingPerform(std::vector const& handleVector); /** - * Perform on a batch of handles. Completion flag will be set to true when all handles finish their transfers. - * @param handleVector Handles to be performed on. - * @param completionFlag Should be set to false before passing it to this function. Will be set to true after all transfers finish. + * Schedules an asynchronous transfer but doesn't perform it. + * + * @param handle Handle to be performed on. + * @param requestCounter Counter shared by a batch of CURL handles. */ - std::vector batchAsynchPerform(std::vector const& handleVector, bool* completionFlag); + void asynchSchedule(CURL* handle, size_t* requestCounter); /** * Limits the number of parallel connections. Should be used only if no transfers are happening. @@ -136,62 +181,97 @@ class CCDBDownloader /** * Limits the time a socket and its connection will be opened after transfer finishes. */ - void setSocketTimoutTime(int timoutMS); + void setKeepaliveTimeoutTime(int timeoutMS); - private: /** - * Indicates whether the loop that the downloader is running on has been created by it or provided externally. - * In case of external loop, the loop will not be closed after downloader is deleted. + * Setter for the connection timeout. */ - bool mIsExternalLoop; + void setConnectionTimeoutTime(int timeoutMS); /** - * Current amount of handles which are performed on. + * Setter for the request timeout. */ - int mHandlesInUse = 0; + void setRequestTimeoutTime(int timeoutMS); /** - * Multi handle which controlls all network flow. + * Setter for the happy eyeballs headstart. */ - CURLM* mCurlMultiHandle = nullptr; + void setHappyEyeballsHeadstartTime(int headstartMS); /** - * The timeout clock that is be used by CURL. + * Sets the timeout values selected for the offline environment. */ - uv_timer_t* mTimeoutTimer; + void setOfflineTimeoutSettings(); /** - * Queue of handles awaiting their transfers to start. + * Sets the timeout values selected for the online environment. */ - std::vector mHandlesToBeAdded; + void setOnlineTimeoutSettings(); /** - * Lock protecting the mHandlesToBeAdded container + * Run the uvLoop once. + * + * @param noWait Using this flag will cause the loop to run only if sockets have pendind data. */ - std::mutex mHandlesQueueLock; + void runLoop(bool noWait); /** - * Thread on which the thread with uv_loop runs. + * Returns a message describing the transfer an it's result. */ - std::thread* mLoopThread; + std::string prepareLogMessage(std::string host_url, std::string userAgent, const std::string& path, long ts, const std::map* headers, long httpCode) const; /** - * Vector with reference to callback threads with a flag marking whether they finished running. + * Leaves only the protocol and host part of the url, discrading path and metadata. */ - std::vector> mThreadFlagPairVector; + std::string trimHostUrl(std::string full_host_url) const; + private: /** - * Flag used to signall the loop to close. + * Recognizes whether the address is a full url, or a partial one (like for example "/Task/Detector/1") and combines it with potentialHost if needed. */ - bool mCloseLoop = false; + std::string prepareRedirectedURL(std::string address, std::string potentialHost) const; + + /** + * Updates the locations vector with the the locations. + * + * @param headerMap Map containing response headers. + * @param locations Location list to be updated. + * @param locIndex Index of the next locaiton to be tried. + */ + void updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const; + + std::string mUserAgentId = "CCDBDownloader"; + /** + * Sets up internal UV loop. + */ + void setupInternalUVLoop(); + + /** + * Current amount of handles which are performed on. + */ + int mHandlesInUse = 0; + + /** + * Multi handle which controlls all network flow. + */ + CURLM* mCurlMultiHandle = nullptr; + + /** + * The timeout clock that is be used by CURL. + */ + uv_timer_t* mTimeoutTimer; + + /** + * Queue of handles awaiting their transfers to start. + */ + std::vector mHandlesToBeAdded; /** * Types of requests. */ enum RequestType { BLOCKING, - ASYNCHRONOUS, - ASYNCHRONOUS_WITH_CALLBACK + ASYNCHRONOUS }; /** @@ -213,29 +293,46 @@ class CCDBDownloader DataForSocket mSocketData; +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) /** * Structure which is stored in a easy_handle. It carries information about the request which the easy_handle is part of. - * All easy handles coming from one request have an identical PerformData structure. */ typedef struct PerformData { - std::condition_variable* cv; - bool* completionFlag; CURLcode* codeDestination; - void (*cbFun)(void*); - std::thread* cbThread; - void* cbData; size_t* requestsLeft; RequestType type; + int hostInd; + int locInd; + DownloaderRequestData* requestData; + curl_slist** options; } PerformData; +#endif /** - * Called by CURL in order to close a socket. It will be called by CURL even if a timout timer closed the socket beforehand. + * Called by CURL in order to close a socket. It will be called by CURL even if a timeout timer closed the socket beforehand. * * @param clientp Pointer to the CCDBDownloader instance which controls the socket. * @param item File descriptor of the socket. */ static void closesocketCallback(void* clientp, curl_socket_t item); +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) + // Returns a new location string or an empty string if all locations under current host have been accessedd + std::string getNewLocation(PerformData* performData, std::vector& locations) const; + + // Reschedules the transfer to be performed with a different host. + void tryNewHost(PerformData* performData, CURL* easy_handle); + + // Retrieves content from either alien, cvmfs or local storage using a callback to CCDBApi. + void getLocalContent(PerformData* performData, std::string& newLocation, bool& contentRetrieved, std::vector& locations); + + // Continues a transfer via a http redirect. + void httpRedirect(PerformData* performData, std::string& newLocation, CURL* easy_handle); + + // Continues a transfer via a redirect. The redirect can point to a local file, alien file or a http address. + void followRedirect(PerformData* performData, CURL* easy_handle, std::vector& locations, bool& rescheduled, bool& contentRetrieved); +#endif + /** * Is used to react to polling file descriptors in poll_handle. * @@ -245,25 +342,11 @@ class CCDBDownloader */ static void curlPerform(uv_poll_t* handle, int status, int events); - /** - * Check if loop was signalled to close. The handle connected with this callbacks is always active as to prevent the uv_loop from stopping. - * - * @param handle uv_handle to which this callbacks is assigned - */ - static void checkStopSignal(uv_timer_t* handle); - /** * Used by CURL to react to action happening on a socket. */ static int handleSocket(CURL* easy, curl_socket_t s, int action, void* userp, void* socketp); - /** - * Asynchronously notify the loop to check its CURL handle queue. - * - * @param handle Handle which is assigned to this callback. - */ - static void asyncUVHandleCheckQueue(uv_async_t* handle); - /** * Close socket assigned to the timer handle. * @@ -295,17 +378,12 @@ class CCDBDownloader /** * Connect curl timer with uv timer. * - * @param multi Multi handle for which the timout will be set + * @param multi Multi handle for which the timeout will be set * @param timeout_ms Time until timeout * @param userp Pointer to the uv_timer_t handle that is used for timeout. */ static int startTimeout(CURLM* multi, long timeout_ms, void* userp); - /** - * Check if any of the callback threads have finished running and approprietly join them. - */ - void checkForThreadsToJoin(); - /** * Create a new multi_handle for the downloader */ @@ -324,10 +402,12 @@ class CCDBDownloader */ void checkMultiInfo(); +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) /** * Set openSocketCallback and closeSocketCallback with appropriate arguments. Stores data inside the CURL handle. */ void setHandleOptions(CURL* handle, PerformData* data); +#endif /** * Create structure holding information about a socket including a poll handle assigned to it @@ -336,20 +416,10 @@ class CCDBDownloader */ curl_context_t* createCurlContext(curl_socket_t sockfd); - /** - * Asynchroniously signal the event loop to check for new easy_handles to add to multi handle. - */ - void makeLoopCheckQueueAsync(); - /** * If multi_handles uses less then maximum number of handles then add handles from the queue. */ void checkHandleQueue(); - - /** - * Start the event loop. This function should be ran in the `loopThread`. - */ - void runLoop(); }; /** @@ -360,6 +430,6 @@ typedef struct DataForClosingSocket { curl_socket_t socket; } DataForClosingSocket; -} // namespace o2 +} // namespace o2::ccdb #endif // O2_CCDB_CCDBDOWNLOADER_H diff --git a/CCDB/include/CCDB/CcdbApi.h b/CCDB/include/CCDB/CcdbApi.h index b2bd50326eee6..4dab11d5972d8 100644 --- a/CCDB/include/CCDB/CcdbApi.h +++ b/CCDB/include/CCDB/CcdbApi.h @@ -24,18 +24,20 @@ #include #include #include "CCDB/CcdbObjectInfo.h" -#include "CCDB/CCDBDownloader.h" #include #include #include #if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) #include "MemoryResources/MemoryResources.h" +#include #include #else class TJAlienCredentials; #endif +#include "CCDB/CCDBDownloader.h" + class TFile; class TGrid; @@ -62,6 +64,10 @@ class CcdbApi //: public DatabaseInterface /// \brief Default destructor virtual ~CcdbApi(); + // Delete copy and copy assignment constructor + CcdbApi(const CcdbApi&) = delete; + void operator=(const CcdbApi&) = delete; + const std::string getUniqueAgentID() const { return mUniqueAgentID; } static bool checkAlienToken(); @@ -119,34 +125,34 @@ class CcdbApi //: public DatabaseInterface static std::unique_ptr> createObjectImage(const void* obj, std::type_info const& tinfo, CcdbObjectInfo* info = nullptr); /** - * Store into the CCDB a TFile containing the ROOT object. - * - * @param rootObject Raw pointer to the object to store. - * @param path The path where the object is going to be stored. - * @param metadata Key-values representing the metadata for this object. - * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. - * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. - * @return 0 -> ok, - * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), - * -1 : object bigger than maxSize, - * -2 : curl initialization error - */ + * Store into the CCDB a TFile containing the ROOT object. + * + * @param rootObject Raw pointer to the object to store. + * @param path The path where the object is going to be stored. + * @param metadata Key-values representing the metadata for this object. + * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. + * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. + * @return 0 -> ok, + * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), + * -1 : object bigger than maxSize, + * -2 : curl initialization error + */ int storeAsTFile(const TObject* rootObject, std::string const& path, std::map const& metadata, long startValidityTimestamp = -1, long endValidityTimestamp = -1, std::vector::size_type maxSize = 0 /*bytes*/) const; /** - * Store into the CCDB a TFile containing an object of type T (which needs to have a ROOT dictionary) - * - * @param obj Raw pointer to the object to store. - * @param path The path where the object is going to be stored. - * @param metadata Key-values representing the metadata for this object. - * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. - * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. - * @return 0 -> ok, - * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), - * -1 : object bigger than maxSize, - * -2 : curl initialization error - */ + * Store into the CCDB a TFile containing an object of type T (which needs to have a ROOT dictionary) + * + * @param obj Raw pointer to the object to store. + * @param path The path where the object is going to be stored. + * @param metadata Key-values representing the metadata for this object. + * @param startValidityTimestamp Start of validity. If omitted, current timestamp is used. + * @param endValidityTimestamp End of validity. If omitted, current timestamp + 1 day is used. + * @return 0 -> ok, + * positive number -> curl error (https://curl.se/libcurl/c/libcurl-errors.html), + * -1 : object bigger than maxSize, + * -2 : curl initialization error + */ template int storeAsTFileAny(const T* obj, std::string const& path, std::map const& metadata, long startValidityTimestamp = -1, long endValidityTimestamp = -1, std::vector::size_type maxSize = 0 /*bytes*/) const @@ -208,8 +214,9 @@ class CcdbApi //: public DatabaseInterface * @param metadata The metadata to update * @param timestamp The timestamp to select the object * @param id The id, if any, to select the object + * @return anithing non-0 is CURL error code or -1 */ - void updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id = "", long newEOV = 0); + int updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id = "", long newEOV = 0); /** * Return the listing of objects, and in some cases subfolders, matching this path. @@ -232,7 +239,7 @@ class CcdbApi //: public DatabaseInterface * @param returnFormat The format of the returned string -> one of "text/plain (default)", "application/json", "text/xml" * @return The listing of folder and/or objects in the format requested */ - std::string list(std::string const& path = "", bool latestOnly = false, std::string const& returnFormat = "text/plain") const; + std::string list(std::string const& path = "", bool latestOnly = false, std::string const& returnFormat = "text/plain", long createdNotAfter = -1, long createdNotBefore = -1) const; /** * Make a local snapshot of all valid objects, given a timestamp, of the CCDB under a given local path. @@ -248,12 +255,12 @@ class CcdbApi //: public DatabaseInterface bool isHostReachable() const; /** - * Helper function to extract the list of sub-folders from a list reply into a vector container. - * Can be used to achieve full recursive traversal/listing of the CCDB. - * - * @param reply The reply that we got from a GET/browse sort of request. - * @return The vector of sub-folders. - */ + * Helper function to extract the list of sub-folders from a list reply into a vector container. + * Can be used to achieve full recursive traversal/listing of the CCDB. + * + * @param reply The reply that we got from a GET/browse sort of request. + * @return The vector of sub-folders. + */ std::vector parseSubFolders(std::string const& reply) const; /** @@ -274,7 +281,7 @@ class CcdbApi //: public DatabaseInterface * @return: True in case operation successful or false if there was a failure/problem. */ bool retrieveBlob(std::string const& path, std::string const& targetdir, std::map const& metadata, long timestamp, - bool preservePathStructure = true, std::string const& localFileName = "snapshot.root", std::string const& createdNotAfter = "", std::string const& createdNotBefore = "") const; + bool preservePathStructure = true, std::string const& localFileName = "snapshot.root", std::string const& createdNotAfter = "", std::string const& createdNotBefore = "", std::map* headers = nullptr) const; /** * Retrieve the headers of a CCDB entry, if it exists. @@ -341,14 +348,57 @@ class CcdbApi //: public DatabaseInterface TObject* retrieveFromTFile(std::string const& path, std::map const& metadata, long timestamp, std::map* headers, std::string const& etag, const std::string& createdNotAfter, const std::string& createdNotBefore) const; + void loadFileToMemory(std::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot = true) const; #if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) - void loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders = nullptr) const; + typedef struct RequestContext { + o2::pmr::vector& dest; + std::string path; + std::map const& metadata; + long timestamp; + std::map& headers; + std::string etag; + std::string createdNotAfter; + std::string createdNotBefore; + bool considerSnapshot; + + RequestContext(o2::pmr::vector& d, + std::map const& m, + std::map& h) + : dest(d), metadata(m), headers(h) {} + } RequestContext; + + // Stores file associated with requestContext as a snapshot. + void saveSnapshot(RequestContext& requestContext) const; + + // Schedules download via CCDBDownloader, but doesn't perform it until mUVLoop is ran. + void scheduleDownload(RequestContext& requestContext, size_t* requestCounter) const; + + void getFromSnapshot(bool createSnapshot, std::string const& path, + long timestamp, std::map& headers, + std::string& snapshotpath, o2::pmr::vector& dest, int& fromSnapshot, std::string const& etag) const; + void releaseNamedSemaphore(boost::interprocess::named_semaphore* sem, std::string const& path) const; + boost::interprocess::named_semaphore* createNamedSemaphore(std::string const& path) const; + static std::string determineSemaphoreName(std::string const& basedir, std::string const& objectpath); + // queries and optionally removes a named semaphore from the system + // returns true when successful (either found or found + removed) + static bool removeSemaphore(std::string const& name, bool remove = false); + static void removeLeakingSemaphores(std::string const& basedir, bool remove = false); + + void loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders = nullptr, bool fetchLocalMetaData = true) const; void loadFileToMemory(o2::pmr::vector& dest, std::string const& path, std::map const& metadata, long timestamp, std::map* headers, std::string const& etag, const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot = true) const; - void navigateURLsAndLoadFileToMemory(o2::pmr::vector& dest, CURL* curl_handle, std::string const& url, std::map* headers) const; + + // Loads files from alien and cvmfs into given destination. + bool loadLocalContentToMemory(o2::pmr::vector& dest, std::string& url) const; + + // add annotated flattened headers in the end of the blob + static void appendFlatHeader(o2::pmr::vector& dest, const std::map& headers); // the failure to load the file to memory is signaled by 0 size and non-0 capacity static bool isMemoryFileInvalid(const o2::pmr::vector& v) { return v.size() == 0 && v.capacity() > 0; } @@ -363,9 +413,36 @@ class CcdbApi //: public DatabaseInterface } return obj; } + + /** + * Retrieves files either as snapshot or schedules them to be downloaded via CCDBDownloader. + * + * @param requestContext Structure giving details about the transfer. + * @param fromSnapshot After navigateSourcesAndLoadFile returns signals whether file was retrieved from snapshot. + * @param requestCounter Pointer to the variable storing the number of requests to be done. + */ + void navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const; + + /** + * Retrieves files described via RequestContexts into memory. Downloads are performed in parallel via CCDBDownloader. + * + * @param requestContext Structure giving details about the transfer. + */ + void vectoredLoadFileToMemory(std::vector& requestContext) const; #endif private: + // Sets the unique agent ID + void setUniqueAgentID(); + + /** + * Schedules download of data associated with the curl_handle. Doing that increments the requestCounter by 1. Requests are performed by running the mUVLoop + * + * @param handle CURL handle associated with the request. + * @param requestCounter Pointer to the variable storing the number of requests to be done. + */ + void asynchPerform(CURL* handle, size_t* requestCounter) const; + // internal helper function to update a CCDB file with meta information static void updateMetaInformationInLocalFile(std::string const& filename, std::map const* headers, CCDBQuery const* querysummary = nullptr); @@ -447,6 +524,23 @@ class CcdbApi //: public DatabaseInterface long timestamp = -1, std::map* headers = nullptr, std::string const& etag = "", const std::string& createdNotAfter = "", const std::string& createdNotBefore = "") const; + /** + * Run the uvLoop belonging to mDownloader once. + * + * @param noWait Using this flag will cause the loop to run only if sockets have pendind data. + */ + void runDownloaderLoop(bool noWait); + /** + * Set the number of times curl should retry in case of failure and the delay between thte attempts. + * @param numberRetries + * @param delay + */ + void setCurlRetriesParameters(int numberRetries, int delay = 100000 /* microseconds */) + { + mCurlRetries = numberRetries; + mCurlDelayRetries = delay; + } + private: /** * A helper function to extract object from a local ROOT file @@ -457,12 +551,12 @@ class CcdbApi //: public DatabaseInterface void* extractFromLocalFile(std::string const& filename, std::type_info const& tinfo, std::map* headers) const; /** - * Helper function to download binary content from alien:// storage + * Helper function to download binary content from alien://, cvmfs or local storage * @param fullUrl The alien URL * @param tcl The TClass object describing the serialized type * @return raw pointer to created object */ - void* downloadAlienContent(std::string const& fullUrl, std::type_info const& tinfo) const; + void* downloadFilesystemContent(std::string const& fullUrl, std::type_info const& tinfo, std::map* headers) const; // initialize the TGrid (Alien connection) bool initTGrid() const; @@ -482,9 +576,6 @@ class CcdbApi //: public DatabaseInterface // convert type_info to TClass, throw on failure static TClass* tinfo2TClass(std::type_info const& tinfo); - // split string on delimiters and return tokens as vector - std::vector splitString(const std::string& str, const char* delimiters); - typedef size_t (*CurlWriteCallback)(void*, size_t, size_t, void*); void initCurlOptionsForRetrieve(CURL* curlHandle, void* pointer, CurlWriteCallback writeCallback, bool followRedirect = true) const; @@ -505,9 +596,9 @@ class CcdbApi //: public DatabaseInterface const std::string& createdNotAfter, const std::string& createdNotBefore, bool followRedirect, CurlWriteCallback writeCallback) const; /** - * Initialize hostsPool - * @param hosts string with hosts separated by "," or ";" - */ + * Initialize hostsPool + * @param hosts string with hosts separated by "," or ";" + */ void initHostsPool(std::string hosts); std::string getHostUrl(int hostIndex) const; @@ -524,12 +615,22 @@ class CcdbApi //: public DatabaseInterface return getSnapshotDir(topdir, path) + '/' + sfile; } + template // can be either std::map or std::multimap + static size_t getFlatHeaderSize(const MAP& Headers) + { + size_t hsize = sizeof(int) + sizeof(FlatHeaderAnnot); // annotation size + for (auto& h : Headers) { + hsize += h.first.length() + h.second.length() + 2; // 2*(string_buffer + terminating null character) + } + return hsize; + } + // tmp helper and single point of entry for a CURL perform call // helps to switch between easy handle perform and multi handles in a single place CURLcode CURL_perform(CURL* handle) const; mutable CCDBDownloader* mDownloader = nullptr; //! the multi-handle (async) CURL downloader - bool mIsCCDBDownloaderEnabled = false; + bool mIsCCDBDownloaderPreferred = false; /// Base URL of the CCDB (with port) std::string mUniqueAgentID{}; // Unique User-Agent ID communicated to server for logging std::string mUrl{}; @@ -541,6 +642,12 @@ class CcdbApi //: public DatabaseInterface mutable TGrid* mAlienInstance = nullptr; // a cached connection to TGrid (needed for Alien locations) bool mNeedAlienToken = true; // On EPN and FLP we use a local cache and don't need the alien token static std::unique_ptr mJAlienCredentials; // access JAliEn credentials + int mCurlRetries = 3; + int mCurlDelayRetries = 100000; // in microseconds + size_t mCurlTimeoutDownload = 15; // download timeout in seconds, can be configured via ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD, updated according to the deployment mode + size_t mCurlTimeoutUpload = 15; // upload timeout in seconds, can be configured via ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD, updated according to the deployment mode + + static constexpr char FlatHeaderAnnot[] = "$HEADER$"; // annotation for flat header ClassDefNV(CcdbApi, 1); }; @@ -572,4 +679,4 @@ typename std::enable_if::value, } // namespace ccdb } // namespace o2 -#endif //PROJECT_CCDBAPI_H +#endif // PROJECT_CCDBAPI_H diff --git a/CCDB/include/CCDB/CcdbObjectInfo.h b/CCDB/include/CCDB/CcdbObjectInfo.h index 6db4cffd63b85..117ca1123104f 100644 --- a/CCDB/include/CCDB/CcdbObjectInfo.h +++ b/CCDB/include/CCDB/CcdbObjectInfo.h @@ -93,6 +93,16 @@ class CcdbObjectInfo [[nodiscard]] long getEndValidityTimestamp() const { return mEnd; } void setEndValidityTimestamp(long end) { mEnd = end; } + bool operator<(const CcdbObjectInfo& other) const + { + return mStart < other.mStart; + } + + bool operator>(const CcdbObjectInfo& other) const + { + return mStart > other.mStart; + } + private: std::string mObjType{}; // object type (e.g. class) std::string mFileName{}; // file name in the CCDB @@ -107,4 +117,17 @@ class CcdbObjectInfo } // namespace o2::ccdb +namespace std +{ +// defining std::hash for InteractionRecord to be used with std containers +template <> +struct hash { + public: + size_t operator()(const o2::ccdb::CcdbObjectInfo& info) const + { + return info.getStartValidityTimestamp(); + } +}; +} // namespace std + #endif // O2_CCDB_CCDBOBJECTINFO_H_ diff --git a/CCDB/src/BasicCCDBManager.cxx b/CCDB/src/BasicCCDBManager.cxx index 148de1048b4d9..d55fdad960d3a 100644 --- a/CCDB/src/BasicCCDBManager.cxx +++ b/CCDB/src/BasicCCDBManager.cxx @@ -13,6 +13,8 @@ // Created by Sandro Wenzel on 2019-08-14. // #include "CCDB/BasicCCDBManager.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include @@ -32,29 +34,74 @@ void CCDBManagerInstance::reportFatal(std::string_view err) LOG(fatal) << err; } -std::pair CCDBManagerInstance::getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal) +std::pair CCDBManagerInstance::getRunDuration(const std::map& headers) { - auto response = api.retrieveHeaders("RCT/Info/RunInformation", std::map(), runnumber); - if (response.size() == 0 || response.find("SOR") == response.end() || response.find("EOR") == response.end()) { - if (fatal) { - LOG(fatal) << "Empty or missing response from query to RCT/Info/RunInformation for run " << runnumber; - } else { - return std::make_pair(-1L, -1L); + if (headers.size() != 0) { + std::string report{}; + auto strt = headers.find("STF"); + auto stop = headers.find("ETF"); + long valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + long valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + if (valStrt < 0 || valStop < 0) { + report += "Missing STF/EFT -> use SOX/EOX;"; + strt = headers.find("SOX"); + valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + if (valStrt < 1) { + report += fmt::format(" Missing/invalid SOX -> use SOR"); + strt = headers.find("SOR"); + valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); + } + stop = headers.find("EOX"); + valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + if (valStop < 1) { + report += fmt::format(" | Missing/invalid EOX -> use EOR"); + stop = headers.find("EOR"); + valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); + } + if (!report.empty()) { + LOGP(warn, "{}", report); + } } + return std::make_pair(valStrt, valStop); } - auto sor = boost::lexical_cast(response["SOR"]); - auto eor = boost::lexical_cast(response["EOR"]); - return std::make_pair(sor, eor); + return std::make_pair(-1L, -1L); } -std::pair CCDBManagerInstance::getRunDuration(int runnumber, bool fatal) const +std::pair CCDBManagerInstance::getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool fatal) { - return CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + auto headers = api.retrieveHeaders("RCT/Info/RunInformation", std::map(), runnumber); + auto response = getRunDuration(headers); + if ((response.first <= 0 || response.second < response.first) && fatal) { + LOG(fatal) << "Empty, missing or invalid response from query to RCT/Info/RunInformation for run " << runnumber; + } + return response; +} + +std::pair CCDBManagerInstance::getRunDuration(int runnumber, bool fatal) +{ + mQueries++; + if (!isCachingEnabled()) { + return CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + } + auto& cached = mCache["RCT-Run-Info HeaderOnly"]; + std::pair rd; + cached.queries++; + if (cached.startvalidity != runnumber) { // need to fetch + rd = CCDBManagerInstance::getRunDuration(mCCDBAccessor, runnumber, fatal); + cached.objPtr = std::make_shared>(rd); + cached.startvalidity = runnumber; + cached.endvalidity = runnumber + 1; + cached.minSize = cached.maxSize = 0; + cached.fetches++; + } else { + rd = *reinterpret_cast*>(cached.objPtr.get()); + } + return rd; } std::string CCDBManagerInstance::getSummaryString() const { - std::string res = fmt::format("{} queries", mQueries); + std::string res = fmt::format("{} queries, {} bytes", mQueries, fmt::group_digits(mFetchedSize)); if (mCachingEnabled) { res += fmt::format(" for {} objects", mCache.size()); } @@ -72,9 +119,20 @@ std::string CCDBManagerInstance::getSummaryString() const return res; } -void CCDBManagerInstance::endOfStream() +void CCDBManagerInstance::report(bool longrep) { LOG(info) << "CCDBManager summary: " << getSummaryString(); + if (longrep && mCachingEnabled) { + LOGP(info, "CCDB cache miss/hit/failures"); + for (const auto& obj : mCache) { + LOGP(info, " {}: {}/{}/{} ({}-{} bytes)", obj.first, obj.second.fetches, obj.second.queries - obj.second.fetches - obj.second.failures, obj.second.failures, obj.second.minSize, obj.second.maxSize); + } + } +} + +void CCDBManagerInstance::endOfStream() +{ + report(true); } } // namespace ccdb diff --git a/CCDB/src/CCDBDownloader.cxx b/CCDB/src/CCDBDownloader.cxx index c879a245c6987..2f033a50b36e7 100644 --- a/CCDB/src/CCDBDownloader.cxx +++ b/CCDB/src/CCDBDownloader.cxx @@ -10,10 +10,11 @@ // or submit itself to any jurisdiction. #include +#include "CommonUtils/StringUtils.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "Framework/Signpost.h" #include -#include - #include #include #include @@ -26,133 +27,153 @@ #include #include #include +#include +#include + +O2_DECLARE_DYNAMIC_STACKTRACE_LOG(ccdb_downloader); namespace o2::ccdb { +void uvErrorCheck(int code, DownloaderErrorLevel level) +{ + if (code != 0) { + char buf[1000]; + uv_strerror_r(code, buf, 1000); + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + if (level == SEVERE) { + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "UV error - %{public}s", buf); + } else { + O2_SIGNPOST_EVENT_EMIT_WARN(ccdb_downloader, sid, "CCDBDownloader", "UV minor error - %{public}s", buf); + } + } +} + +void curlEasyErrorCheck(CURLcode code) +{ + if (code != CURLE_OK) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CURL error - %{public}s", curl_easy_strerror(code)); + } +} + +void curlMultiErrorCheck(CURLMcode code) +{ + if (code != CURLM_OK) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CURL error - %{public}s", curl_multi_strerror(code)); + } +} +namespace +{ +std::string uniqueAgentID() +{ + std::string host = boost::asio::ip::host_name(); + char const* jobID = getenv("ALIEN_PROC_ID"); + if (jobID) { + return fmt::format("{}-{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6), jobID); + } else { + return fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + } +} +} // namespace + CCDBDownloader::CCDBDownloader(uv_loop_t* uv_loop) + : mUserAgentId(uniqueAgentID()) { if (uv_loop) { + mExternalLoop = true; mUVLoop = uv_loop; - mIsExternalLoop = true; } else { - mUVLoop = new uv_loop_t(); - mIsExternalLoop = false; + mExternalLoop = false; + setupInternalUVLoop(); } // Preparing timer to be used by curl - mTimeoutTimer = new uv_timer_t(); + mTimeoutTimer = (uv_timer_t*)malloc(sizeof(*mTimeoutTimer)); mTimeoutTimer->data = this; - uv_loop_init(mUVLoop); - uv_timer_init(mUVLoop, mTimeoutTimer); + uvErrorCheck(uv_timer_init(mUVLoop, mTimeoutTimer), SEVERE); mHandleMap[(uv_handle_t*)mTimeoutTimer] = true; - // Preparing curl handle initializeMultiHandle(); +} - // Global timer - // uv_loop runs only when there are active handles, this handle guarantees the loop won't close immedietly after starting - auto timerCheckQueueHandle = new uv_timer_t(); - timerCheckQueueHandle->data = this; - uv_timer_init(mUVLoop, timerCheckQueueHandle); - mHandleMap[(uv_handle_t*)timerCheckQueueHandle] = true; - uv_timer_start(timerCheckQueueHandle, checkStopSignal, 100, 100); - - mLoopThread = new std::thread(&CCDBDownloader::runLoop, this); +void CCDBDownloader::setupInternalUVLoop() +{ + mUVLoop = new uv_loop_t(); + uvErrorCheck(uv_loop_init(mUVLoop), SEVERE); } void CCDBDownloader::initializeMultiHandle() { mCurlMultiHandle = curl_multi_init(); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETFUNCTION, handleSocket); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETFUNCTION, handleSocket)); auto socketData = &mSocketData; socketData->curlm = mCurlMultiHandle; socketData->CD = this; - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETDATA, socketData); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERFUNCTION, startTimeout); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERDATA, mTimeoutTimer); - curl_multi_setopt(mCurlMultiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, mMaxHandlesInUse); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_SOCKETDATA, socketData)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERFUNCTION, startTimeout)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_TIMERDATA, mTimeoutTimer)); + curlMultiErrorCheck(curl_multi_setopt(mCurlMultiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, mMaxHandlesInUse)); } CCDBDownloader::~CCDBDownloader() { - // Cleanup and close all socket timers (curl_multi_cleanup will take care of the sockets) - for (auto socketTimerPair : mSocketTimerMap) { - auto timer = socketTimerPair.second; - if (timer->data) { - delete (DataForClosingSocket*)timer->data; - } - uv_timer_stop(socketTimerPair.second); - uv_close((uv_handle_t*)socketTimerPair.second, onUVClose); - } - // all timers have been closed --> so clear this map (otherwise it may get accessed in different callbacks again - // ... for instance when called from within curl_multi_cleanup) - mSocketTimerMap.clear(); - - // Close loop thread - mCloseLoop = true; - mLoopThread->join(); - delete mLoopThread; - - // Close the loop and if any handles are running then signal to close, and run loop once to close them - // This may take more then one iteration of loop - hence the "while" - if (mIsExternalLoop) { - uv_walk(mUVLoop, closeHandles, this); - } else { - while (UV_EBUSY == uv_loop_close(mUVLoop)) { - mCloseLoop = false; + // Loop has been ordered to stop via signalToClose() + curlMultiErrorCheck(curl_multi_cleanup(mCurlMultiHandle)); + + if (!mExternalLoop) { + // Schedule all handles to close. Execute loop to allow them to execute their destructors. + while (uv_loop_alive(mUVLoop) || (uv_loop_close(mUVLoop) == UV_EBUSY)) { uv_walk(mUVLoop, closeHandles, this); uv_run(mUVLoop, UV_RUN_ONCE); } delete mUVLoop; } - - // delete timer - // delete mTimeoutTimer; ---> not necessay (done elsewhere??) - - curl_multi_cleanup(mCurlMultiHandle); } void closeHandles(uv_handle_t* handle, void* arg) { auto CD = (CCDBDownloader*)arg; - if (!uv_is_closing(handle) && CD->mHandleMap.find(handle) != CD->mHandleMap.end()) { - CD->mHandleMap.erase(handle); - uv_close(handle, onUVClose); + // Close only handles belonging to the Downloader + if (CD->mHandleMap.find(handle) != CD->mHandleMap.end()) { + if (!uv_is_closing(handle)) { + uv_close(handle, onUVClose); + } } } void onUVClose(uv_handle_t* handle) { if (handle != nullptr) { - delete handle; + free(handle); } } -void CCDBDownloader::checkStopSignal(uv_timer_t* handle) -{ - // Check for closing signal - auto CD = (CCDBDownloader*)handle->data; - if (CD->mCloseLoop) { - uv_timer_stop(handle); - uv_stop(CD->mUVLoop); - } - CD->checkForThreadsToJoin(); -} - void CCDBDownloader::closesocketCallback(void* clientp, curl_socket_t item) { auto CD = (CCDBDownloader*)clientp; - if (CD->mSocketTimerMap.find(item) != CD->mSocketTimerMap.end()) { - auto timer = CD->mSocketTimerMap[item]; - uv_timer_stop(timer); - // we are getting rid of the uv_timer_t pointer ... so we need - // to free possibly attached user data pointers as well. Counteracts action of opensocketCallback - if (timer->data) { - delete (DataForClosingSocket*)timer->data; + if (CD->mExternalLoop) { + // If external uv loop is used then the keepalive mechanism is active. + if (CD->mSocketTimerMap.find(item) != CD->mSocketTimerMap.end()) { + auto timer = CD->mSocketTimerMap[item]; + uvErrorCheck(uv_timer_stop(timer), SEVERE); + // we are getting rid of the uv_timer_t pointer ... so we need + // to free possibly attached user data pointers as well. Counteracts action of opensocketCallback + if (timer->data) { + delete (DataForClosingSocket*)timer->data; + } + CD->mSocketTimerMap.erase(item); + if (close(item) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); + } + } + } else { + if (close(item) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); } - CD->mSocketTimerMap.erase(item); - close(item); } } @@ -160,26 +181,25 @@ curl_socket_t opensocketCallback(void* clientp, curlsocktype purpose, struct cur { auto CD = (CCDBDownloader*)clientp; auto sock = socket(address->family, address->socktype, address->protocol); + if (sock == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to open"); + } - CD->mSocketTimerMap[sock] = new uv_timer_t(); - uv_timer_init(CD->mUVLoop, CD->mSocketTimerMap[sock]); - CD->mHandleMap[(uv_handle_t*)CD->mSocketTimerMap[sock]] = true; + if (CD->mExternalLoop) { + CD->mSocketTimerMap[sock] = (uv_timer_t*)malloc(sizeof(*CD->mSocketTimerMap[sock])); + uvErrorCheck(uv_timer_init(CD->mUVLoop, CD->mSocketTimerMap[sock]), SEVERE); + CD->mHandleMap[(uv_handle_t*)CD->mSocketTimerMap[sock]] = true; - auto data = new DataForClosingSocket(); - data->CD = CD; - data->socket = sock; - CD->mSocketTimerMap[sock]->data = data; + auto data = new DataForClosingSocket(); + data->CD = CD; + data->socket = sock; + CD->mSocketTimerMap[sock]->data = data; + } return sock; } -void CCDBDownloader::asyncUVHandleCheckQueue(uv_async_t* handle) -{ - auto CD = (CCDBDownloader*)handle->data; - uv_close((uv_handle_t*)handle, onUVClose); - CD->checkHandleQueue(); -} - void CCDBDownloader::closeSocketByTimer(uv_timer_t* handle) { auto data = (DataForClosingSocket*)handle->data; @@ -187,10 +207,12 @@ void CCDBDownloader::closeSocketByTimer(uv_timer_t* handle) auto sock = data->socket; if (CD->mSocketTimerMap.find(sock) != CD->mSocketTimerMap.end()) { - uv_timer_stop(CD->mSocketTimerMap[sock]); + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[sock]), SEVERE); CD->mSocketTimerMap.erase(sock); - close(sock); - + if (close(sock) == -1) { + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); + } delete data; } } @@ -205,6 +227,7 @@ void CCDBDownloader::curlTimeout(uv_timer_t* handle) void CCDBDownloader::curlPerform(uv_poll_t* handle, int status, int events) { + uvErrorCheck(status, MINOR); int running_handles; int flags = 0; if (events & UV_READABLE) { @@ -216,7 +239,7 @@ void CCDBDownloader::curlPerform(uv_poll_t* handle, int status, int events) auto context = (CCDBDownloader::curl_context_t*)handle->data; - curl_multi_socket_action(context->CD->mCurlMultiHandle, context->sockfd, flags, &running_handles); + curlMultiErrorCheck(curl_multi_socket_action(context->CD->mCurlMultiHandle, context->sockfd, flags, &running_handles)); context->CD->checkMultiInfo(); } @@ -233,7 +256,7 @@ int CCDBDownloader::handleSocket(CURL* easy, curl_socket_t s, int action, void* case CURL_POLL_INOUT: curl_context = socketp ? (CCDBDownloader::curl_context_t*)socketp : CD->createCurlContext(s); - curl_multi_assign(socketData->curlm, s, (void*)curl_context); + curlMultiErrorCheck(curl_multi_assign(socketData->curlm, s, (void*)curl_context)); if (action != CURL_POLL_IN) { events |= UV_WRITABLE; @@ -242,20 +265,23 @@ int CCDBDownloader::handleSocket(CURL* easy, curl_socket_t s, int action, void* events |= UV_READABLE; } - if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { - uv_timer_stop(CD->mSocketTimerMap[s]); + if (CD->mExternalLoop && CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[s]), SEVERE); } - uv_poll_start(curl_context->poll_handle, events, curlPerform); + uvErrorCheck(uv_poll_start(curl_context->poll_handle, events, curlPerform), SEVERE); break; case CURL_POLL_REMOVE: if (socketp) { - if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { - uv_timer_start(CD->mSocketTimerMap[s], closeSocketByTimer, CD->mSocketTimeoutMS, 0); + if (CD->mExternalLoop) { + // If external loop is used then start the keepalive timeout. + if (CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { + uvErrorCheck(uv_timer_start(CD->mSocketTimerMap[s], closeSocketByTimer, CD->mKeepaliveTimeoutMS, 0), SEVERE); + } } - uv_poll_stop(((CCDBDownloader::curl_context_t*)socketp)->poll_handle); + uvErrorCheck(uv_poll_stop(((CCDBDownloader::curl_context_t*)socketp)->poll_handle), SEVERE); CD->destroyCurlContext((CCDBDownloader::curl_context_t*)socketp); - curl_multi_assign(socketData->curlm, s, nullptr); + curlMultiErrorCheck(curl_multi_assign(socketData->curlm, s, nullptr)); } break; default: @@ -270,21 +296,38 @@ void CCDBDownloader::setMaxParallelConnections(int limit) mMaxHandlesInUse = limit; } -void CCDBDownloader::setSocketTimoutTime(int timoutMS) +void CCDBDownloader::setKeepaliveTimeoutTime(int timeoutMS) { - mSocketTimeoutMS = timoutMS; + mKeepaliveTimeoutMS = timeoutMS; } -void CCDBDownloader::checkForThreadsToJoin() +void CCDBDownloader::setConnectionTimeoutTime(int timeoutMS) { - for (int i = 0; i < mThreadFlagPairVector.size(); i++) { - if (*(mThreadFlagPairVector[i].second)) { - mThreadFlagPairVector[i].first->join(); - delete (mThreadFlagPairVector[i].first); - delete (mThreadFlagPairVector[i].second); - mThreadFlagPairVector.erase(mThreadFlagPairVector.begin() + i); - } - } + mConnectionTimeoutMS = timeoutMS; +} + +void CCDBDownloader::setRequestTimeoutTime(int timeoutMS) +{ + mRequestTimeoutMS = timeoutMS; +} + +void CCDBDownloader::setHappyEyeballsHeadstartTime(int headstartMS) +{ + mHappyEyeballsHeadstartMS = headstartMS; +} + +void CCDBDownloader::setOfflineTimeoutSettings() +{ + setConnectionTimeoutTime(60000); + setRequestTimeoutTime(300000); + setHappyEyeballsHeadstartTime(500); +} + +void CCDBDownloader::setOnlineTimeoutSettings() +{ + setConnectionTimeoutTime(5000); + setRequestTimeoutTime(30000); + setHappyEyeballsHeadstartTime(500); } CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t sockfd) @@ -294,9 +337,9 @@ CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t context = (curl_context_t*)malloc(sizeof(*context)); context->CD = this; context->sockfd = sockfd; - context->poll_handle = new uv_poll_t(); + context->poll_handle = (uv_poll_t*)malloc(sizeof(*context->poll_handle)); - uv_poll_init_socket(mUVLoop, context->poll_handle, sockfd); + uvErrorCheck(uv_poll_init_socket(mUVLoop, context->poll_handle, sockfd), SEVERE); mHandleMap[(uv_handle_t*)(context->poll_handle)] = true; context->poll_handle->data = context; @@ -306,7 +349,7 @@ CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t void CCDBDownloader::curlCloseCB(uv_handle_t* handle) { auto* context = (curl_context_t*)handle->data; - delete context->poll_handle; + free(context->poll_handle); free(context); } @@ -315,46 +358,233 @@ void CCDBDownloader::destroyCurlContext(curl_context_t* context) uv_close((uv_handle_t*)context->poll_handle, curlCloseCB); } -void callbackWrappingFunction(void (*cbFun)(void*), void* data, bool* completionFlag) +void CCDBDownloader::tryNewHost(PerformData* performData, CURL* easy_handle) +{ + auto requestData = performData->requestData; + std::string newUrl = requestData->hosts.at(performData->hostInd) + "/" + requestData->path + "/" + std::to_string(requestData->timestamp); + LOG(debug) << "Connecting to another host " << newUrl << "\n"; + requestData->hoPair.header.clear(); + curl_easy_setopt(easy_handle, CURLOPT_URL, newUrl.c_str()); + mHandlesToBeAdded.push_back(easy_handle); +} + +void CCDBDownloader::getLocalContent(PerformData* performData, std::string& newLocation, bool& contentRetrieved, std::vector& locations) +{ + auto requestData = performData->requestData; + LOG(debug) << "Redirecting to local content " << newLocation << "\n"; + if (requestData->localContentCallback(newLocation)) { + contentRetrieved = true; + LOG(debug) << "Local content retrieved succesfully: " << newLocation << " n"; + } else { + // Prepare next redirect url + newLocation = getNewLocation(performData, locations); + LOG(debug) << "Failed to retrieve local content: " << newLocation << "\n"; + } +} + +std::string CCDBDownloader::getNewLocation(PerformData* performData, std::vector& locations) const { - cbFun(data); - *completionFlag = true; + auto requestData = performData->requestData; + if (performData->locInd < locations.size()) { + std::string newLocation = locations.at(performData->locInd++); + std::string hostUrl = requestData->hosts.at(performData->hostInd); + std::string newUrl = prepareRedirectedURL(newLocation, hostUrl); + return newUrl; + } else { + return ""; + } +} + +void CCDBDownloader::httpRedirect(PerformData* performData, std::string& newLocation, CURL* easy_handle) +{ + auto requestData = performData->requestData; + LOG(debug) << "Trying content location " << newLocation << "\n"; + curl_easy_setopt(easy_handle, CURLOPT_URL, newLocation.c_str()); + mHandlesToBeAdded.push_back(easy_handle); +} + +void CCDBDownloader::followRedirect(PerformData* performData, CURL* easy_handle, std::vector& locations, bool& rescheduled, bool& contentRetrieved) +{ + std::string newLocation = getNewLocation(performData, locations); + while (!contentRetrieved && (newLocation.find("alien:/", 0) != std::string::npos || newLocation.find("file:/", 0) != std::string::npos)) { + getLocalContent(performData, newLocation, contentRetrieved, locations); + } + if (!contentRetrieved && newLocation != "") { + httpRedirect(performData, newLocation, easy_handle); + rescheduled = true; + } +} + +std::string CCDBDownloader::trimHostUrl(std::string full_host_url) const +{ + CURLU* host_url = curl_url(); + curl_url_set(host_url, CURLUPART_URL, full_host_url.c_str(), 0); + + // Get host part (the only critical part) + char* host; + CURLUcode host_result = curl_url_get(host_url, CURLUPART_HOST, &host, 0); + if (host_result != CURLUE_OK) { + LOG(error) << "CCDBDownloader: Malformed url detected when processing redirect, could not identify the host part: " << full_host_url; + curl_url_cleanup(host_url); + return ""; + } + // Get scheme (protocol) part + char* scheme; + CURLUcode scheme_result = curl_url_get(host_url, CURLUPART_SCHEME, &scheme, 0); + // Get port + char* port; + CURLUcode port_result = curl_url_get(host_url, CURLUPART_PORT, &port, 0); + + curl_url_cleanup(host_url); + + // Assemble parts + std::string trimmed_url = ""; + if (scheme_result == CURLUE_OK) { + trimmed_url += scheme + std::string("://"); + free(scheme); + } + trimmed_url += host; + free(host); + if (port_result == CURLUE_OK) { + trimmed_url += std::string(":") + port; + free(port); + } + return trimmed_url; +} + +std::string CCDBDownloader::prepareRedirectedURL(std::string address, std::string potentialHost) const +{ + // If it is an alien or local address it does not need preparation + if (address.find("alien:/") != std::string::npos || address.find("file:/") != std::string::npos) { + return address; + } + // Check if URL contains a scheme (protocol) + CURLU* redirected_url = curl_url(); + curl_url_set(redirected_url, CURLUPART_URL, address.c_str(), 0); + char* scheme; + CURLUcode scheme_result = curl_url_get(redirected_url, CURLUPART_SCHEME, &scheme, 0); + curl_free(scheme); + curl_url_cleanup(redirected_url); + if (scheme_result == CURLUE_OK) { + // The redirected_url contains a scheme (protocol) so there is no need for preparation + return address; + } + // If the address doesn't contain a scheme it means it is a relative url. We need to append it to the trimmed host url + // The host url must be trimmed from it's path (if it ends in one) as otherwise the redirection url would be appended after said path + return trimHostUrl(potentialHost) + address; } void CCDBDownloader::transferFinished(CURL* easy_handle, CURLcode curlCode) { mHandlesInUse--; - PerformData* data; - curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &data); + PerformData* performData; + curlEasyErrorCheck(curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &performData)); - curl_multi_remove_handle(mCurlMultiHandle, easy_handle); - *data->codeDestination = curlCode; + curlMultiErrorCheck(curl_multi_remove_handle(mCurlMultiHandle, easy_handle)); + *performData->codeDestination = curlCode; - // If no requests left then signal finished based on type of operation - if (--(*data->requestsLeft) == 0) { - switch (data->type) { - case BLOCKING: - data->cv->notify_all(); - break; - case ASYNCHRONOUS: - *data->completionFlag = true; - break; - case ASYNCHRONOUS_WITH_CALLBACK: - *data->completionFlag = true; - bool* cbFlag = (bool*)malloc(sizeof(bool)); - *cbFlag = false; - auto cbThread = new std::thread(&callbackWrappingFunction, data->cbFun, data->cbData, cbFlag); - mThreadFlagPairVector.emplace_back(cbThread, cbFlag); - break; - } + bool rescheduled = false; + bool contentRetrieved = false; + + if (curlCode != 0) { + LOG(error) << "CCDBDownloader CURL transfer error - " << curl_easy_strerror(curlCode) << "\n"; + } + + switch (performData->type) { + case BLOCKING: { + --(*performData->requestsLeft); + } break; + case ASYNCHRONOUS: { + DownloaderRequestData* requestData = performData->requestData; + if (requestData->headers) { + for (auto& p : requestData->hoPair.header) { + (*requestData->headers)[p.first] = p.second; + } + } + // Log that transfer finished + long httpCode; + curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &httpCode); + char* url; + curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &url); + LOG(debug) << "Transfer for " << url << " finished with code " << httpCode << "\n"; + std::string currentHost = requestData->hosts[performData->hostInd]; + std::string loggingMessage = prepareLogMessage(currentHost, requestData->userAgent, requestData->path, requestData->timestamp, requestData->headers, httpCode); + + // Get new locations based on received headers + updateLocations(&(requestData->hoPair.header), &requestData->locations, &performData->locInd); + + // React to received http code + if (200 <= httpCode && httpCode < 400) { + LOG(debug) << loggingMessage; + if (304 == httpCode) { + LOGP(debug, "Object exists but I am not serving it since it's already in your possession"); + contentRetrieved = true; + } else if (300 <= httpCode && httpCode < 400 && performData->locInd < requestData->locations.size()) { + followRedirect(performData, easy_handle, requestData->locations, rescheduled, contentRetrieved); + } else if (200 <= httpCode && httpCode < 300) { + contentRetrieved = true; // Can be overruled by following error check + } + } else { + LOG(error) << loggingMessage; + } + + // Check for errors + if (curlCode != 0) { + contentRetrieved = false; + } + + // Check if content was retrieved or scheduled to be retrieved + if (!rescheduled && !contentRetrieved) { + // Current location failed without providing 3xx http code, try next redirect for the same host + if (performData->locInd < requestData->locations.size()) { + followRedirect(performData, easy_handle, requestData->locations, rescheduled, contentRetrieved); + } + } + + // Check again because content might have been retrieved or rescheduled via a redirect + if (!rescheduled && !contentRetrieved) { + // Ran out of locations to redirect, try new host + if (++performData->hostInd < requestData->hosts.size()) { + tryNewHost(performData, easy_handle); + rescheduled = true; + } else { + LOG(error) << "File " << requestData->path << " could not be retrieved. No more hosts to try."; + } + } + + if (!rescheduled) { + // No more transfers will be done for this request, do cleanup specific for ASYNCHRONOUS calls + if (!contentRetrieved) { + if (requestData->hoPair.object) { + requestData->hoPair.object->clear(); + } + if (requestData->headers) { + (*requestData->headers)["Error"] = "An error occurred during retrieval"; + } + LOGP(alarm, "Curl request to {}, response code: {}", url, httpCode); + } else { + if (requestData->headers && requestData->headers->find("fileSize") == requestData->headers->end()) { + (*requestData->headers)["fileSize"] = fmt::format("{}", requestData->hoPair.object ? requestData->hoPair.object->size() : 0); + } + } + --(*performData->requestsLeft); + curl_slist_free_all(*performData->options); + delete requestData; + delete performData->codeDestination; + curl_easy_cleanup(easy_handle); + } + } break; + } + if (!rescheduled) { + // No more transfers will be done for this request, do general cleanup + delete performData; } - delete data; checkHandleQueue(); - // Calling timout starts a new download if a new easy_handle was added. + // Calling timeout starts a new download if a new easy_handle was added. int running_handles; - curl_multi_socket_action(mCurlMultiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles); + curlMultiErrorCheck(curl_multi_socket_action(mCurlMultiHandle, CURL_SOCKET_TIMEOUT, 0, &running_handles)); checkMultiInfo(); } @@ -382,44 +612,45 @@ int CCDBDownloader::startTimeout(CURLM* multi, long timeout_ms, void* userp) auto timeout = (uv_timer_t*)userp; if (timeout_ms < 0) { - uv_timer_stop(timeout); + uvErrorCheck(uv_timer_stop(timeout), SEVERE); } else { if (timeout_ms == 0) { timeout_ms = 1; // Calling curlTimeout when timeout = 0 could create an infinite loop } - uv_timer_start(timeout, curlTimeout, timeout_ms, 0); + uvErrorCheck(uv_timer_start(timeout, curlTimeout, timeout_ms, 0), SEVERE); } return 0; } void CCDBDownloader::setHandleOptions(CURL* handle, PerformData* data) { - curl_easy_setopt(handle, CURLOPT_PRIVATE, data); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_PRIVATE, data)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CLOSESOCKETFUNCTION, closesocketCallback)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CLOSESOCKETDATA, this)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, opensocketCallback)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_OPENSOCKETDATA, this)); - curl_easy_setopt(handle, CURLOPT_CLOSESOCKETFUNCTION, closesocketCallback); - curl_easy_setopt(handle, CURLOPT_CLOSESOCKETDATA, this); - curl_easy_setopt(handle, CURLOPT_OPENSOCKETFUNCTION, opensocketCallback); - curl_easy_setopt(handle, CURLOPT_OPENSOCKETDATA, this); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, mRequestTimeoutMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, mConnectionTimeoutMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, mHappyEyeballsHeadstartMS)); + curlEasyErrorCheck(curl_easy_setopt(handle, CURLOPT_USERAGENT, mUserAgentId.c_str())); } void CCDBDownloader::checkHandleQueue() { - // Lock access to handle queue - mHandlesQueueLock.lock(); if (mHandlesToBeAdded.size() > 0) { // Add handles without going over the limit while (mHandlesToBeAdded.size() > 0 && mHandlesInUse < mMaxHandlesInUse) { - curl_multi_add_handle(mCurlMultiHandle, mHandlesToBeAdded.front()); + curlMultiErrorCheck(curl_multi_add_handle(mCurlMultiHandle, mHandlesToBeAdded.front())); mHandlesInUse++; mHandlesToBeAdded.erase(mHandlesToBeAdded.begin()); } } - mHandlesQueueLock.unlock(); } -void CCDBDownloader::runLoop() +void CCDBDownloader::runLoop(bool noWait) { - uv_run(mUVLoop, UV_RUN_DEFAULT); + uv_run(mUVLoop, noWait ? UV_RUN_NOWAIT : UV_RUN_ONCE); } CURLcode CCDBDownloader::perform(CURL* handle) @@ -429,92 +660,113 @@ CURLcode CCDBDownloader::perform(CURL* handle) return batchBlockingPerform(handleVector).back(); } -std::vector CCDBDownloader::batchAsynchPerform(std::vector const& handleVector, bool* completionFlag) +void CCDBDownloader::updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const { - std::vector codeVector(handleVector.size()); - size_t* requestsLeft = new size_t(); - *requestsLeft = handleVector.size(); - - mHandlesQueueLock.lock(); - for (int i = 0; i < handleVector.size(); i++) { - auto* data = new CCDBDownloader::PerformData(); - - data->codeDestination = &(codeVector)[i]; - (codeVector)[i] = CURLE_FAILED_INIT; + std::vector newLocations; - data->requestsLeft = requestsLeft; - data->completionFlag = completionFlag; - data->type = ASYNCHRONOUS; + auto iter = headerMap->find("Location"); + if (iter != headerMap->end()) { + auto range = headerMap->equal_range("Location"); + for (auto it = range.first; it != range.second; ++it) { + if (std::find(locations->begin(), locations->end(), it->second) == locations->end()) { + if (std::find(newLocations.begin(), newLocations.end(), it->second) == newLocations.end()) { + newLocations.push_back(it->second); + } + } + } + } - setHandleOptions(handleVector[i], data); - mHandlesToBeAdded.push_back(handleVector[i]); + // add alternative locations (not yet included) + auto iter2 = headerMap->find("Content-Location"); + if (iter2 != headerMap->end()) { + auto range = headerMap->equal_range("Content-Location"); + for (auto it = range.first; it != range.second; ++it) { + if (std::find(locations->begin(), locations->end(), it->second) == locations->end()) { + if (std::find(newLocations.begin(), newLocations.end(), it->second) == newLocations.end()) { + newLocations.push_back(it->second); + } + } + } } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - return codeVector; + + // Insert location list at the current location index. This assures that the provided locations will be tried first. + locations->insert(locations->begin() + (*locIndex), newLocations.begin(), newLocations.end()); } std::vector CCDBDownloader::batchBlockingPerform(std::vector const& handleVector) { - std::condition_variable cv; - std::mutex cv_m; - std::unique_lock lk(cv_m); - std::vector codeVector(handleVector.size()); size_t requestsLeft = handleVector.size(); - mHandlesQueueLock.lock(); for (int i = 0; i < handleVector.size(); i++) { auto* data = new CCDBDownloader::PerformData(); data->codeDestination = &codeVector[i]; codeVector[i] = CURLE_FAILED_INIT; - data->cv = &cv; data->type = BLOCKING; data->requestsLeft = &requestsLeft; - setHandleOptions(handleVector[i], data); mHandlesToBeAdded.push_back(handleVector[i]); } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - cv.wait(lk); + checkHandleQueue(); + while (requestsLeft > 0) { + uv_run(mUVLoop, UV_RUN_ONCE); + } + return codeVector; } -std::vector CCDBDownloader::asynchBatchPerformWithCallback(std::vector const& handleVector, bool* completionFlag, void (*cbFun)(void*), void* cbData) +void CCDBDownloader::asynchSchedule(CURL* handle, size_t* requestCounter) { - std::vector codeVector(handleVector.size()); - size_t* requestsLeft = new size_t(); - *requestsLeft = handleVector.size(); + (*requestCounter)++; - mHandlesQueueLock.lock(); - for (int i = 0; i < handleVector.size(); i++) { - auto* data = new CCDBDownloader::PerformData(); + CURLcode* codeVector = new CURLcode(); - data->codeDestination = &(codeVector)[i]; - (codeVector)[i] = CURLE_FAILED_INIT; + // Get data about request + DownloaderRequestData* requestData; + std::multimap* headerMap; + std::vector* hostsPool; + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &requestData); + headerMap = &(requestData->hoPair.header); + hostsPool = &(requestData->hosts); + auto* options = &(requestData->optionsList); - data->requestsLeft = requestsLeft; - data->completionFlag = completionFlag; - data->type = ASYNCHRONOUS_WITH_CALLBACK; - data->cbFun = cbFun; - data->cbData = cbData; + // Prepare temporary data about transfer + auto* data = new CCDBDownloader::PerformData(); // Freed in transferFinished + data->codeDestination = codeVector; + *codeVector = CURLE_FAILED_INIT; - setHandleOptions(handleVector[i], data); - mHandlesToBeAdded.push_back(handleVector[i]); - } - mHandlesQueueLock.unlock(); - makeLoopCheckQueueAsync(); - return codeVector; + data->type = ASYNCHRONOUS; + data->requestsLeft = requestCounter; + data->hostInd = 0; + data->locInd = 0; + data->requestData = requestData; + data->options = options; + + // Prepare handle and schedule download + setHandleOptions(handle, data); + mHandlesToBeAdded.push_back(handle); + + checkHandleQueue(); + + // return codeVector; } -void CCDBDownloader::makeLoopCheckQueueAsync() +std::string CCDBDownloader::prepareLogMessage(std::string host_url, std::string userAgent, const std::string& path, long ts, const std::map* headers, long httpCode) const { - auto asyncHandle = new uv_async_t(); - asyncHandle->data = this; - uv_async_init(mUVLoop, asyncHandle, asyncUVHandleCheckQueue); - uv_async_send(asyncHandle); + std::string upath{path}; + if (headers) { + auto ent = headers->find("Valid-From"); + if (ent != headers->end()) { + upath += "/" + ent->second; + } + ent = headers->find("ETag"); + if (ent != headers->end()) { + upath += "/" + ent->second; + } + } + upath.erase(remove(upath.begin(), upath.end(), '\"'), upath.end()); + return fmt::format("CcdbDownloader finished transfer {}{}{} for {} (agent_id: {}) with http code: {}", host_url, (host_url.back() == '/') ? "" : "/", upath, (ts < 0) ? getCurrentTimestamp() : ts, userAgent, httpCode); } -} // namespace o2 +} // namespace o2::ccdb diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index e695f2e19fa95..42bc13904bf61 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -20,9 +20,11 @@ #include "CommonUtils/StringUtils.h" #include "CommonUtils/FileSystemUtils.h" #include "CommonUtils/MemFileHelper.h" -#include "MemoryResources/MemoryResources.h" +#include "Framework/DefaultsHelpers.h" +#include "Framework/DataTakingContext.h" #include #include +#include #include #include #include @@ -38,12 +40,17 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" namespace o2::ccdb { @@ -53,23 +60,64 @@ using namespace std; std::mutex gIOMutex; // to protect TMemFile IO operations unique_ptr CcdbApi::mJAlienCredentials = nullptr; +/** + * Object, encapsulating a semaphore, regulating + * concurrent (multi-process) access to CCDB snapshot files. + * Intended to be used with smart pointers to achieve automatic resource + * cleanup after the smart pointer goes out of scope. + */ +class CCDBSemaphore +{ + public: + CCDBSemaphore(std::string const& cachepath, std::string const& path); + ~CCDBSemaphore(); + + private: + boost::interprocess::named_semaphore* mSem = nullptr; + std::string mSemName{}; // name under which semaphore is kept by the OS kernel +}; + +// Small registry class with the purpose that a static object +// ensures cleanup of registered semaphores even when programs +// "crash". +class SemaphoreRegistry +{ + public: + SemaphoreRegistry() = default; + ~SemaphoreRegistry(); + void add(CCDBSemaphore const* ptr); + void remove(CCDBSemaphore const* ptr); + + private: + std::unordered_set mStore; +}; +static SemaphoreRegistry gSemaRegistry; + CcdbApi::CcdbApi() { - std::string host = boost::asio::ip::host_name(); - mUniqueAgentID = fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + using namespace o2::framework; + setUniqueAgentID(); - mIsCCDBDownloaderEnabled = getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI") && atoi(getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")); - if (mIsCCDBDownloaderEnabled) { - mDownloader = new CCDBDownloader(); + DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); + mIsCCDBDownloaderPreferred = 0; + if (deploymentMode == DeploymentMode::OnlineDDS && deploymentMode == DeploymentMode::OnlineECS && deploymentMode == DeploymentMode::OnlineAUX && deploymentMode == DeploymentMode::FST) { + mIsCCDBDownloaderPreferred = 1; + } + if (getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")) { // todo rename ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI to ALICEO2_PREFER_MULTIHANDLE_CCDBAPI + mIsCCDBDownloaderPreferred = atoi(getenv("ALICEO2_ENABLE_MULTIHANDLE_CCDBAPI")); } + mDownloader = new CCDBDownloader(); } CcdbApi::~CcdbApi() { curl_global_cleanup(); - if (mDownloader) { - delete mDownloader; - } + delete mDownloader; +} + +void CcdbApi::setUniqueAgentID() +{ + mUniqueAgentID = TAlienUserAgent::BasedOnEnvironment().ToString(); } bool CcdbApi::checkAlienToken() @@ -99,20 +147,22 @@ void CcdbApi::curlInit() CcdbApi::mJAlienCredentials->loadCredentials(); CcdbApi::mJAlienCredentials->selectPreferedCredentials(); - // allow to configure the socket timeout of CCDBDownloader, if activated (for some tuning studies) - if (mIsCCDBDownloaderEnabled) { - if (getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")) { - auto timeoutMS = atoi(getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")); - if (timeoutMS >= 0) { - LOG(info) << "Setting socket timeout to " << timeoutMS << " milliseconds"; - mDownloader->setSocketTimoutTime(timeoutMS); - } + // allow to configure the socket timeout of CCDBDownloader (for some tuning studies) + if (getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")) { + auto timeoutMS = atoi(getenv("ALICEO2_CCDB_SOCKET_TIMEOUT")); + if (timeoutMS >= 0) { + LOG(info) << "Setting socket timeout to " << timeoutMS << " milliseconds"; + mDownloader->setKeepaliveTimeoutTime(timeoutMS); } } } void CcdbApi::init(std::string const& host) { + if (host.empty()) { + throw std::invalid_argument("Empty url passed CcdbApi, cannot initialize. Aborting."); + } + // if host is prefixed with "file://" this is a local snapshot // in this case we init the API in snapshot (readonly) mode constexpr const char* SNAPSHOTPREFIX = "file://"; @@ -133,21 +183,26 @@ void CcdbApi::init(std::string const& host) // In addition, we can monitor exactly which objects are fetched and what is their content. // One can also distribute so obtained caches to sites without network access. // + // THE INFORMATION BELOW IS TEMPORARILY WRONG: the functionality of checking the validity if IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE + // is NOT set is broken. At the moment the code is modified to behave as if the IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is always set + // whenever the ALICEO2_CCDB_LOCALCACHE is defined. + // // When used with the DPL CCDB fetcher (i.e. loadFileToMemory is called), in order to prefer the available snapshot w/o its validity // check an extra variable IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE must be defined, otherwhise the object will be fetched from the // server after the validity check and new snapshot will be created if needed std::string snapshotReport{}; const char* cachedir = getenv("ALICEO2_CCDB_LOCALCACHE"); + namespace fs = std::filesystem; if (cachedir) { if (cachedir[0] == 0) { - mSnapshotCachePath = "."; + mSnapshotCachePath = fs::weakly_canonical(fs::absolute(".")); } else { - mSnapshotCachePath = cachedir; + mSnapshotCachePath = fs::weakly_canonical(fs::absolute(cachedir)); } snapshotReport = fmt::format("(cache snapshots to dir={}", mSnapshotCachePath); } - if (getenv("IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE")) { + if (cachedir) { // || getenv("IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE")) { mPreferSnapshotCache = true; if (mSnapshotCachePath.empty()) { LOGP(fatal, "IGNORE_VALIDITYCHECK_OF_CCDB_LOCALCACHE is defined but the ALICEO2_CCDB_LOCALCACHE is not"); @@ -160,8 +215,57 @@ void CcdbApi::init(std::string const& host) mNeedAlienToken = (host.find("https://") != std::string::npos) || (host.find("alice-ccdb.cern.ch") != std::string::npos); - LOGP(info, "Init CcdApi with UserAgentID: {}, Host: {}{}", mUniqueAgentID, host, - mInSnapshotMode ? "(snapshot readonly mode)" : snapshotReport.c_str()); + // Set the curl timeout. It can be forced with an env var or it has different defaults based on the deployment mode. + if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")) { + auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_DOWNLOAD")); + if (timeout >= 0) { // if valid int + mCurlTimeoutDownload = timeout; + } + } else { // set a default depending on the deployment mode + o2::framework::DeploymentMode deploymentMode = o2::framework::DefaultsHelpers::deploymentMode(); + if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS || + deploymentMode == o2::framework::DeploymentMode::OnlineAUX || + deploymentMode == o2::framework::DeploymentMode::OnlineECS) { + mCurlTimeoutDownload = 15; + } else if (deploymentMode == o2::framework::DeploymentMode::Grid || + deploymentMode == o2::framework::DeploymentMode::FST) { + mCurlTimeoutDownload = 15; + } else if (deploymentMode == o2::framework::DeploymentMode::Local) { + mCurlTimeoutDownload = 5; + } + } + + if (getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD")) { + auto timeout = atoi(getenv("ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD")); + if (timeout >= 0) { // if valid int + mCurlTimeoutUpload = timeout; + } + } else { // set a default depending on the deployment mode + o2::framework::DeploymentMode deploymentMode = o2::framework::DefaultsHelpers::deploymentMode(); + if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS || + deploymentMode == o2::framework::DeploymentMode::OnlineAUX || + deploymentMode == o2::framework::DeploymentMode::OnlineECS) { + mCurlTimeoutUpload = 3; + } else if (deploymentMode == o2::framework::DeploymentMode::Grid || + deploymentMode == o2::framework::DeploymentMode::FST) { + mCurlTimeoutUpload = 20; + } else if (deploymentMode == o2::framework::DeploymentMode::Local) { + mCurlTimeoutUpload = 20; + } + } + if (mDownloader) { + mDownloader->setRequestTimeoutTime(mCurlTimeoutDownload * 1000L); + } + + LOGP(debug, "Curl timeouts are set to: download={:2}, upload={:2} seconds", mCurlTimeoutDownload, mCurlTimeoutUpload); + + LOGP(info, "Init CcdApi with UserAgentID: {}, Host: {}{}, Curl timeouts: upload:{} download:{}", mUniqueAgentID, host, + mInSnapshotMode ? "(snapshot readonly mode)" : snapshotReport.c_str(), mCurlTimeoutUpload, mCurlTimeoutDownload); +} + +void CcdbApi::runDownloaderLoop(bool noWait) +{ + mDownloader->runLoop(noWait); } // A helper function used in a few places. Updates a ROOT file with meta/header information. @@ -192,7 +296,7 @@ void CcdbApi::updateMetaInformationInLocalFile(std::string const& filename, std: */ std::string sanitizeObjectName(const std::string& objectName) { - string tmpObjectName = objectName; + std::string tmpObjectName = objectName; tmpObjectName.erase(std::remove_if(tmpObjectName.begin(), tmpObjectName.end(), [](auto const& c) -> bool { return (!std::isalnum(c) && c != '_' && c != '/' && c != '.'); }), tmpObjectName.end()); @@ -233,6 +337,10 @@ int CcdbApi::storeAsTFile_impl(const void* obj, std::type_info const& tinfo, std std::vector::size_type maxSize) const { // We need the TClass for this type; will verify if dictionary exists + if (!obj) { + LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp); + return -1; + } CcdbObjectInfo info; auto img = createObjectImage(obj, tinfo, &info); return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(), @@ -261,6 +369,10 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin sanitizedEndValidityTimestamp = getFutureTimestamp(60 * 60 * 24 * 1); } if (mInSnapshotMode) { // write local file + if (filename.empty() || buffer == nullptr || size == 0) { + LOGP(alarm, "Snapshot mode does not support headers-only upload"); + return -3; + } auto pthLoc = getSnapshotDir(mSnapshotTopPath, path); o2::utils::createDirectoriesIfAbsent(pthLoc); auto flLoc = getSnapshotFile(mSnapshotTopPath, path, filename); @@ -301,15 +413,17 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin checkMetadataKeys(metadata); if (curl != nullptr) { - struct curl_httppost* formpost = nullptr; - struct curl_httppost* lastptr = nullptr; - curl_formadd(&formpost, - &lastptr, - CURLFORM_COPYNAME, "send", - CURLFORM_BUFFER, filename.c_str(), - CURLFORM_BUFFERPTR, buffer, //.Buffer(), - CURLFORM_BUFFERLENGTH, size, - CURLFORM_END); + auto mime = curl_mime_init(curl); + auto field = curl_mime_addpart(mime); + curl_mime_name(field, "send"); + if (!filename.empty()) { + curl_mime_filedata(field, filename.c_str()); + } + if (buffer != nullptr && size > 0) { + curl_mime_data(field, buffer, size); + } else { + curl_mime_data(field, "", 0); + } struct curl_slist* headerlist = nullptr; static const char buf[] = "Expect:"; @@ -317,15 +431,16 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin curlSetSSLOptions(curl); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, mCurlTimeoutUpload); CURLcode res = CURL_LAST; for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res > 0; hostIndex++) { - string fullUrl = getFullUrlForStorage(curl, path, objectType, metadata, sanitizedStartValidityTimestamp, sanitizedEndValidityTimestamp, hostIndex); + std::string fullUrl = getFullUrlForStorage(curl, path, objectType, metadata, sanitizedStartValidityTimestamp, sanitizedEndValidityTimestamp, hostIndex); LOG(debug3) << "Full URL Encoded: " << fullUrl; /* what URL that receives this POST */ curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); @@ -334,7 +449,11 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin res = CURL_perform(curl); /* Check for errors */ if (res != CURLE_OK) { - LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(res)); + if (res == CURLE_OPERATION_TIMEDOUT) { + LOGP(alarm, "curl_easy_perform() timed out. Consider increasing the timeout using the env var `ALICEO2_CCDB_CURL_TIMEOUT_UPLOAD` (seconds), current one is {}", mCurlTimeoutUpload); + } else { // generic message + LOGP(alarm, "curl_easy_perform() failed: {}", curl_easy_strerror(res)); + } returnValue = res; } } @@ -342,10 +461,10 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin /* always cleanup */ curl_easy_cleanup(curl); - /* then cleanup the formpost chain */ - curl_formfree(formpost); /* free slist */ curl_slist_free_all(headerlist); + /* free mime */ + curl_mime_free(mime); } else { LOGP(alarm, "curl initialization failure"); returnValue = -2; @@ -357,35 +476,39 @@ int CcdbApi::storeAsTFile(const TObject* rootObject, std::string const& path, st long startValidityTimestamp, long endValidityTimestamp, std::vector::size_type maxSize) const { // Prepare file + if (!rootObject) { + LOGP(error, "nullptr is provided for object {}/{}/{}", path, startValidityTimestamp, endValidityTimestamp); + return -1; + } CcdbObjectInfo info; auto img = createObjectImage(rootObject, &info); return storeAsBinaryFile(img->data(), img->size(), info.getFileName(), info.getObjectType(), path, metadata, startValidityTimestamp, endValidityTimestamp, maxSize); } -string CcdbApi::getFullUrlForStorage(CURL* curl, const string& path, const string& objtype, - const map& metadata, - long startValidityTimestamp, long endValidityTimestamp, int hostIndex) const +std::string CcdbApi::getFullUrlForStorage(CURL* curl, const std::string& path, const std::string& objtype, + const std::map& metadata, + long startValidityTimestamp, long endValidityTimestamp, int hostIndex) const { // Prepare timestamps - string startValidityString = getTimestampString(startValidityTimestamp < 0 ? getCurrentTimestamp() : startValidityTimestamp); - string endValidityString = getTimestampString(endValidityTimestamp < 0 ? getFutureTimestamp(60 * 60 * 24 * 1) : endValidityTimestamp); + std::string startValidityString = getTimestampString(startValidityTimestamp < 0 ? getCurrentTimestamp() : startValidityTimestamp); + std::string endValidityString = getTimestampString(endValidityTimestamp < 0 ? getFutureTimestamp(60 * 60 * 24 * 1) : endValidityTimestamp); // Get url - string url = getHostUrl(hostIndex); + std::string url = getHostUrl(hostIndex); // Build URL - string fullUrl = url + "/" + path + "/" + startValidityString + "/" + endValidityString + "/"; + std::string fullUrl = url + "/" + path + "/" + startValidityString + "/" + endValidityString + "/"; // Add type as part of metadata // we need to URL encode the object type, since in case it has special characters (like the "<", ">" for templated classes) it won't work otherwise char* objtypeEncoded = curl_easy_escape(curl, objtype.c_str(), objtype.size()); - fullUrl += "ObjectType=" + string(objtypeEncoded) + "/"; + fullUrl += "ObjectType=" + std::string(objtypeEncoded) + "/"; curl_free(objtypeEncoded); // Add general metadata for (auto& kv : metadata) { - string mfirst = kv.first; - string msecond = kv.second; + std::string mfirst = kv.first; + std::string msecond = kv.second; // same trick for the metadata as for the object type char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size()); char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size()); - fullUrl += string(mfirstEncoded) + "=" + string(msecondEncoded) + "/"; + fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/"; curl_free(mfirstEncoded); curl_free(msecondEncoded); } @@ -393,26 +516,26 @@ string CcdbApi::getFullUrlForStorage(CURL* curl, const string& path, const strin } // todo make a single method of the one above and below -string CcdbApi::getFullUrlForRetrieval(CURL* curl, const string& path, const map& metadata, long timestamp, int hostIndex) const +std::string CcdbApi::getFullUrlForRetrieval(CURL* curl, const std::string& path, const std::map& metadata, long timestamp, int hostIndex) const { if (mInSnapshotMode) { return getSnapshotFile(mSnapshotTopPath, path); } // Prepare timestamps - string validityString = getTimestampString(timestamp < 0 ? getCurrentTimestamp() : timestamp); + std::string validityString = getTimestampString(timestamp < 0 ? getCurrentTimestamp() : timestamp); // Get host url - string hostUrl = getHostUrl(hostIndex); + std::string hostUrl = getHostUrl(hostIndex); // Build URL - string fullUrl = hostUrl + "/" + path + "/" + validityString + "/"; + std::string fullUrl = hostUrl + "/" + path + "/" + validityString + "/"; // Add metadata for (auto& kv : metadata) { - string mfirst = kv.first; - string msecond = kv.second; + std::string mfirst = kv.first; + std::string msecond = kv.second; // trick for the metadata in case it contains special characters char* mfirstEncoded = curl_easy_escape(curl, mfirst.c_str(), mfirst.size()); char* msecondEncoded = curl_easy_escape(curl, msecond.c_str(), msecond.size()); - fullUrl += string(mfirstEncoded) + "=" + string(msecondEncoded) + "/"; + fullUrl += std::string(mfirstEncoded) + "=" + std::string(msecondEncoded) + "/"; curl_free(mfirstEncoded); curl_free(msecondEncoded); } @@ -420,8 +543,8 @@ string CcdbApi::getFullUrlForRetrieval(CURL* curl, const string& path, const map } /** - * Struct to store the data we will receive from the CCDB with CURL. - */ + * Struct to store the data we will receive from the CCDB with CURL. + */ struct MemoryStruct { char* memory; unsigned int size; @@ -541,7 +664,38 @@ size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userd if (index != std::string::npos) { const auto key = boost::algorithm::trim_copy(header.substr(0, index)); const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); - headers->insert(std::make_pair(key, value)); + LOGP(debug, "Adding #{} {} -> {}", headers->size(), key, value); + bool insert = true; + if (key == "Content-Length") { + auto cl = headers->find("Content-Length"); + if (cl != headers->end()) { + if (std::stol(cl->second) < stol(value)) { + headers->erase(key); + } else { + insert = false; + } + } + } + + // Keep only the first ETag encountered + if (key == "ETag") { + auto cl = headers->find("ETag"); + if (cl != headers->end()) { + insert = false; + } + } + + // Keep only the first Content-Type encountered + if (key == "Content-Type") { + auto cl = headers->find("Content-Type"); + if (cl != headers->end()) { + insert = false; + } + } + + if (insert) { + headers->insert(std::make_pair(key, value)); + } } return size * nitems; } @@ -597,6 +751,7 @@ bool CcdbApi::receiveObject(void* dataHolder, std::string const& path, std::map< CURL* curlHandle; curlHandle = curl_easy_init(); + curl_easy_setopt(curlHandle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curlHandle != nullptr) { @@ -609,7 +764,7 @@ bool CcdbApi::receiveObject(void* dataHolder, std::string const& path, std::map< CURLcode curlResultCode = CURL_LAST; for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (responseCode >= 400 || curlResultCode > 0); hostIndex++) { - string fullUrl = getFullUrlForRetrieval(curlHandle, path, metadata, timestamp, hostIndex); + std::string fullUrl = getFullUrlForRetrieval(curlHandle, path, metadata, timestamp, hostIndex); curl_easy_setopt(curlHandle, CURLOPT_URL, fullUrl.c_str()); curlResultCode = CURL_perform(curlHandle); @@ -684,7 +839,7 @@ TObject* CcdbApi::retrieveFromTFile(std::string const& path, std::map const& metadata, - long timestamp, bool preservePath, std::string const& localFileName, std::string const& createdNotAfter, std::string const& createdNotBefore) const + long timestamp, bool preservePath, std::string const& localFileName, std::string const& createdNotAfter, std::string const& createdNotBefore, std::map* outHeaders) const { // we setup the target path for this blob @@ -693,7 +848,7 @@ bool CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir try { o2::utils::createDirectoriesIfAbsent(fulltargetdir); } catch (std::exception e) { - LOGP(error, fmt::format("Could not create local snapshot cache directory {}, reason: {}", fulltargetdir, e.what())); + LOGP(error, "Could not create local snapshot cache directory {}, reason: {}", fulltargetdir, e.what()); return false; } @@ -732,6 +887,9 @@ bool CcdbApi::retrieveBlob(std::string const& path, std::string const& targetdir CCDBQuery querysummary(path, metadata, timestamp); updateMetaInformationInLocalFile(targetpath.c_str(), &headers, &querysummary); + if (outHeaders) { + *outHeaders = std::move(headers); + } return true; } @@ -739,7 +897,7 @@ void CcdbApi::snapshot(std::string const& ccdbrootpath, std::string const& local { // query all subpaths to ccdbrootpath const auto allfolders = getAllFolders(ccdbrootpath); - std::map metadata; + std::map metadata; for (auto& folder : allfolders) { retrieveBlob(folder, localDir, metadata, timestamp); } @@ -803,20 +961,23 @@ void* CcdbApi::extractFromLocalFile(std::string const& filename, std::type_info if ((isSnapshotMode() || mPreferSnapshotCache) && headers->find("ETag") == headers->end()) { // generate dummy ETag to profit from the caching (*headers)["ETag"] = filename; } + if (headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", f.GetEND()); + } } return extractFromTFile(f, tcl); } bool CcdbApi::initTGrid() const { - if (mNeedAlienToken && !mAlienInstance) { + if (mNeedAlienToken && !gGrid) { static bool allowNoToken = getenv("ALICEO2_CCDB_NOTOKENCHECK") && atoi(getenv("ALICEO2_CCDB_NOTOKENCHECK")); if (!allowNoToken && !checkAlienToken()) { LOG(fatal) << "Alien Token Check failed - Please get an alien token before running with https CCDB endpoint, or alice-ccdb.cern.ch!"; } - mAlienInstance = TGrid::Connect("alien"); + TGrid::Connect("alien"); static bool errorShown = false; - if (!mAlienInstance && errorShown == false) { + if (!gGrid && errorShown == false) { if (allowNoToken) { LOG(error) << "TGrid::Connect returned nullptr. May be due to missing alien token"; } else { @@ -825,12 +986,12 @@ bool CcdbApi::initTGrid() const errorShown = true; } } - return mAlienInstance != nullptr; + return gGrid != nullptr; } -void* CcdbApi::downloadAlienContent(std::string const& url, std::type_info const& tinfo) const +void* CcdbApi::downloadFilesystemContent(std::string const& url, std::type_info const& tinfo, std::map* headers) const { - if (!initTGrid()) { + if ((url.find("alien:/", 0) != std::string::npos) && !initTGrid()) { return nullptr; } std::lock_guard guard(gIOMutex); @@ -838,6 +999,9 @@ void* CcdbApi::downloadAlienContent(std::string const& url, std::type_info const if (memfile) { auto cl = tinfo2TClass(tinfo); auto content = extractFromTFile(*memfile, cl); + if (headers && headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", memfile->GetEND()); + } delete memfile; return content; } @@ -864,7 +1028,7 @@ void* CcdbApi::interpretAsTMemFileAndExtract(char* contentptr, size_t contentsiz } // navigate sequence of URLs until TFile content is found; object is extracted and returned -void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map* headers) const +void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string const& url, std::type_info const& tinfo, std::map* headers) const { // a global internal data structure that can be filled with HTTP header information // static --> to avoid frequent alloc/dealloc as optimization @@ -872,8 +1036,8 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con static thread_local std::multimap headerData; // let's see first of all if the url is something specific that curl cannot handle - if (url.find("alien:/", 0) != std::string::npos) { - return downloadAlienContent(url, tinfo); + if ((url.find("alien:/", 0) != std::string::npos) || (url.find("file:/", 0) != std::string::npos)) { + return downloadFilesystemContent(url, tinfo, headers); } // add other final cases here // example root:// @@ -904,6 +1068,9 @@ void* CcdbApi::navigateURLsAndRetrieveContent(CURL* curl_handle, std::string con if (200 <= response_code && response_code < 300) { // good response and the content is directly provided and should have been dumped into "chunk" content = interpretAsTMemFileAndExtract(chunk.memory, chunk.size, tinfo); + if (headers && headers->find("fileSize") == headers->end()) { + (*headers)["fileSize"] = fmt::format("{}", chunk.size); + } } else if (response_code == 304) { // this means the object exist but I am not serving // it since it's already in your possession @@ -979,18 +1146,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& { if (!mSnapshotCachePath.empty()) { // protect this sensitive section by a multi-process named semaphore - boost::interprocess::named_semaphore* sem = nullptr; - std::hash hasher; - const auto semhashedstring = "aliceccdb" + std::to_string(hasher(mSnapshotCachePath + path)).substr(0, 16); - try { - sem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); - } catch (std::exception e) { - LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; - sem = nullptr; - } - if (sem) { - sem->wait(); // wait until we can enter (no one else there) - } + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, path); std::string logfile = mSnapshotCachePath + "/log"; std::fstream out(logfile, ios_base::out | ios_base::app); if (out.is_open()) { @@ -1008,14 +1164,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& } else { out << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n"; } - if (sem) { - sem->post(); - if (sem->try_wait()) { - // if nobody else is waiting remove the semaphore resource - sem->post(); - boost::interprocess::named_semaphore::remove(semhashedstring.c_str()); - } - } + auto res = extractFromLocalFile(snapshotfile, tinfo, headers); if (!snapshoting) { // if snapshot was created at this call, the log was already done logReading(path, timestamp, headers, "retrieve from snapshot"); @@ -1026,11 +1175,15 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& // normal mode follows CURL* curl_handle = curl_easy_init(); - string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + std::string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode // if we are in snapshot mode we can simply open the file; extract the object and return if (mInSnapshotMode) { - return extractFromLocalFile(fullUrl, tinfo, headers); - logReading(path, timestamp, headers, "retrieve from snapshot"); + auto res = extractFromLocalFile(fullUrl, tinfo, headers); + if (res) { + logReading(path, timestamp, headers, "retrieve from snapshot"); + } + return res; } curl_slist* option_list = nullptr; @@ -1064,7 +1217,7 @@ size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nme return size * nmemb; } -std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string const& returnFormat) const +std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string const& returnFormat, long createdNotAfter, long createdNotBefore) const { CURL* curl; CURLcode res = CURL_LAST; @@ -1074,15 +1227,22 @@ std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString2); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); struct curl_slist* headers = nullptr; - headers = curl_slist_append(headers, (string("Accept: ") + returnFormat).c_str()); - headers = curl_slist_append(headers, (string("Content-Type: ") + returnFormat).c_str()); + headers = curl_slist_append(headers, (std::string("Accept: ") + returnFormat).c_str()); + headers = curl_slist_append(headers, (std::string("Content-Type: ") + returnFormat).c_str()); + if (createdNotAfter >= 0) { + headers = curl_slist_append(headers, ("If-Not-After: " + std::to_string(createdNotAfter)).c_str()); + } + if (createdNotBefore >= 0) { + headers = curl_slist_append(headers, ("If-Not-Before: " + std::to_string(createdNotBefore)).c_str()); + } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curlSetSSLOptions(curl); - string fullUrl; + std::string fullUrl; // Perform the request, res will get the return code for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) { fullUrl = getHostUrl(hostIndex); @@ -1119,6 +1279,7 @@ void CcdbApi::deleteObject(std::string const& path, long timestamp) const curl = curl_easy_init(); if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); curlSetSSLOptions(curl); for (size_t hostIndex = 0; hostIndex < hostsPool.size(); hostIndex++) { @@ -1141,10 +1302,11 @@ void CcdbApi::truncate(std::string const& path) const CURLcode res; stringstream fullUrl; for (size_t i = 0; i < hostsPool.size(); i++) { - string url = getHostUrl(i); + std::string url = getHostUrl(i); fullUrl << url << "/truncate/" << path; curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl != nullptr) { curl_easy_setopt(curl, CURLOPT_URL, fullUrl.str().c_str()); @@ -1172,6 +1334,7 @@ bool CcdbApi::isHostReachable() const bool result = false; curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl) { for (size_t hostIndex = 0; hostIndex < hostsPool.size() && res != CURLE_OK; hostIndex++) { curl_easy_setopt(curl, CURLOPT_URL, mUrl.data()); @@ -1223,53 +1386,140 @@ size_t header_callback(char* buffer, size_t size, size_t nitems, void* userdata) } } // namespace -std::map CcdbApi::retrieveHeaders(std::string const& path, std::map const& metadata, long timestamp) const +bool stdmap_to_jsonfile(std::map const& meta, std::string const& filename) { - CURL* curl = curl_easy_init(); - CURLcode res = CURL_LAST; - string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); - std::map headers; - if (curl != nullptr) { - struct curl_slist* list = nullptr; - list = curl_slist_append(list, ("If-None-Match: " + std::to_string(timestamp)).c_str()); + // create directory structure if necessary + auto p = std::filesystem::path(filename).parent_path(); + if (!std::filesystem::exists(p)) { + std::filesystem::create_directories(p); + } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + writer.StartObject(); + for (const auto& pair : meta) { + writer.Key(pair.first.c_str()); + writer.String(pair.second.c_str()); + } + writer.EndObject(); - /* get us the resource without a body! */ - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_map_callback<>); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); - curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + // Write JSON to file + std::ofstream file(filename); + if (file.is_open()) { + file << buffer.GetString(); + file.close(); + } else { + return false; + } + return true; +} - curlSetSSLOptions(curl); +bool jsonfile_to_stdmap(std::map& meta, std::string const& filename) +{ + // Read JSON from file + std::ifstream file(filename); + if (!file.is_open()) { + std::cerr << "Failed to open file for reading." << std::endl; + return false; + } - // Perform the request, res will get the return code + std::string jsonStr((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - long httpCode = 404; - CURLcode getCodeRes = CURL_LAST; - for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (httpCode >= 400 || res > 0 || getCodeRes > 0); hostIndex++) { - curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); - res = CURL_perform(curl); - if (res != CURLE_OK && res != CURLE_UNSUPPORTED_PROTOCOL) { - // We take out the unsupported protocol error because we are only querying - // header info which is returned in any case. Unsupported protocol error - // occurs sometimes because of redirection to alien for blobs. - LOG(error) << "CURL_perform() failed: " << curl_easy_strerror(res); - } + // Parse JSON + rapidjson::Document document; + document.Parse(jsonStr.c_str()); + + if (document.HasParseError()) { + std::cerr << "Error parsing JSON" << std::endl; + return false; + } + + // Convert JSON to std::map + for (auto itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr) { + meta[itr->name.GetString()] = itr->value.GetString(); + } + return true; +} + +std::map CcdbApi::retrieveHeaders(std::string const& path, std::map const& metadata, long timestamp) const +{ + // lambda that actually does the call to the CCDB server + auto do_remote_header_call = [this, &path, &metadata, timestamp]() -> std::map { + CURL* curl = curl_easy_init(); + CURLcode res = CURL_LAST; + std::string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); + std::map headers; + + if (curl != nullptr) { + struct curl_slist* list = nullptr; + list = curl_slist_append(list, ("If-None-Match: " + std::to_string(timestamp)).c_str()); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - getCodeRes = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); + /* get us the resource without a body! */ + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_map_callback<>); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &headers); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + + curlSetSSLOptions(curl); + + // Perform the request, res will get the return code + long httpCode = 404; + CURLcode getCodeRes = CURL_LAST; + for (size_t hostIndex = 0; hostIndex < hostsPool.size() && (httpCode >= 400 || res > 0 || getCodeRes > 0); hostIndex++) { + curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); + res = CURL_perform(curl); + if (res != CURLE_OK && res != CURLE_UNSUPPORTED_PROTOCOL) { + // We take out the unsupported protocol error because we are only querying + // header info which is returned in any case. Unsupported protocol error + // occurs sometimes because of redirection to alien for blobs. + LOG(error) << "CURL_perform() failed: " << curl_easy_strerror(res); + } + getCodeRes = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode); + } + if (httpCode == 404) { + headers.clear(); + } + curl_easy_cleanup(curl); } + return headers; + }; + + if (!mSnapshotCachePath.empty()) { + // protect this sensitive section by a multi-process named semaphore + auto semaphore_barrier = std::make_unique(mSnapshotCachePath + std::string("_headers"), path); - if (httpCode == 404) { - headers.clear(); + std::string logfile = mSnapshotCachePath + "/log"; + std::fstream out(logfile, ios_base::out | ios_base::app); + if (out.is_open()) { + out << "CCDB-header-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << "\n"; } + auto snapshotfile = getSnapshotFile(mSnapshotCachePath, path + "/" + std::to_string(timestamp), "header.json"); + if (!std::filesystem::exists(snapshotfile)) { + out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << " storing to snapshot " << snapshotfile << "\n"; - curl_easy_cleanup(curl); - } + // if file not already here and valid --> snapshot it + auto meta = do_remote_header_call(); - return headers; + // cache the result + if (!stdmap_to_jsonfile(meta, snapshotfile)) { + LOG(warn) << "Failed to cache the header information to disc"; + } + return meta; + } else { + out << "CCDB-header-access[" << getpid() << "] ... " << mUniqueAgentID << "serving from local snapshot " << snapshotfile << "\n"; + std::map meta; + if (!jsonfile_to_stdmap(meta, snapshotfile)) { + LOG(warn) << "Failed to read cached information from disc"; + return do_remote_header_call(); + } + return meta; + } + } + return do_remote_header_call(); } bool CcdbApi::getCCDBEntryHeaders(std::string const& url, std::string const& etag, std::vector& headers, const std::string& agentID) @@ -1375,9 +1625,11 @@ TClass* CcdbApi::tinfo2TClass(std::type_info const& tinfo) return cl; } -void CcdbApi::updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id, long newEOV) +int CcdbApi::updateMetadata(std::string const& path, std::map const& metadata, long timestamp, std::string const& id, long newEOV) { + int ret = -1; CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); if (curl != nullptr) { CURLcode res; stringstream fullUrl; @@ -1392,12 +1644,12 @@ void CcdbApi::updateMetadata(std::string const& path, std::map CcdbApi::splitString(const std::string& str, const char* delimiters) -{ - std::vector tokens; - char stringForStrTok[str.length() + 1]; - strcpy(stringForStrTok, str.c_str()); - char* token = strtok(stringForStrTok, delimiters); - while (token != nullptr) { - tokens.emplace_back(token); - token = strtok(nullptr, delimiters); - } - return tokens; + return ret; } void CcdbApi::initHostsPool(std::string hosts) { - hostsPool = splitString(hosts, ",;"); + hostsPool.clear(); + auto splitted = hosts | std::views::transform([](char c) { return (c == ';') ? ',' : c; }) | std::views::split(','); + for (auto&& part : splitted) { + hostsPool.emplace_back(part.begin(), part.end()); + } } std::string CcdbApi::getHostUrl(int hostIndex) const @@ -1444,215 +1691,332 @@ std::string CcdbApi::getHostUrl(int hostIndex) const return hostsPool.at(hostIndex); } -void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& path, - std::map const& metadata, long timestamp, - std::map* headers, std::string const& etag, - const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const +void CcdbApi::scheduleDownload(RequestContext& requestContext, size_t* requestCounter) const { - LOGP(debug, "loadFileToMemory {} ETag=[{}]", path, etag); - - // if we are in snapshot mode we can simply open the file, unless the etag is non-empty: - // this would mean that the object was is already fetched and in this mode we don't to validity checks! - bool createSnapshot = considerSnapshot && !mSnapshotCachePath.empty(); // create snaphot if absent - int fromSnapshot = 0; - boost::interprocess::named_semaphore* sem = nullptr; - std::string semhashedstring{}, snapshotpath{}, logfile{}; - std::unique_ptr logStream; - auto sem_release = [&sem, &semhashedstring, path, this]() { - if (sem) { - sem->post(); - if (sem->try_wait()) { // if nobody else is waiting remove the semaphore resource - sem->post(); - boost::interprocess::named_semaphore::remove(semhashedstring.c_str()); - } - } + auto data = new DownloaderRequestData(); // Deleted in transferFinished of CCDBDownloader.cxx + data->hoPair.object = &requestContext.dest; + + std::function localContentCallback = [this, &requestContext](std::string url) { + return this->loadLocalContentToMemory(requestContext.dest, url); }; - if (createSnapshot) { // create named semaphore - std::hash hasher; - semhashedstring = "aliceccdb" + std::to_string(hasher(mSnapshotCachePath + path)).substr(0, 16); + auto writeCallback = [](void* contents, size_t size, size_t nmemb, void* chunkptr) { + auto& ho = *static_cast(chunkptr); + auto& chunk = *ho.object; + size_t realsize = size * nmemb, sz = 0; + ho.counter++; try { - sem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); + if (chunk.capacity() < chunk.size() + realsize) { + // estimate headers size when converted to annotated text string + const char hannot[] = "header"; + size_t hsize = getFlatHeaderSize(ho.header); + auto cl = ho.header.find("Content-Length"); + if (cl != ho.header.end()) { + size_t sizeFromHeader = std::stol(cl->second); + sz = hsize + std::max(chunk.size() * (sizeFromHeader ? 1 : 2) + realsize, sizeFromHeader); + } else { + sz = hsize + std::max(chunk.size() * 2, chunk.size() + realsize); + // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz); + } + chunk.reserve(sz); + } + char* contC = (char*)contents; + chunk.insert(chunk.end(), contC, contC + realsize); } catch (std::exception e) { - LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; - sem = nullptr; + // LOGP(alarm, "failed to reserve {} bytes in CURL write callback (realsize = {}): {}", sz, realsize, e.what()); + realsize = 0; } - if (sem) { - sem->wait(); // wait until we can enter (no one else there) + return realsize; + }; + + CURL* curl_handle = curl_easy_init(); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); + std::string fullUrl = getFullUrlForRetrieval(curl_handle, requestContext.path, requestContext.metadata, requestContext.timestamp); + curl_slist* options_list = nullptr; + initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, requestContext.timestamp, &requestContext.headers, + requestContext.etag, requestContext.createdNotAfter, requestContext.createdNotBefore); + + data->headers = &requestContext.headers; + data->hosts = hostsPool; + data->path = requestContext.path; + data->timestamp = requestContext.timestamp; + data->localContentCallback = localContentCallback; + data->userAgent = mUniqueAgentID; + data->optionsList = options_list; + + curl_easy_setopt(curl_handle, CURLOPT_URL, fullUrl.c_str()); + initCurlOptionsForRetrieve(curl_handle, (void*)(&data->hoPair), writeCallback, false); + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callbackhoPair.header)>); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&(data->hoPair.header)); + curl_easy_setopt(curl_handle, CURLOPT_PRIVATE, (void*)data); + curlSetSSLOptions(curl_handle); + + asynchPerform(curl_handle, requestCounter); +} + +std::string CcdbApi::determineSemaphoreName(std::string const& basedir, std::string const& ccdbpath) +{ + std::hash hasher; + std::string semhashedstring = "aliceccdb" + std::to_string(hasher(basedir + ccdbpath)).substr(0, 16); + return semhashedstring; +} + +boost::interprocess::named_semaphore* CcdbApi::createNamedSemaphore(std::string const& path) const +{ + std::string semhashedstring = determineSemaphoreName(mSnapshotCachePath, path); + // LOG(info) << "Creating named semaphore with name " << semhashedstring.c_str(); + try { + return new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, semhashedstring.c_str(), 1); + } catch (std::exception e) { + LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; + return nullptr; + } +} + +void CcdbApi::releaseNamedSemaphore(boost::interprocess::named_semaphore* sem, std::string const& path) const +{ + if (sem) { + sem->post(); + if (sem->try_wait()) { // if nobody else is waiting remove the semaphore resource + sem->post(); + boost::interprocess::named_semaphore::remove(determineSemaphoreName(mSnapshotCachePath, path).c_str()); } - logfile = mSnapshotCachePath + "/log"; - logStream = std::make_unique(logfile, ios_base::out | ios_base::app); - if (logStream->is_open()) { - *logStream.get() << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << " for load to memory\n"; + } +} + +bool CcdbApi::removeSemaphore(std::string const& semaname, bool remove) +{ + // removes a given named semaphore from the system + try { + boost::interprocess::named_semaphore semaphore(boost::interprocess::open_only, semaname.c_str()); + std::cout << "Found CCDB semaphore: " << semaname << "\n"; + if (remove) { + auto success = boost::interprocess::named_semaphore::remove(semaname.c_str()); + if (success) { + std::cout << "Removed CCDB semaphore: " << semaname << "\n"; + } + return success; } + return true; + } catch (std::exception const& e) { + // no EXISTING under this name semaphore found + // nothing to be done } + return false; +} - if (mInSnapshotMode) { // file must be there, otherwise a fatal will be produced - loadFileToMemory(dest, getSnapshotFile(mSnapshotTopPath, path), headers); +// helper function checking for leaking semaphores associated to CCDB cache files and removing them +// walks a local CCDB snapshot tree and checks +void CcdbApi::removeLeakingSemaphores(std::string const& snapshotdir, bool remove) +{ + namespace fs = std::filesystem; + std::string fileName{"snapshot.root"}; + try { + auto absolutesnapshotdir = fs::weakly_canonical(fs::absolute(snapshotdir)); + for (const auto& entry : fs::recursive_directory_iterator(absolutesnapshotdir)) { + if (entry.is_directory()) { + const fs::path& currentDir = fs::canonical(fs::absolute(entry.path())); + fs::path filePath = currentDir / fileName; + if (fs::exists(filePath) && fs::is_regular_file(filePath)) { + std::cout << "Directory with file '" << fileName << "': " << currentDir << std::endl; + + // we need to obtain the path relative to snapshotdir + auto pathtokens = o2::utils::Str::tokenize(currentDir, '/', true); + auto numtokens = pathtokens.size(); + if (numtokens < 3) { + // cannot be a CCDB path + continue; + } + // path are last 3 entries + std::string path = pathtokens[numtokens - 3] + "/" + pathtokens[numtokens - 2] + "/" + pathtokens[numtokens - 1]; + auto semaname = o2::ccdb::CcdbApi::determineSemaphoreName(absolutesnapshotdir, path); + removeSemaphore(semaname, remove); + } + } + } + } catch (std::exception const& e) { + LOG(info) << "Semaphore search had exception " << e.what(); + } +} + +void CcdbApi::getFromSnapshot(bool createSnapshot, std::string const& path, + long timestamp, std::map& headers, + std::string& snapshotpath, o2::pmr::vector& dest, int& fromSnapshot, std::string const& etag) const +{ + if (createSnapshot) { // create named semaphore + std::string logfile = mSnapshotCachePath + "/log"; + std::fstream logStream = std::fstream(logfile, ios_base::out | ios_base::app); + if (logStream.is_open()) { + logStream << "CCDB-access[" << getpid() << "] of " << mUniqueAgentID << " to " << path << " timestamp " << timestamp << " for load to memory\n"; + } + } + if (mInSnapshotMode) { // file must be there, otherwise a fatal will be produced; + if (etag.empty()) { + loadFileToMemory(dest, getSnapshotFile(mSnapshotTopPath, path), &headers); + } fromSnapshot = 1; - } else if (mPreferSnapshotCache && std::filesystem::exists(snapshotpath = getSnapshotFile(mSnapshotCachePath, path))) { + } else if (mPreferSnapshotCache && std::filesystem::exists(snapshotpath)) { // if file is available, use it, otherwise cache it below from the server. Do this only when etag is empty since otherwise the object was already fetched and cached if (etag.empty()) { - loadFileToMemory(dest, snapshotpath, headers); + loadFileToMemory(dest, snapshotpath, &headers); } fromSnapshot = 2; - } else { // look on the server - CURL* curl_handle = curl_easy_init(); - string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); - - curl_slist* options_list = nullptr; - initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, timestamp, headers, etag, createdNotAfter, createdNotBefore); - - navigateURLsAndLoadFileToMemory(dest, curl_handle, fullUrl, headers); - - for (size_t hostIndex = 1; hostIndex < hostsPool.size() && isMemoryFileInvalid(dest); hostIndex++) { - fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp, hostIndex); - loadFileToMemory(dest, fullUrl, headers); // headers loaded from the file in case of the snapshot reading only - } - curl_slist_free_all(options_list); - curl_easy_cleanup(curl_handle); } +} - if (dest.empty()) { - sem_release(); - return; // nothing was fetched: either cached value is good or error was produced - } - // !considerSnapshot means that the call was made by retrieve for snapshoting reasons - logReading(path, timestamp, headers, fmt::format("{}{}", considerSnapshot ? "load to memory" : "retrieve", fromSnapshot ? " from snapshot" : "")); +void CcdbApi::saveSnapshot(RequestContext& requestContext) const +{ + // Consider saving snapshot + if (!mSnapshotCachePath.empty() && !(mInSnapshotMode && mSnapshotTopPath == mSnapshotCachePath)) { // store in the snapshot only if the object was not read from the snapshot + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, requestContext.path); - // are we asked to create a snapshot ? - if (createSnapshot && fromSnapshot != 2 && !(mInSnapshotMode && mSnapshotTopPath == mSnapshotCachePath)) { // store in the snapshot only if the object was not read from the snapshot - auto snapshotdir = getSnapshotDir(mSnapshotCachePath, path); - snapshotpath = getSnapshotFile(mSnapshotCachePath, path); + auto snapshotdir = getSnapshotDir(mSnapshotCachePath, requestContext.path); + std::string snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path); o2::utils::createDirectoriesIfAbsent(snapshotdir); - if (logStream->is_open()) { - *logStream.get() << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotpath << " from memory\n"; + std::fstream logStream; + if (logStream.is_open()) { + logStream << "CCDB-access[" << getpid() << "] ... " << mUniqueAgentID << " downloading to snapshot " << snapshotpath << " from memory\n"; } { // dump image to a file - LOGP(debug, "creating snapshot {} -> {}", path, snapshotpath); - CCDBQuery querysummary(path, metadata, timestamp); + LOGP(debug, "creating snapshot {} -> {}", requestContext.path, snapshotpath); + CCDBQuery querysummary(requestContext.path, requestContext.metadata, requestContext.timestamp); { std::ofstream objFile(snapshotpath, std::ios::out | std::ofstream::binary); - std::copy(dest.begin(), dest.end(), std::ostreambuf_iterator(objFile)); + std::copy(requestContext.dest.begin(), requestContext.dest.end(), std::ostreambuf_iterator(objFile)); } // now open the same file as root file and store metadata - updateMetaInformationInLocalFile(snapshotpath, headers, &querysummary); + updateMetaInformationInLocalFile(snapshotpath, &requestContext.headers, &querysummary); } } - sem_release(); } -// navigate sequence of URLs until TFile content is found; object is extracted and returned -void CcdbApi::navigateURLsAndLoadFileToMemory(o2::pmr::vector& dest, CURL* curl_handle, std::string const& url, std::map* headers) const +void CcdbApi::loadFileToMemory(std::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const { - // a global internal data structure that can be filled with HTTP header information - // static --> to avoid frequent alloc/dealloc as optimization - // not sure if thread_local takes away that benefit - static thread_local std::multimap headerData; - - // let's see first of all if the url is something specific that curl cannot handle - if (url.find("alien:/", 0) != std::string::npos) { - return loadFileToMemory(dest, url, nullptr); // headers loaded from the file in case of the snapshot reading only + o2::pmr::vector destP; + destP.reserve(dest.size()); + loadFileToMemory(destP, path, metadata, timestamp, headers, etag, createdNotAfter, createdNotBefore, considerSnapshot); + dest.clear(); + dest.reserve(destP.size()); + for (const auto c : destP) { + dest.push_back(c); } - // otherwise make an HTTP/CURL request - bool errorflag = false; - auto signalError = [&chunk = dest, &errorflag]() { - chunk.clear(); - chunk.reserve(1); - errorflag = true; - }; - auto writeCallBack = [](void* contents, size_t size, size_t nmemb, void* chunkptr) { - o2::pmr::vector& chunk = *static_cast*>(chunkptr); - size_t realsize = size * nmemb; - try { - chunk.reserve(chunk.size() + realsize); - char* contC = (char*)contents; - chunk.insert(chunk.end(), contC, contC + realsize); - } catch (std::exception e) { - LOGP(info, "failed to expand by {} bytes chunk provided to CURL: {}", realsize, e.what()); - realsize = 0; +} + +void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& path, + std::map const& metadata, long timestamp, + std::map* headers, std::string const& etag, + const std::string& createdNotAfter, const std::string& createdNotBefore, bool considerSnapshot) const +{ + RequestContext requestContext(dest, metadata, *headers); + requestContext.path = path; + // std::map metadataCopy = metadata; // Create a copy because metadata will be passed as a pointer so it cannot be constant. The const in definition is for backwards compatability. + // requestContext.metadata = metadataCopy; + requestContext.timestamp = timestamp; + requestContext.etag = etag; + requestContext.createdNotAfter = createdNotAfter; + requestContext.createdNotBefore = createdNotBefore; + requestContext.considerSnapshot = considerSnapshot; + std::vector contexts = {requestContext}; + vectoredLoadFileToMemory(contexts); +} + +void CcdbApi::appendFlatHeader(o2::pmr::vector& dest, const std::map& headers) +{ + size_t hsize = getFlatHeaderSize(headers), cnt = dest.size(); + dest.resize(cnt + hsize); + auto addString = [&dest, &cnt](const std::string& s) { + for (char c : s) { + dest[cnt++] = c; } - return realsize; + dest[cnt++] = 0; }; - // specify URL to get - curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); - initCurlOptionsForRetrieve(curl_handle, (void*)&dest, writeCallBack, false); - curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callback); - headerData.clear(); - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&headerData); - curlSetSSLOptions(curl_handle); + for (auto& h : headers) { + addString(h.first); + addString(h.second); + } + *reinterpret_cast(&dest[cnt]) = hsize; // store size + std::memcpy(&dest[cnt + sizeof(int)], FlatHeaderAnnot, sizeof(FlatHeaderAnnot)); // annotate the flattened headers map +} - auto res = CURL_perform(curl_handle); - long response_code = -1; - if (res == CURLE_OK && curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) { - if (headers) { - for (auto& p : headerData) { - (*headers)[p.first] = p.second; +void CcdbApi::navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const +{ + LOGP(debug, "loadFileToMemory {} ETag=[{}]", requestContext.path, requestContext.etag); + bool createSnapshot = requestContext.considerSnapshot && !mSnapshotCachePath.empty(); // create snaphot if absent + + std::string snapshotpath; + if (mInSnapshotMode || std::filesystem::exists(snapshotpath = getSnapshotFile(mSnapshotCachePath, requestContext.path))) { + auto semaphore_barrier = std::make_unique(mSnapshotCachePath, requestContext.path); + // if we are in snapshot mode we can simply open the file, unless the etag is non-empty: + // this would mean that the object was is already fetched and in this mode we don't to validity checks! + getFromSnapshot(createSnapshot, requestContext.path, requestContext.timestamp, requestContext.headers, snapshotpath, requestContext.dest, fromSnapshot, requestContext.etag); + } else { // look on the server + scheduleDownload(requestContext, requestCounter); + } +} + +void CcdbApi::vectoredLoadFileToMemory(std::vector& requestContexts) const +{ + std::vector fromSnapshots(requestContexts.size()); + size_t requestCounter = 0; + + // Get files from snapshots and schedule downloads + for (int i = 0; i < requestContexts.size(); i++) { + // navigateSourcesAndLoadFile either retrieves file from snapshot immediately, or schedules it to be downloaded when mDownloader->runLoop is ran at a later time + auto& requestContext = requestContexts.at(i); + navigateSourcesAndLoadFile(requestContext, fromSnapshots.at(i), &requestCounter); + } + + // Download the rest + while (requestCounter > 0) { + mDownloader->runLoop(0); + } + + // Save snapshots + for (int i = 0; i < requestContexts.size(); i++) { + auto& requestContext = requestContexts.at(i); + if (!requestContext.dest.empty()) { + logReading(requestContext.path, requestContext.timestamp, &requestContext.headers, + fmt::format("{}{}", requestContext.considerSnapshot ? "load to memory" : "retrieve", fromSnapshots.at(i) ? " from snapshot" : "")); + if (requestContext.considerSnapshot && fromSnapshots.at(i) != 2) { + saveSnapshot(requestContext); } } - if (200 <= response_code && response_code < 300) { - // good response and the content is directly provided and should have been dumped into "chunk" - } else if (response_code == 304) { - LOGP(debug, "Object exists but I am not serving it since it's already in your possession"); - } - // this is a more general redirection - else if (300 <= response_code && response_code < 400) { - // we try content locations in order of appearance until one succeeds - // 1st: The "Location" field - // 2nd: Possible "Content-Location" fields - Location field - // some locations are relative to the main server so we need to fix/complement them - auto complement_Location = [this](std::string const& loc) { - if (loc[0] == '/') { - // if it's just a path (noticed by trailing '/' we prepend the server url - return getURL() + loc; - } - return loc; - }; + } +} - std::vector locs; - auto iter = headerData.find("Location"); - if (iter != headerData.end()) { - locs.push_back(complement_Location(iter->second)); - } - // add alternative locations (not yet included) - auto iter2 = headerData.find("Content-Location"); - if (iter2 != headerData.end()) { - auto range = headerData.equal_range("Content-Location"); - for (auto it = range.first; it != range.second; ++it) { - if (std::find(locs.begin(), locs.end(), it->second) == locs.end()) { - locs.push_back(complement_Location(it->second)); - } - } - } - for (auto& l : locs) { - if (l.size() > 0) { - LOG(debug) << "Trying content location " << l; - navigateURLsAndLoadFileToMemory(dest, curl_handle, l, headers); - if (dest.size()) { /* or other success marker in future */ - break; - } - } - } - } else if (response_code == 404) { - LOG(error) << "Requested resource does not exist: " << url; - signalError(); +bool CcdbApi::loadLocalContentToMemory(o2::pmr::vector& dest, std::string& url) const +{ + if (url.find("alien:/", 0) != std::string::npos) { + std::map localHeaders; + loadFileToMemory(dest, url, &localHeaders, false); + auto it = localHeaders.find("Error"); + if (it != localHeaders.end() && it->second == "An error occurred during retrieval") { + return false; } else { - LOG(error) << "Error in fetching object " << url << ", curl response code:" << response_code; - signalError(); + return true; } - } else { - LOGP(alarm, "Curl request to {} failed with result {}, response code: {}", url, int(res), response_code); - signalError(); } - // indicate that an error occurred ---> used by caching layers (such as CCDBManager) - if (errorflag && headers) { - (*headers)["Error"] = "An error occurred during retrieval"; + if ((url.find("file:/", 0) != std::string::npos)) { + std::string path = url.substr(7); + if (std::filesystem::exists(path)) { + std::map localHeaders; + loadFileToMemory(dest, url, &localHeaders, o2::utils::Str::endsWith(path, ".root")); + auto it = localHeaders.find("Error"); + if (it != localHeaders.end() && it->second == "An error occurred during retrieval") { + return false; + } else { + return true; + } + } } - return; + return false; } -void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders) const +void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& path, std::map* localHeaders, bool fetchLocalMetaData) const { // Read file to memory as vector. For special case of the locally cached file retriev metadata stored directly in the file constexpr size_t MaxCopySize = 0x1L << 25; @@ -1700,7 +2064,7 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& p totalread += nread; } while (nread == (long)MaxCopySize); - if (localHeaders) { + if (localHeaders && fetchLocalMetaData) { TMemFile memFile("name", const_cast(dest.data()), dest.size(), "READ"); auto storedmeta = (std::map*)extractFromTFile(memFile, TClass::GetClass("std::map"), CCDBMETA_ENTRY); if (storedmeta) { @@ -1710,6 +2074,9 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, const std::string& p if ((isSnapshotMode() || mPreferSnapshotCache) && localHeaders->find("ETag") == localHeaders->end()) { // generate dummy ETag to profit from the caching (*localHeaders)["ETag"] = path; } + if (localHeaders->find("fileSize") == localHeaders->end()) { + (*localHeaders)["fileSize"] = fmt::format("{}", memFile.GetEND()); + } } return; } @@ -1756,12 +2123,74 @@ void CcdbApi::logReading(const std::string& path, long ts, const std::mapasynchSchedule(handle, requestCounter); +} + CURLcode CcdbApi::CURL_perform(CURL* handle) const { - if (mIsCCDBDownloaderEnabled) { + if (mIsCCDBDownloaderPreferred) { return mDownloader->perform(handle); } - return curl_easy_perform(handle); + CURLcode result; + for (int i = 1; i <= mCurlRetries && (result = curl_easy_perform(handle)) != CURLE_OK; i++) { + usleep(mCurlDelayRetries * i); + } + return result; +} + +/** + * Object, encapsulating a semaphore, regulating + * concurrent (multi-process) access to CCDB snapshot files. + */ +CCDBSemaphore::CCDBSemaphore(std::string const& snapshotpath, std::string const& path) +{ + LOG(debug) << "Entering semaphore barrier"; + mSemName = CcdbApi::determineSemaphoreName(snapshotpath, path); + try { + mSem = new boost::interprocess::named_semaphore(boost::interprocess::open_or_create_t{}, mSemName.c_str(), 1); + } catch (std::exception e) { + LOG(warn) << "Exception occurred during CCDB (cache) semaphore setup; Continuing without"; + mSem = nullptr; + } + // automatically wait + if (mSem) { + gSemaRegistry.add(this); + mSem->wait(); + } +} + +CCDBSemaphore::~CCDBSemaphore() +{ + LOG(debug) << "Ending semaphore barrier"; + if (mSem) { + mSem->post(); + if (mSem->try_wait()) { // if nobody else is waiting remove the semaphore resource + mSem->post(); + boost::interprocess::named_semaphore::remove(mSemName.c_str()); + } + gSemaRegistry.remove(this); + } +} + +SemaphoreRegistry::~SemaphoreRegistry() +{ + LOG(debug) << "Cleaning up semaphore registry with count " << mStore.size(); + for (auto& s : mStore) { + delete s; + mStore.erase(s); + } +} + +void SemaphoreRegistry::add(CCDBSemaphore const* ptr) +{ + mStore.insert(ptr); +} + +void SemaphoreRegistry::remove(CCDBSemaphore const* ptr) +{ + mStore.erase(ptr); } -} // namespace o2 +} // namespace o2::ccdb diff --git a/CCDB/src/CleanCCDBSemaphores.cxx b/CCDB/src/CleanCCDBSemaphores.cxx new file mode 100644 index 0000000000000..f107cde21a051 --- /dev/null +++ b/CCDB/src/CleanCCDBSemaphores.cxx @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CCDB/CcdbApi.h" +#include +#include + +namespace bpo = boost::program_options; + +bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm) +{ + options.add_options()( + "cachepath,p", bpo::value()->default_value("ccdb"), "path to whole CCDB cache dir as a basis for semaphore search")( + "sema,s", bpo::value()->default_value(""), "Specific named semaphore to be remove")( + "help,h", "Produce help message."); + + try { + bpo::store(parse_command_line(argc, argv, options), vm); + // help + if (vm.count("help")) { + std::cout << options << std::endl; + return false; + } + bpo::notify(vm); + } catch (const bpo::error& e) { + std::cerr << e.what() << "\n\n"; + std::cerr << "Error parsing command line arguments; Available options:\n"; + + std::cerr << options << std::endl; + return false; + } + return true; +} + +// A simple tool to clean CCDB related semaphores +int main(int argc, char* argv[]) +{ + bpo::options_description options("Tool to find and remove leaking CCDB semaphore from the system"); + bpo::variables_map vm; + if (!initOptionsAndParse(options, argc, argv, vm)) { + return 1; + } + + std::string sema = vm["sema"].as(); + if (sema.size() > 0) { + if (o2::ccdb::CcdbApi::removeSemaphore(sema, true)) { + std::cout << "Successfully removed " << sema << "\n"; + } + } + + std::string path = vm["cachepath"].as(); + o2::ccdb::CcdbApi::removeLeakingSemaphores(path, true); + return 0; +} diff --git a/CCDB/src/UploadTool.cxx b/CCDB/src/UploadTool.cxx index 44b8d8e20bc7d..9aba417b4f4a9 100644 --- a/CCDB/src/UploadTool.cxx +++ b/CCDB/src/UploadTool.cxx @@ -147,33 +147,44 @@ int main(int argc, char* argv[]) meta[p.first] = p.second; } - TFile f(filename.c_str()); - auto key = f.GetKey(keyname.c_str()); - if (key) { - // get type of key - auto classname = key->GetClassName(); - auto tcl = TClass::GetClass(classname); - auto object = f.Get(keyname.c_str()); - if (tcl->InheritsFrom("TTree")) { - auto tree = static_cast(object); - tree->LoadBaskets(0x1L << 32); // make tree memory based - tree->SetDirectory(nullptr); - } - // convert classname to typeinfo - // typeinfo - auto ti = tcl->GetTypeInfo(); - - std::cout << " Uploading an object of type " << key->GetClassName() - << " to path " << path << " with timestamp validity from " << starttimestamp - << " to " << endtimestamp << "\n"; - - api.storeAsTFile_impl(object, *ti, path, meta, starttimestamp, endtimestamp); + if (filename == "headersOnly") { + auto ent = meta.find("Redirect"); + std::cout << " Uploading a headers-only object to path " << path << " with timestamp validity from " << starttimestamp << " to " << endtimestamp + << " Redirection to: " << ((ent != meta.end()) ? ent->second : std::string{"none"}) << "\n"; + api.storeAsBinaryFile(nullptr, 0, "ignored", "", path, meta, starttimestamp, endtimestamp); if (!api.isSnapshotMode() && meta.find("adjustableEOV") != meta.end() && meta.find("default") == meta.end()) { - o2::ccdb::CcdbObjectInfo oi(path, classname, filename, meta, starttimestamp, endtimestamp); + o2::ccdb::CcdbObjectInfo oi(path, "", "", meta, starttimestamp, endtimestamp); o2::ccdb::adjustOverriddenEOV(api, oi); } } else { - std::cerr << "Key " << keyname << " does not exist\n"; + TFile f(filename.c_str()); + auto key = f.GetKey(keyname.c_str()); + if (key) { + // get type of key + auto classname = key->GetClassName(); + auto tcl = TClass::GetClass(classname); + auto object = f.Get(keyname.c_str()); + if (tcl->InheritsFrom("TTree")) { + auto tree = static_cast(object); + tree->LoadBaskets(0x1L << 32); // make tree memory based + tree->SetDirectory(nullptr); + } + // convert classname to typeinfo + // typeinfo + auto ti = tcl->GetTypeInfo(); + + std::cout << " Uploading an object of type " << key->GetClassName() + << " to path " << path << " with timestamp validity from " << starttimestamp + << " to " << endtimestamp << "\n"; + + api.storeAsTFile_impl(object, *ti, path, meta, starttimestamp, endtimestamp); + if (!api.isSnapshotMode() && meta.find("adjustableEOV") != meta.end() && meta.find("default") == meta.end()) { + o2::ccdb::CcdbObjectInfo oi(path, classname, filename, meta, starttimestamp, endtimestamp); + o2::ccdb::adjustOverriddenEOV(api, oi); + } + } else { + std::cerr << "Key " << keyname << " does not exist\n"; + } } return 0; diff --git a/CCDB/test/testBasicCCDBManager.cxx b/CCDB/test/testBasicCCDBManager.cxx index 7cd143f655547..6359bf2f5ccf4 100644 --- a/CCDB/test/testBasicCCDBManager.cxx +++ b/CCDB/test/testBasicCCDBManager.cxx @@ -26,7 +26,7 @@ using namespace o2::ccdb; -static string basePath; +static std::string basePath; std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; bool hostReachable = false; @@ -43,7 +43,7 @@ struct Fixture { std::cout << "Is host reachable ? --> " << hostReachable << std::endl; char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/") + hostname + "/pid" + getpid() + "/BasicCCDBManager/"; + basePath = std::string("Test/") + hostname + "/pid" + getpid() + "/BasicCCDBManager/"; std::cout << "Path we will use in this test suite : " + basePath << std::endl; } ~Fixture() diff --git a/CCDB/test/testCcdbApi.cxx b/CCDB/test/testCcdbApi.cxx index c2a3bf4e483c0..1b6a5d6f0967a 100644 --- a/CCDB/test/testCcdbApi.cxx +++ b/CCDB/test/testCcdbApi.cxx @@ -45,8 +45,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -63,7 +63,7 @@ struct Fixture { cout << "Is host reachable ? --> " << hostReachable << endl; char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/TestCcdbApi/") + hostname + "/pid" + getpid() + "/"; + basePath = std::string("Test/TestCcdbApi/") + hostname + "/pid" + getpid() + "/"; // Replace dashes by underscores to avoid problems in the creation of local directories std::replace(basePath.begin(), basePath.end(), '-','_'); cout << "Path we will use in this test suite : " + basePath << endl; @@ -72,7 +72,7 @@ struct Fixture { { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -104,7 +104,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(storeTMemFile_test, *utf::precondition(if_reachable())) @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(store_retrieve_TMemFile_templated_test, *utf::precondition( BOOST_CHECK(f.api.retrieveFromTFileAny(basePath + "CCDBPath", f.metadata) == nullptr); // try to get the headers back and to find the metadata - map md; + std::map md; path2 = f.api.retrieveFromTFileAny(basePath + "CCDBPath", f.metadata, -1, &md); BOOST_CHECK_EQUAL(md.count("Hello"), 1); BOOST_CHECK_EQUAL(md["Hello"], "World"); @@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(delete_test, *utf::precondition(if_reachable())) BOOST_CHECK(h2 == nullptr); } -void countItems(const string& s, int& countObjects, int& countSubfolders) +void countItems(const std::string& s, int& countObjects, int& countSubfolders) { countObjects = 0; countSubfolders = 0; @@ -368,7 +368,7 @@ BOOST_AUTO_TEST_CASE(list_test, *utf::precondition(if_reachable())) test_fixture f; // test non-empty top dir - string s = f.api.list("", "application/json"); // top dir + std::string s = f.api.list("", "application/json"); // top dir long nbLines = std::count(s.begin(), s.end(), '\n') + 1; BOOST_CHECK(nbLines > 5); @@ -436,7 +436,7 @@ BOOST_AUTO_TEST_CASE(TestHeaderParsing) BOOST_AUTO_TEST_CASE(TestFetchingHeaders, *utf::precondition(if_reachable())) { // first store the object - string objectPath = basePath + "objectETag"; + std::string objectPath = basePath + "objectETag"; test_fixture f; TH1F h1("objectETag", "objectETag", 100, 0, 99); f.api.storeAsTFile(&h1, objectPath, f.metadata); @@ -445,7 +445,7 @@ BOOST_AUTO_TEST_CASE(TestFetchingHeaders, *utf::precondition(if_reachable())) std::string etag; std::vector headers; std::vector pfns; - string path = objectPath + "/" + std::to_string(getCurrentTimestamp()); + std::string path = objectPath + "/" + std::to_string(getCurrentTimestamp()); auto updated = CcdbApi::getCCDBEntryHeaders("http://ccdb-test.cern.ch:8080/" + path, etag, headers); BOOST_CHECK_EQUAL(updated, true); BOOST_REQUIRE(headers.size() != 0); @@ -462,7 +462,7 @@ BOOST_AUTO_TEST_CASE(TestRetrieveHeaders, *utf::precondition(if_reachable())) TH1F h1("object1", "object1", 100, 0, 99); cout << "storing object 1 in " << basePath << "Test" << endl; - map metadata; + std::map metadata; metadata["custom"] = "whatever"; f.api.storeAsTFile(&h1, basePath + "Test", metadata); @@ -498,7 +498,7 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) // upload an object TH1F h1("object1", "object1", 100, 0, 99); cout << "storing object 1 in " << basePath << "Test" << endl; - map metadata; + std::map metadata; metadata["custom"] = "whatever"; metadata["id"] = "first"; f.api.storeAsTFile(&h1, basePath + "Test", metadata); @@ -507,10 +507,10 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) std::map headers = f.api.retrieveHeaders(basePath + "Test", metadata); BOOST_CHECK(headers.count("custom") > 0); BOOST_CHECK(headers.at("custom") == "whatever"); - string firstID = headers.at("ETag"); + std::string firstID = headers.at("ETag"); firstID.erase(std::remove(firstID.begin(), firstID.end(), '"'), firstID.end()); - map newMetadata; + std::map newMetadata; newMetadata["custom"] = "somethingelse"; // update the metadata and check @@ -529,7 +529,7 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) // get id cout << "get id" << endl; headers = f.api.retrieveHeaders(basePath + "Test", metadata); - string secondID = headers.at("ETag"); + std::string secondID = headers.at("ETag"); secondID.erase(std::remove(secondID.begin(), secondID.end(), '"'), secondID.end()); // update the metadata by id @@ -553,3 +553,47 @@ BOOST_AUTO_TEST_CASE(TestUpdateMetadata, *utf::precondition(if_reachable())) BOOST_CHECK(headers.count("custom") > 0); BOOST_CHECK(headers.at("custom") == "second"); } + +BOOST_AUTO_TEST_CASE(multi_host_test) +{ + CcdbApi api; + api.init("http://bogus-host.cern.ch,http://ccdb-test.cern.ch:8080"); + std::map metadata; + std::map headers; + o2::pmr::vector dst; + std::string url = "Analysis/ALICE3/Centrality"; + api.loadFileToMemory(dst, url, metadata, 1645780010602, &headers, "", "", "", true); + BOOST_CHECK(dst.size() != 0); +} + +BOOST_AUTO_TEST_CASE(vectored) +{ + CcdbApi api; + api.init("http://ccdb-test.cern.ch:8080"); + + int TEST_SAMPLE_SIZE = 5; + std::vector> dests(TEST_SAMPLE_SIZE); + std::vector> metadatas(TEST_SAMPLE_SIZE); + std::vector> headers(TEST_SAMPLE_SIZE); + + std::vector contexts; + for (int i = 0; i < TEST_SAMPLE_SIZE; i++) { + contexts.push_back(CcdbApi::RequestContext(dests.at(i), metadatas.at(i), headers.at(i))); + contexts.at(i).path = "Analysis/ALICE3/Centrality"; + contexts.at(i).timestamp = 1645780010602; + contexts.at(i).considerSnapshot = true; + } + + api.vectoredLoadFileToMemory(contexts); + + for (auto context : contexts) { + BOOST_CHECK(context.dest.size() != 0); + } +} + +BOOST_AUTO_TEST_CASE(empty_url) +{ + CcdbApi api; + string url = ""; + BOOST_CHECK_EXCEPTION(api.init(url), invalid_argument, [](std::invalid_argument const&) -> bool { return true; }); +} diff --git a/CCDB/test/testCcdbApiDownloader.cxx b/CCDB/test/testCcdbApiDownloader.cxx index 80e2826209bb4..76686f5ee1c00 100644 --- a/CCDB/test/testCcdbApiDownloader.cxx +++ b/CCDB/test/testCcdbApiDownloader.cxx @@ -13,6 +13,8 @@ #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK +#include "CommonUtils/StringUtils.h" +#include "CCDB/CCDBTimeStampUtils.h" #include #include #include @@ -21,9 +23,12 @@ #include #include -#include +#include #include +#include +#include "MemoryResources/MemoryResources.h" + using namespace std; namespace o2 @@ -46,15 +51,137 @@ size_t CurlWrite_CallbackFunc_StdString2(void* contents, size_t size, size_t nme return size * nmemb; } +std::string uniqueAgentID() +{ + std::string host = boost::asio::ip::host_name(); + char const* jobID = getenv("ALIEN_PROC_ID"); + if (jobID) { + return fmt::format("{}-{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6), jobID); + } else { + return fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); + } +} + CURL* createTestHandle(std::string* dst) { CURL* handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString2); curl_easy_setopt(handle, CURLOPT_WRITEDATA, dst); - curl_easy_setopt(handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/latest/"); + curl_easy_setopt(handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/"); + auto userAgent = uniqueAgentID(); + curl_easy_setopt(handle, CURLOPT_USERAGENT, userAgent.c_str()); return handle; } +namespace +{ +template > +size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userdata) +{ + auto* headers = static_cast(userdata); + auto header = std::string(buffer, size * nitems); + std::string::size_type index = header.find(':', 0); + if (index != std::string::npos) { + const auto key = boost::algorithm::trim_copy(header.substr(0, index)); + const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); + headers->insert(std::make_pair(key, value)); + } + return size * nitems; +} +} // namespace + +size_t writeCallbackNoLambda(void* contents, size_t size, size_t nmemb, void* chunkptr) +{ + auto& ho = *static_cast(chunkptr); + auto& chunk = *ho.object; + size_t realsize = size * nmemb, sz = 0; + ho.counter++; + try { + if (chunk.capacity() < chunk.size() + realsize) { + auto cl = ho.header.find("Content-Length"); + if (cl != ho.header.end()) { + sz = std::max(chunk.size() + realsize, (size_t)std::stol(cl->second)); + } else { + sz = chunk.size() + realsize; + // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz); + } + chunk.reserve(sz); + } + char* contC = (char*)contents; + chunk.insert(chunk.end(), contC, contC + realsize); + } catch (std::exception e) { + // LOGP(alarm, "failed to reserve {} bytes in CURL write callback (realsize = {}): {}", sz, realsize, e.what()); + realsize = 0; + } + return realsize; +} + +std::vector prepareAsyncHandles(size_t num, std::vector*>& dests) +{ + std::vector handles; + + for (int i = 0; i < num; i++) { + auto dest = new o2::pmr::vector(); + dests.push_back(dest); + CURL* curl_handle = curl_easy_init(); + handles.push_back(curl_handle); + + auto data = new DownloaderRequestData(); + data->hoPair.object = dest; + data->hosts.emplace_back("http://ccdb-test.cern.ch:8080"); + data->path = "Analysis/ALICE3/Centrality"; + data->timestamp = 1646729604010; + data->localContentCallback = nullptr; + + curl_easy_setopt(curl_handle, CURLOPT_URL, "http://ccdb-test.cern.ch:8080/Analysis/ALICE3/Centrality/1646729604010"); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeCallbackNoLambda); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&(data->hoPair)); + + curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, header_map_callbackhoPair.header)>); + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void*)&(data->hoPair.header)); + curl_easy_setopt(curl_handle, CURLOPT_PRIVATE, (void*)data); + } + return handles; +} + +BOOST_AUTO_TEST_CASE(asynch_schedule_test) +{ + int TRANSFERS = 5; + + if (curl_global_init(CURL_GLOBAL_ALL)) { + fprintf(stderr, "Could not init curl\n"); + return; + } + + CCDBDownloader downloader; + std::vector*> dests; + auto handles = prepareAsyncHandles(TRANSFERS, dests); + size_t transfersLeft = 0; + + for (auto handle : handles) { + downloader.asynchSchedule(handle, &transfersLeft); + } + + while (transfersLeft > 0) { + downloader.runLoop(false); + } + + for (int i = 0; i < TRANSFERS; i++) { + // I would claim that accessing the handles after they are complete + // is actually not supported by the current API, because it was + // previously relying on leaking the handles. Disabling the whole + // thing until we verify that's actually the case. + // + // long httpCode; + // curl_easy_getinfo(handles[i], CURLINFO_HTTP_CODE, &httpCode); + // BOOST_CHECK_EQUAL(httpCode, 200); + // BOOST_CHECK_NE(dests[i]->size(), 0); + // curl_easy_cleanup(handles[i]); + delete dests[i]; + } + curl_global_cleanup(); +} + BOOST_AUTO_TEST_CASE(perform_test) { if (curl_global_init(CURL_GLOBAL_ALL)) { @@ -68,13 +195,11 @@ BOOST_AUTO_TEST_CASE(perform_test) CURLcode curlCode = downloader.perform(handle); - BOOST_CHECK(curlCode == CURLE_OK); - std::cout << "CURL code: " << curlCode << "\n"; + BOOST_CHECK_EQUAL(curlCode, CURLE_OK); long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - std::cout << "HTTP code: " << httpCode << "\n"; + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); curl_global_cleanup(); @@ -97,19 +222,13 @@ BOOST_AUTO_TEST_CASE(blocking_batch_test) auto curlCodes = downloader.batchBlockingPerform(handleVector); for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -120,7 +239,7 @@ BOOST_AUTO_TEST_CASE(blocking_batch_test) curl_global_cleanup(); } -BOOST_AUTO_TEST_CASE(asynch_batch_test) +BOOST_AUTO_TEST_CASE(test_with_break) { if (curl_global_init(CURL_GLOBAL_ALL)) { fprintf(stderr, "Could not init curl\n"); @@ -130,31 +249,21 @@ BOOST_AUTO_TEST_CASE(asynch_batch_test) CCDBDownloader downloader; std::vector handleVector; std::vector destinations; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 100; i++) { destinations.push_back(new std::string()); handleVector.push_back(createTestHandle(destinations.back())); } - bool flag = false; - auto curlCodes = downloader.batchAsynchPerform(handleVector, &flag); - while (!flag) { - sleep(1); - } + auto curlCodes = downloader.batchBlockingPerform(handleVector); for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -162,29 +271,6 @@ BOOST_AUTO_TEST_CASE(asynch_batch_test) delete dst; } - curl_global_cleanup(); -} - -BOOST_AUTO_TEST_CASE(test_with_break) -{ - if (curl_global_init(CURL_GLOBAL_ALL)) { - fprintf(stderr, "Could not init curl\n"); - return; - } - - CCDBDownloader downloader; - std::vector handleVector; - std::vector destinations; - for (int i = 0; i < 100; i++) { - destinations.push_back(new std::string()); - handleVector.push_back(createTestHandle(destinations.back())); - } - - auto curlCodes = downloader.batchBlockingPerform(handleVector); - for (std::string* dst : destinations) { - delete dst; - } - sleep(10); std::vector handleVector2; @@ -196,19 +282,13 @@ BOOST_AUTO_TEST_CASE(test_with_break) auto curlCodes2 = downloader.batchBlockingPerform(handleVector2); for (CURLcode code : curlCodes2) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } + BOOST_CHECK_EQUAL(code, CURLE_OK); } for (CURL* handle : handleVector2) { long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); } @@ -219,87 +299,80 @@ BOOST_AUTO_TEST_CASE(test_with_break) curl_global_cleanup(); } -void testCallback(void* ptr) +void onUVClose(uv_handle_t* handle) { - int* intPtr = (int*)ptr; - *intPtr = 46; + if (handle != nullptr) { + free(handle); + } } -BOOST_AUTO_TEST_CASE(asynch_batch_callback) +void closeAllHandles(uv_handle_t* handle, void* arg) { - if (curl_global_init(CURL_GLOBAL_ALL)) { - fprintf(stderr, "Could not init curl\n"); - return; - } - - CCDBDownloader downloader; - std::vector handleVector; - std::vector destinations; - for (int i = 0; i < 10; i++) { - destinations.push_back(new std::string()); - handleVector.push_back(createTestHandle(destinations.back())); - } - - int testValue = 0; - - bool flag = false; - auto curlCodes = downloader.asynchBatchPerformWithCallback(handleVector, &flag, testCallback, &testValue); - while (!flag) { - sleep(1); - } - - BOOST_CHECK(testValue == 46); - - for (CURLcode code : curlCodes) { - BOOST_CHECK(code == CURLE_OK); - if (code != CURLE_OK) { - std::cout << "CURL Code: " << code << "\n"; - } - } - - for (CURL* handle : handleVector) { - long httpCode; - curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - if (httpCode != 200) { - std::cout << "HTTP Code: " << httpCode << "\n"; - } - curl_easy_cleanup(handle); - } - - for (std::string* dst : destinations) { - delete dst; + if (!uv_is_closing(handle)) { + uv_close(handle, onUVClose); } +} - curl_global_cleanup(); +void testTimerCB(uv_timer_t* handle) +{ + // Mock function to be used by tested timer } BOOST_AUTO_TEST_CASE(external_loop_test) { + // Prepare uv_loop to be provided to the downloader + auto uvLoop = new uv_loop_t(); + uv_loop_init(uvLoop); + + // Prepare test timer. It will be used to check whether the downloader affects external handles. + auto testTimer = (uv_timer_t*)malloc(sizeof(uv_timer_t)); + uv_timer_init(uvLoop, testTimer); + uv_timer_start(testTimer, testTimerCB, 10, 10); + if (curl_global_init(CURL_GLOBAL_ALL)) { fprintf(stderr, "Could not init curl\n"); return; } - uv_loop_t loop; - - CCDBDownloader downloader(&loop); + // Regular downloader test + auto downloader = new o2::ccdb::CCDBDownloader(uvLoop); std::string dst = ""; CURL* handle = createTestHandle(&dst); - CURLcode curlCode = downloader.perform(handle); + CURLcode curlCode = downloader->perform(handle); - BOOST_CHECK(curlCode == CURLE_OK); - std::cout << "CURL code: " << curlCode << "\n"; + BOOST_CHECK_EQUAL(curlCode, CURLE_OK); long httpCode; curl_easy_getinfo(handle, CURLINFO_HTTP_CODE, &httpCode); - BOOST_CHECK(httpCode == 200); - std::cout << "HTTP code: " << httpCode << "\n"; + BOOST_CHECK_EQUAL(httpCode, 200); curl_easy_cleanup(handle); - curl_global_cleanup(); + + // Check if test timer and external loop are still alive + BOOST_CHECK_NE(uv_is_active((uv_handle_t*)testTimer), 0); + BOOST_CHECK_NE(uv_loop_alive(uvLoop), 0); + + // Downloader must be closed before uv_loop. + // The reason for that are the uv_poll handles attached to the curl multi handle. + // The multi handle must be cleaned (via destuctor) before poll handles attached to them are removed (via walking and closing). + delete downloader; + while (uv_loop_alive(uvLoop) || uv_loop_close(uvLoop) == UV_EBUSY) { + uv_walk(uvLoop, closeAllHandles, nullptr); + uv_run(uvLoop, UV_RUN_ONCE); + } + delete uvLoop; +} + +BOOST_AUTO_TEST_CASE(trim_host_url_test) +{ + CCDBDownloader downloader; + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080"), "http://localhost:8080"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost"), "http://localhost"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080/some/path"), "http://localhost:8080"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost/some/path"), "http://localhost"); + BOOST_CHECK_EQUAL(downloader.trimHostUrl("http://localhost:8080/Task/Detector/1?HTTPOnly=true"), "http://localhost:8080"); } } // namespace ccdb diff --git a/CCDB/test/testCcdbApiHeaders.cxx b/CCDB/test/testCcdbApiHeaders.cxx new file mode 100644 index 0000000000000..bcfa2a5b44bc2 --- /dev/null +++ b/CCDB/test/testCcdbApiHeaders.cxx @@ -0,0 +1,413 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file testCcdbApiHeaders.cxx +/// \brief Test BasicCCDBManager header/metadata information functionality with caching +/// \author martin.oines.eide@cern.ch + +#define BOOST_TEST_MODULE CCDB +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include + +static std::string basePath; +// std::string ccdbUrl = "http://localhost:8080"; +std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; +bool hostReachable = false; + +/** + * Global fixture, ie general setup and teardown + * Copied from testBasicCCDBManager.cxx + */ +struct Fixture { + Fixture() + { + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + if (std::getenv("ALICEO2_CCDB_HOST")) { + ccdbUrl = std::string(std::getenv("ALICEO2_CCDB_HOST")); + } + ccdbManager.setURL(ccdbUrl); + hostReachable = ccdbManager.getCCDBAccessor().isHostReachable(); + char hostname[_POSIX_HOST_NAME_MAX]; + gethostname(hostname, _POSIX_HOST_NAME_MAX); + basePath = std::string("Users/m/meide/Tests/") + hostname + "/pid-" + getpid() + "/BasicCCDBManager/"; + + LOG(info) << "Path we will use in this test suite : " + basePath << std::endl; + LOG(info) << "ccdb url: " << ccdbUrl << std::endl; + LOG(info) << "Is host reachable ? --> " << hostReachable << std::endl; + } + ~Fixture() + { + if (hostReachable) { + o2::ccdb::BasicCCDBManager::instance().getCCDBAccessor().truncate(basePath + "*"); // This deletes the data after test is run, disable if you want to inspect the data + LOG(info) << "Test data truncated/deleted (" << basePath << ")" << std::endl; + } + } +}; +BOOST_GLOBAL_FIXTURE(Fixture); +/** + * Just an accessor to the hostReachable variable to be used to determine whether tests can be ran or not. + * Copied from testCcdbApi.cxx + */ +struct if_reachable { + boost::test_tools::assertion_result operator()(boost::unit_test::test_unit_id) + { + return hostReachable; + } +}; + +// Only compare known and stable keys (avoid volatile ones like Date) +static const std::set sStableKeys = { + "ETag", + "Valid-From", + "Valid-Until", + "Created", + "Last-Modified", + "Content-Disposition", + "Content-Location", + "path", + "partName", + "Content-MD5", + "Hello" // TODO find other headers to compare to +}; + +// Test that we get back the same header header keys as we put in (for stable keys) + +BOOST_AUTO_TEST_CASE(testCachedHeaders, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + // First store objects to test with + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + std::string pathA = basePath + "CachingA"; + std::string pathB = basePath + "CachingB"; + std::string pathC = basePath + "CachingC"; + std::string ccdbObjO = "testObjectO"; + std::string ccdbObjN = "testObjectN"; + std::string ccdbObjX = "testObjectX"; + std::map md = { + {"Hello", "World"}, + {"Key1", "Value1"}, + {"Key2", "Value2"}, + }; + long start = 1000, stop = 3000; + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjO, pathA, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjN, pathB, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjX, pathC, md, start, stop); + // initilize the BasicCCDBManager + ccdbManager.clearCache(); + ccdbManager.setCaching(true); // This is what we want to test. + + /// ━━━━━━━━━━━ ACT ━━━━━━━━━━━━ + // Plan: get one object, then another, then the first again and check the headers are the same + std::map headers1, headers2, headers3; + + auto* obj1 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers1); + auto* obj2 = ccdbManager.getForTimeStamp(pathB, (start + stop) / 2, &headers2); + auto* obj3 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers3); // Should lead to a cache hit! + + /// ━━━━━━━━━━━ ASSERT ━━━━━━━━━━━━ + /// Check that we got something + BOOST_REQUIRE(obj1 != nullptr); + BOOST_REQUIRE(obj2 != nullptr); + BOOST_REQUIRE(obj3 != nullptr); + + LOG(debug) << "obj1: " << *obj1; + LOG(debug) << "obj2: " << *obj2; + LOG(debug) << "obj3: " << *obj3; + + // Sanity check + /// Check that the objects are correct + BOOST_TEST(*obj1 == ccdbObjO); + BOOST_TEST(*obj3 == ccdbObjO); + BOOST_TEST(obj3 == obj1); // should be the same object in memory since it is cached + + BOOST_TEST(obj2 != obj1); + + (*obj1) = "ModifiedObject"; + BOOST_TEST(*obj1 == "ModifiedObject"); + BOOST_TEST(*obj3 == "ModifiedObject"); // obj3 and obj1 are the same object in memory + + // Check that the headers are the same for the two retrievals of the same object + BOOST_REQUIRE(headers1.size() != 0); + BOOST_REQUIRE(headers3.size() != 0); + + LOG(debug) << "Headers1 size: " << headers1.size(); + for (const auto& h : headers1) { + LOG(debug) << " " << h.first << " -> " << h.second; + } + LOG(debug) << "Headers3 size: " << headers3.size(); + for (const auto& h : headers3) { + LOG(debug) << " " << h.first << " -> " << h.second; + } + + for (const auto& stableKey : sStableKeys) { + LOG(info) << "Checking key: " << stableKey; + + BOOST_REQUIRE(headers1.count(stableKey) > 0); + BOOST_REQUIRE(headers3.count(stableKey) > 0); + BOOST_TEST(headers1.at(stableKey) == headers3.at(stableKey)); + } + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + + // Test that we can change the map and the two headers are not affected + headers1["NewKey"] = "NewValue"; + headers3["NewKey"] = "DifferentValue"; + BOOST_TEST(headers1["NewKey"] != headers3["NewKey"]); // This tests that we have a deep copy of the headers +} + +BOOST_AUTO_TEST_CASE(testNonCachedHeaders, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + // First store objects to test with + auto& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + std::string pathA = basePath + "NonCachingA"; + std::string pathB = basePath + "NonCachingB"; + std::string ccdbObjO = "testObjectO"; + std::string ccdbObjN = "testObjectN"; + std::map md = { + {"Hello", "World"}, + {"Key1", "Value1"}, + {"Key2", "Value2"}, + }; + long start = 1000, stop = 2000; + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjO, pathA, md, start, stop); + ccdbManager.getCCDBAccessor().storeAsTFileAny(&ccdbObjN, pathB, md, start, stop); + // initilize the BasicCCDBManager + ccdbManager.clearCache(); + ccdbManager.setCaching(false); // This is what we want to test, no caching + + /// ━━━━━━━━━━━ ACT ━━━━━━━━━━━━ + // Plan: get one object, then another, then the first again. Then check that the contents is the same but not the object in memory + std::map headers1, headers2, headers3; + + auto* obj1 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers1); + auto* obj2 = ccdbManager.getForTimeStamp(pathB, (start + stop) / 2, &headers2); + auto* obj3 = ccdbManager.getForTimeStamp(pathA, (start + stop) / 2, &headers3); // Should not be cached since explicitly disabled + + ccdbManager.setCaching(true); // Restore default state + /// ━━━━━━━━━━━ ASSERT ━━━━━━━━━━━ + /// Check that we got something + BOOST_REQUIRE(obj1 != nullptr); + BOOST_REQUIRE(obj2 != nullptr); + BOOST_REQUIRE(obj3 != nullptr); + + LOG(debug) << "obj1: " << *obj1; + LOG(debug) << "obj2: " << *obj2; + LOG(debug) << "obj3: " << *obj3; + + // Sanity check + /// Check that the objects are correct + BOOST_TEST(*obj1 == ccdbObjO); + BOOST_TEST(*obj3 == ccdbObjO); + BOOST_TEST(obj2 != obj1); + BOOST_TEST(obj3 != obj1); // should NOT be the same object in memory + (*obj1) = "ModifiedObject"; + BOOST_TEST(*obj1 == "ModifiedObject"); + BOOST_TEST(*obj3 != "ModifiedObject"); // obj3 and obj1 are NOT the same object in memory + + BOOST_TEST(headers1.size() == headers3.size()); + + // Remove the date header since it may be different even for the same object since we might have asked in different seconds + headers1.erase("Date"); + headers3.erase("Date"); + BOOST_TEST(headers1 == headers3, "The headers for the same object should be the same even if not cached"); + + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + BOOST_TEST(headers1.size() != 0); + BOOST_TEST(headers3.size() != 0); + BOOST_TEST(headers2.size() != 0); + BOOST_TEST(headers1 != headers2, "The headers for different objects should be different"); + + // cleanup + delete obj1; + delete obj2; + delete obj3; +} + +BOOST_AUTO_TEST_CASE(CacheFirstRetrievalAndHeadersPersistence, *boost::unit_test::precondition(if_reachable())) +{ + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + // Prepare two validity slots for same path to test ETag change later + std::string path = basePath + "ObjA"; + std::string objV1 = "ObjectVersion1"; + std::string objV2 = "ObjectVersion2"; + std::map meta1{ + {"UserKey1", "UValue1"}, + {"UserKey2", "UValue2"}}; + long v1start = 10'000; + long v1stop = 20'000; + long v2start = v1stop; // contiguous slot + long v2stop = v2start + (v1stop - v1start); + long mid1 = (v1start + v1stop) / 2; + // Store 2 versions + mgr.getCCDBAccessor().storeAsTFileAny(&objV1, path, meta1, v1start, v1stop); + mgr.getCCDBAccessor().storeAsTFileAny(&objV2, path, meta1, v2start, v2stop); + + mgr.clearCache(); + mgr.setCaching(true); + mgr.setFatalWhenNull(true); + mgr.setTimestamp(mid1); + + /// ━━━━━━━ACT━━━━━━━━━ + std::map headers1, headers2, headers4, headers5; + + // 1) First retrieval WITH headers inside 1st slot + auto* p1 = mgr.getForTimeStamp(path, mid1, &headers1); + size_t fetchedSizeAfterFirst = mgr.getFetchedSize(); + // 2) Second retrieval (cache hit) + auto* p2 = mgr.getForTimeStamp(path, mid1, &headers2); + size_t fetchedSizeAfterSecond = mgr.getFetchedSize(); + // 3) Third retrieval (cache hit) WITHOUT passing headers + auto* p3 = mgr.getForTimeStamp(path, mid1); + // 4) Fourth retrieval with headers again -> should still produce same headers + auto* p4 = mgr.getForTimeStamp(path, mid1, &headers4); + // 5) Fifth retrieval with headers again to check persistence + auto* p5 = mgr.getForTimeStamp(path, mid1, &headers5); + + mgr.setFatalWhenNull(false); // restore default + + /// ━━━━━━━ASSERT━━━━━━━━━ + + BOOST_TEST(p1 != nullptr); + BOOST_TEST(*p1 == objV1); + + BOOST_TEST(headers1.count("UserKey1") == 1); + BOOST_TEST(headers1.count("UserKey2") == 1); + BOOST_TEST(headers1["UserKey1"] == "UValue1"); + BOOST_TEST(headers1["UserKey2"] == "UValue2"); + BOOST_TEST(headers1.count("Valid-From") == 1); + BOOST_TEST(headers1.count("Valid-Until") == 1); + BOOST_TEST(headers1.count("ETag") == 1); + + /* Need to manually amend the headers1 to have cache valid until for comparison sake, + * the header is not set in the first request. + * It is only set if the internal cache of CCDB has seen this object before, apperently. + * This will never happen in this test since it was just created and not asked for before. + */ + headers1["Cache-Valid-Until"] = std::to_string(v1stop); + + /* In rare cases the header date might be different, if the second has ticked over between the requests + */ + headers1.erase("Date"); + headers2.erase("Date"); + headers4.erase("Date"); + headers5.erase("Date"); + + BOOST_TEST(p2 == p1); // same pointer for cached scenario + BOOST_TEST(headers2 == headers1); // identical header map + BOOST_TEST(fetchedSizeAfterSecond == fetchedSizeAfterFirst); // no new fetch + + BOOST_TEST(p3 == p1); + + BOOST_TEST(p4 == p1); + BOOST_TEST(headers4 == headers1); + + // Mutate the returned header map locally and ensure it does not corrupt internal cache + headers4["UserKey1"] = "Tampered"; + BOOST_TEST(p5 == p1); + BOOST_TEST(headers5["UserKey1"] == "UValue1"); // internal unchanged +} + +BOOST_AUTO_TEST_CASE(FailedFetchDoesNotGiveMetadata, *boost::unit_test::precondition(if_reachable())) +{ + + /// ━━━━━━━ ARRANGE ━━━━━━━━━ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "FailThenRecover"; + std::string content = "ContentX"; + std::map meta{{"Alpha", "Beta"}}; + long s = 300'000, e = 310'000; + mgr.getCCDBAccessor().storeAsTFileAny(&content, path, meta, s, e); + mgr.clearCache(); + mgr.setCaching(true); + mgr.setFatalWhenNull(false); + + /// ━━━━━━━ ACT ━━━━━━━━━ + // Intentionally pick a timestamp outside validity to fail first + long badTS = s - 1000; + long goodTS = (s + e) / 2; + std::map hFail, hGood; + auto* badObj = mgr.getForTimeStamp(path, badTS, &hFail); + auto* goodObj = mgr.getForTimeStamp(path, goodTS, &hGood); + + /// ━━━━━━━ ASSERT ━━━━━━━━━ + BOOST_TEST(!hFail.empty()); // Should have some headers + BOOST_TEST(hFail["Alpha"] != "Beta"); // But not the metadata + BOOST_TEST(hGood.count("Alpha") == 1); + BOOST_TEST(hGood["Alpha"] == "Beta"); + + mgr.setFatalWhenNull(true); +} + +BOOST_AUTO_TEST_CASE(FirstCallWithoutHeadersThenWithHeaders, *boost::unit_test::precondition(if_reachable())) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "LateHeaders"; + std::string body = "Late"; + std::map meta{{"LateKey", "LateVal"}}; + long s = 400'000, e = 410'000; + mgr.getCCDBAccessor().storeAsTFileAny(&body, path, meta, s, e); + + mgr.clearCache(); + mgr.setCaching(true); + long ts = (s + e) / 2; + + // 1) First call with nullptr headers + auto* first = mgr.getForTimeStamp(path, ts); + BOOST_TEST(first != nullptr); + BOOST_TEST(*first == body); + + // 2) Second call asking for headers - should return the full set + std::map h2; + auto* second = mgr.getForTimeStamp(path, ts, &h2); + BOOST_TEST(second == first); + BOOST_TEST(h2.count("LateKey") == 1); + BOOST_TEST(h2["LateKey"] == "LateVal"); + BOOST_TEST(h2.count("Valid-From") == 1); + BOOST_TEST(h2.count("Valid-Until") == 1); +} + +BOOST_AUTO_TEST_CASE(HeadersAreStableAcrossMultipleHits, *boost::unit_test::precondition(if_reachable())) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + std::string path = basePath + "StableHeaders"; + std::string body = "Stable"; + std::map meta{{"HK", "HV"}}; + long s = 500'000, e = 510'000; + mgr.getCCDBAccessor().storeAsTFileAny(&body, path, meta, s, e); + + mgr.clearCache(); + mgr.setCaching(true); + long ts = (s + e) / 2; + + std::map h1; + auto* o1 = mgr.getForTimeStamp(path, ts, &h1); + BOOST_TEST(o1 != nullptr); + BOOST_TEST(h1.count("HK") == 1); + + std::string etag = h1["ETag"]; + for (int i = 0; i < 15; ++i) { + std::map hi; + auto* oi = mgr.getForTimeStamp(path, ts, &hi); + BOOST_TEST(oi == o1); + BOOST_TEST(hi.count("HK") == 1); + BOOST_TEST(hi["ETag"] == etag); + } +} diff --git a/CCDB/test/testCcdbApiMultipleUrls.cxx b/CCDB/test/testCcdbApiMultipleUrls.cxx index 331d0553c3aec..07ab0ddcb4dcf 100644 --- a/CCDB/test/testCcdbApiMultipleUrls.cxx +++ b/CCDB/test/testCcdbApiMultipleUrls.cxx @@ -24,8 +24,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -40,14 +40,14 @@ struct Fixture { cout << "ccdb url: " << ccdbUrl << endl; hostReachable = api.isHostReachable(); cout << "Is host reachable ? --> " << hostReachable << endl; - basePath = string("Test/pid") + getpid() + "/"; + basePath = std::string("Test/pid") + getpid() + "/"; cout << "Path we will use in this test suite : " + basePath << endl; } ~Fixture() { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -79,7 +79,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(storeAndRetrieve, *utf::precondition(if_reachable())) diff --git a/CCDB/test/testCcdbApi_ConfigParam.cxx b/CCDB/test/testCcdbApi_ConfigParam.cxx index 8b3521bfd468a..568669d05978f 100644 --- a/CCDB/test/testCcdbApi_ConfigParam.cxx +++ b/CCDB/test/testCcdbApi_ConfigParam.cxx @@ -47,8 +47,8 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; -static string basePath; +static std::string ccdbUrl; +static std::string basePath; bool hostReachable = false; /** @@ -64,14 +64,14 @@ struct Fixture { hostReachable = api.isHostReachable(); char hostname[_POSIX_HOST_NAME_MAX]; gethostname(hostname, _POSIX_HOST_NAME_MAX); - basePath = string("Test/") + hostname + "/pid" + getpid() + "/"; + basePath = std::string("Test/") + hostname + "/pid" + getpid() + "/"; cout << "Path we will use in this test suite : " + basePath << endl; } ~Fixture() { if (hostReachable) { CcdbApi api; - map metadata; + std::map metadata; api.init(ccdbUrl); api.truncate(basePath + "*"); cout << "Test data truncated (" << basePath << ")" << endl; @@ -103,7 +103,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; BOOST_AUTO_TEST_CASE(testConfigParamRetrieval, *utf::precondition(if_reachable())) diff --git a/CCDB/test/testCcdbApi_alien.cxx b/CCDB/test/testCcdbApi_alien.cxx index f11f579346524..c50f83466fe06 100644 --- a/CCDB/test/testCcdbApi_alien.cxx +++ b/CCDB/test/testCcdbApi_alien.cxx @@ -29,7 +29,7 @@ using namespace o2::ccdb; namespace utf = boost::unit_test; namespace tt = boost::test_tools; -static string ccdbUrl; +static std::string ccdbUrl; bool hostReachable = false; /** @@ -71,7 +71,7 @@ struct test_fixture { ~test_fixture() = default; CcdbApi api; - map metadata; + std::map metadata; }; // handle the case where the object comes from alien and redirect does not work with curl diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ff713f8d05b..adecffc0f4dbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # Preamble -cmake_minimum_required(VERSION 3.19 FATAL_ERROR) +cmake_minimum_required(VERSION 3.27.1 FATAL_ERROR) # it's important to specify accurately the list of languages. for instance C and # C++ as we _do_ have some C files to compile explicitely as C (e.g. gl3w.c) @@ -25,6 +25,7 @@ include(CTest) set(ALIGPU_BUILD_TYPE "O2") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) set_property(GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES) cmake_host_system_information(RESULT _totalmem QUERY TOTAL_PHYSICAL_MEMORY) @@ -37,14 +38,9 @@ set_property(GLOBAL PROPERTY JOB_POOLS analysis=${ANALYSIS_COMPILE_POOL}) include(O2BuildSanityChecks) o2_build_sanity_checks() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -find_package(ONNXRuntime::ONNXRuntime CONFIG) -if (ONNXRuntime::ONNXRuntime_FOUND) - add_definitions(-DZDC_FASTSIM_ONNX) -endif() - include(O2CheckCXXFeatures) o2_check_cxx_features() @@ -90,9 +86,10 @@ include(O2AddTestRootMacro) include(O2ReportNonTestedMacros) include(O2TargetRootDictionary) include(O2DataFile) -include(O2TargetManPage) include(O2AddWorkflow) include(O2SetROOTPCMDependencies) +include(O2AddHipifiedExecutable) +include(O2AddHipifiedLibrary) # Main targets of the project in various subdirectories. Order matters. add_subdirectory(Common) @@ -119,10 +116,6 @@ endif() add_subdirectory(config) -add_custom_target(man ALL) -o2_target_man_page(man NAME o2) -o2_target_man_page(man NAME FairMQDevice) - # Testing and packaging only needed if we are the top level directory if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) # Documentation @@ -138,4 +131,3 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() set_root_pcm_dependencies() - diff --git a/CODEOWNERS b/CODEOWNERS index a4f4b141c639c..26021d458ad76 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,7 +18,7 @@ /Common/Constants @shahor02 /Common/Field @shahor02 /Common/MathUtils @shahor02 -/Common/SimConfig @sawenzel @benedikt-voelkel +/Common/SimConfig @sawenzel #/Common/Topologies #/Common/Types /Common/Utils @sawenzel @@ -28,20 +28,20 @@ /DataFormats/Detectors/Common @shahor02 /DataFormats/Detectors/CPV @peressounko @kharlov /DataFormats/Detectors/CTP @lietava -/DataFormats/Detectors/EMCAL @mfasDa @jokonig -/DataFormats/Detectors/FIT @jotwinow @afurs @andreasmolander @arvindkhuntia @mslupeck -/DataFormats/Detectors/FOCAL @maxrauch @mfasDa +/DataFormats/Detectors/EMCAL @nstrangm @jokonig +/DataFormats/Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 +/DataFormats/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter /DataFormats/Detectors/GlobalTracking @shahor02 /DataFormats/Detectors/GlobalTrackingWorkflow @shahor02 /DataFormats/Detectors/HMPID @gvolpe79 -/DataFormats/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 -/DataFormats/Detectors/MUON @AliceO2Group/muon-experts +/DataFormats/Detectors/ITSMFT @fprino @mcoquet642 @shahor02 +/DataFormats/Detectors/MUON @AliceO2Group/muon-experts @shahor02 /DataFormats/Detectors/PHOS @peressounko @kharlov -/DataFormats/Detectors/Passive @sawenzel @benedikt-voelkel +/DataFormats/Detectors/Passive @sawenzel /DataFormats/Detectors/TOF @noferini /DataFormats/Detectors/TPC @davidrohr @wiechula @shahor02 -/DataFormats/Detectors/TRD @f3sch @bazinski @tdietel @martenole @pachmay -/DataFormats/Detectors/Upgrades @qgp @marcovanleeuwen @mconcas +/DataFormats/Detectors/TRD @f3sch @bazinski @wille10 +/DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 /DataFormats/Detectors/ZDC @coppedis @@ -49,31 +49,32 @@ #/DataFormats/Legacy #/DataFormats/MemoryResources /DataFormats/Parameters @shahor02 -/DataFormats/QualityControl @knopers8 @Barthelemy @chiarazampolli +/DataFormats/QualityControl @knopers8 @Barthelemy @justonedev1 @chiarazampolli /DataFormats/Reconstruction @shahor02 #/DataFormats/TimeFrame /DataFormats/common @shahor02 -/DataFormats/simulation @sawenzel @benedikt-voelkel +/DataFormats/simulation @sawenzel -/Detectors/Base @sawenzel @shahor02 @benedikt-voelkel +/Detectors/Base @sawenzel @shahor02 /Detectors/Calibration @chiarazampolli @shahor02 /Detectors/CPV @peressounko @kharlov -/Detectors/EMCAL @mfasDa @jokonig -/Detectors/FIT @jotwinow @afurs @andreasmolander @arvindkhuntia @mslupeck -/Detectors/FOCAL @maxrauch @mfasDa -/Detectors/Geometry @sawenzel @shahor02 @benedikt-voelkel +/Detectors/EMCAL @nstrangm @jokonig +/Detectors/FIT @jotwinow @afurs @andreasmolander @sahilupadhyaya92 +/Detectors/FOCAL @maxrauch @iarsene @matthiasrichter +/Detectors/Geometry @sawenzel @shahor02 /Detectors/GlobalTracking @shahor02 /Detectors/GlobalTrackingWorkflow @shahor02 /Detectors/HMPID @gvolpe79 -/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 -/Detectors/MUON @AliceO2Group/muon-experts +/Detectors/ITSMFT @fprino @mcoquet642 @mconcas @shahor02 +/Detectors/MUON @AliceO2Group/muon-experts @shahor02 /Detectors/PHOS @peressounko @kharlov -/Detectors/Passive @sawenzel @benedikt-voelkel +/Detectors/Passive @sawenzel /Detectors/TOF @noferini /Detectors/TPC @davidrohr @wiechula @shahor02 -/Detectors/TRD @f3sch @bazinski @tdietel @martenole @pachmay -/Detectors/Upgrades @qgp @mconcas -/Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas +/Detectors/TRD @f3sch @bazinski @wille10 +/Detectors/Upgrades @mconcas +/Detectors/Upgrades/ALICE3 @mconcas @njacazio +/Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch /Detectors/ZDC @coppedis /Detectors/CTF @shahor02 /Detectors/Raw @shahor02 @@ -110,19 +111,19 @@ /GPU @davidrohr #/GPU/Common #/GPU/GPUTracking -/GPU/GPUTracking/TRDTracking @davidrohr @martenole +/GPU/GPUTracking/TRDTracking @davidrohr /GPU/TPCFastTransformation @davidrohr @sgorbuno /GPU/TPCSpaceChargeBase @davidrohr @ehellbar -/Generators @sawenzel @benedikt-voelkel +/Generators @sawenzel @jackal1-66 -/Steer @sawenzel @shahor02 @benedikt-voelkel +/Steer @sawenzel @shahor02 /Testing /Utilities @AliceO2Group/framework-admins -/Utilities/Mergers @Barthelemy @knopers8 -/Utilities/DataSampling @Barthelemy @knopers8 +/Utilities/Mergers @Barthelemy @knopers8 @justonedev1 +/Utilities/DataSampling @Barthelemy @knopers8 @justonedev1 #/Utilities/DataCompression #/Utilities/DataFlow #/Utilities/MCStepLogger diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index f435e269575aa..0b92758e45f43 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -16,5 +16,6 @@ add_subdirectory(Types) add_subdirectory(Utils) add_subdirectory(SimConfig) add_subdirectory(DCAFitter) +add_subdirectory(ML) o2_data_file(COPY maps DESTINATION Common) diff --git a/Common/Constants/include/CommonConstants/MathConstants.h b/Common/Constants/include/CommonConstants/MathConstants.h index 03368bb6f8bf9..9ef3b4dba5ae0 100644 --- a/Common/Constants/include/CommonConstants/MathConstants.h +++ b/Common/Constants/include/CommonConstants/MathConstants.h @@ -22,8 +22,9 @@ namespace constants { namespace math { -constexpr float Almost0 = 1.17549e-38; -constexpr float Almost1 = 1.f - Almost0; +constexpr float Almost0 = 0x1.0p-126f; // smallest non-denormal float +constexpr float Epsilon = 0x0.000002p0f; // smallest float such that 1 != 1 + Epsilon +constexpr float Almost1 = 1.f - 1.0e-6f; constexpr float VeryBig = 1.f / Almost0; constexpr float PI = 3.14159274101257324e+00f; diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index d3342f1df6f64..46aeff98d6033 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -12,39 +12,232 @@ /// \file PhysicsConstants.h /// \brief Header to collect physics constants /// \author ruben.shahoyan@cern.ch +/// \author Vít Kučera , Inha University +/// \note Use the make_pdg_header.py script to generate the enums and mass declarations. #ifndef ALICEO2_PHYSICSCONSTANTS_H_ #define ALICEO2_PHYSICSCONSTANTS_H_ -namespace o2 -{ -namespace constants -{ -namespace physics +namespace o2::constants::physics { // particles masses -constexpr float MassPhoton = 0.0; -constexpr float MassElectron = 0.000511; -constexpr float MassMuon = 0.105658; -constexpr float MassPionCharged = 0.139570; -constexpr float MassPionNeutral = 0.134976; -constexpr float MassKaonCharged = 0.493677; -constexpr float MassKaonNeutral = 0.497648; -constexpr float MassProton = 0.938272; -constexpr float MassLambda = 1.115683; -constexpr float MassDeuteron = 1.8756129; -constexpr float MassTriton = 2.8089211; -constexpr float MassHelium3 = 2.8083916; -constexpr float MassAlpha = 3.7273794; -constexpr float MassHyperTriton = 2.992; -constexpr float MassHyperhydrog4 = 3.931; -constexpr float MassXiMinus = 1.32171; -constexpr float MassOmegaMinus = 1.67245; - -constexpr float LightSpeedCm2S = 299792458.e2; // C in cm/s -constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns -} // namespace physics -} // namespace constants -} // namespace o2 + +// BEGINNING OF THE GENERATED BLOCK. +// DO NOT EDIT THIS BLOCK DIRECTLY! +// It has been generated by the make_pdg_header.py script. +// For modifications, edit the script and generate this block again. + +/// \brief Declarations of named PDG codes of particles missing in ROOT PDG_t +/// \note Follow kCamelCase naming convention +/// \link https://root.cern/doc/master/TPDGCode_8h.html +enum Pdg { + kEta = 221, + kOmega = 223, + kEtaPrime = 331, + kB0 = 511, + kB0Bar = -511, + kBPlus = 521, + kBCPlus = 541, + kBS = 531, + kBSBar = -531, + kD0 = 421, + kD0Bar = -421, + kD0StarPlus = 10411, + kD0Star0 = 10421, + kD1Plus = 20413, + kD10 = 20423, + kD2StarPlus = 415, + kD2Star0 = 425, + kDMinus = -411, + kDPlus = 411, + kDS = 431, + kDSBar = -431, + kDSStar = 433, + kDS1 = 10433, + kDS1Star2700 = 30433, + kDS1Star2860 = 40433, + kDS2Star = 435, + kDS3Star2860 = 437, + kDStar = 413, + kDStar0 = 423, + kChiC1 = 20443, + kJPsi = 443, + kLambdaB0 = 5122, + kLambdaCPlus = 4122, + kOmegaC0 = 4332, + kK0Star892 = 313, + kKPlusStar892 = 323, + kPhi = 333, + kSigmaC0 = 4112, + kSigmaCPlusPlus = 4222, + kSigmaCStar0 = 4114, + kSigmaCStarPlusPlus = 4224, + kX3872 = 9920443, + kXi0 = 3322, + kXiB0 = 5232, + kXiCCPlusPlus = 4422, + kXiCPlus = 4232, + kXiC0 = 4132, + kXiC3055Plus = 4325, + kXiC3080Plus = 4326, + kXiC3055_0 = 4315, + kXiC3080_0 = 4316, + kDeuteron = 1000010020, + kTriton = 1000010030, + kHelium3 = 1000020030, + kAlpha = 1000020040, + kLithium4 = 1000030040, + kHyperTriton = 1010010030, + kHyperHydrogen4 = 1010010040, + kHyperHelium4 = 1010020040, + kHyperHelium5 = 1010020050, + kHyperHelium4Sigma = 1110020040, + kLambda1520_Py = 102134 +}; + +/// \brief Declarations of masses for additional particles +constexpr double MassEta = 0.547862; +constexpr double MassOmega = 0.78266; +constexpr double MassEtaPrime = 0.95778; +constexpr double MassB0 = 5.27966; +constexpr double MassB0Bar = 5.27966; +constexpr double MassBPlus = 5.27934; +constexpr double MassBCPlus = 6.27447; +constexpr double MassBS = 5.36692; +constexpr double MassBSBar = 5.36692; +constexpr double MassD0 = 1.86484; +constexpr double MassD0Bar = 1.86484; +constexpr double MassD0StarPlus = 2.272; +constexpr double MassD0Star0 = 2.343; +constexpr double MassD1Plus = 2.372; +constexpr double MassD10 = 2.412; +constexpr double MassD2StarPlus = 2.4601; +constexpr double MassD2Star0 = 2.4611; +constexpr double MassDMinus = 1.86966; +constexpr double MassDPlus = 1.86966; +constexpr double MassDS = 1.96835; +constexpr double MassDSBar = 1.96835; +constexpr double MassDSStar = 2.1122; +constexpr double MassDS1 = 2.53511; +constexpr double MassDS1Star2700 = 2.714; +constexpr double MassDS1Star2860 = 2.859; +constexpr double MassDS2Star = 2.5691; +constexpr double MassDS3Star2860 = 2.86; +constexpr double MassDStar = 2.01026; +constexpr double MassDStar0 = 2.00685; +constexpr double MassChiC1 = 3.51067; +constexpr double MassJPsi = 3.0969; +constexpr double MassLambdaB0 = 5.6196; +constexpr double MassLambdaCPlus = 2.28646; +constexpr double MassOmegaC0 = 2.6952; +constexpr double MassK0Star892 = 0.89555; +constexpr double MassKPlusStar892 = 0.89167; +constexpr double MassPhi = 1.019461; +constexpr double MassSigmaC0 = 2.45375; +constexpr double MassSigmaCPlusPlus = 2.45397; +constexpr double MassSigmaCStar0 = 2.51848; +constexpr double MassSigmaCStarPlusPlus = 2.51841; +constexpr double MassX3872 = 3.87165; +constexpr double MassXi0 = 1.31486; +constexpr double MassXiB0 = 5.7919; +constexpr double MassXiCCPlusPlus = 3.62155; +constexpr double MassXiCPlus = 2.46771; +constexpr double MassXiC0 = 2.47044; +constexpr double MassXiC3055Plus = 3.0559; +constexpr double MassXiC3080Plus = 3.0772; +constexpr double MassXiC3055_0 = 3.059; +constexpr double MassXiC3080_0 = 3.0799; +constexpr double MassDeuteron = 1.87561294257; +constexpr double MassTriton = 2.80892113298; +constexpr double MassHelium3 = 2.80839160743; +constexpr double MassAlpha = 3.7273794066; +constexpr double MassLithium4 = 3.7513; +constexpr double MassHyperTriton = 2.991134; +constexpr double MassHyperHydrogen4 = 3.922434; +constexpr double MassHyperHelium4 = 3.921728; +constexpr double MassHyperHelium5 = 4.839961; +constexpr double MassHyperHelium4Sigma = 3.995; +constexpr double MassLambda1520_Py = 1.5195; + +/// \brief Declarations of masses for particles in ROOT PDG_t +constexpr double MassDown = 0.00467; +constexpr double MassDownBar = 0.00467; +constexpr double MassUp = 0.00216; +constexpr double MassUpBar = 0.00216; +constexpr double MassStrange = 0.0934; +constexpr double MassStrangeBar = 0.0934; +constexpr double MassCharm = 1.27; +constexpr double MassCharmBar = 1.27; +constexpr double MassBottom = 4.18; +constexpr double MassBottomBar = 4.18; +constexpr double MassTop = 172.5; +constexpr double MassTopBar = 172.5; +constexpr double MassGluon = 0.0; +constexpr double MassElectron = 0.000510999; +constexpr double MassPositron = 0.000510999; +constexpr double MassNuE = 0.0; +constexpr double MassNuEBar = 0.0; +constexpr double MassMuonMinus = 0.1056584; +constexpr double MassMuonPlus = 0.1056584; +constexpr double MassNuMu = 0.0; +constexpr double MassNuMuBar = 0.0; +constexpr double MassTauMinus = 1.77686; +constexpr double MassTauPlus = 1.77686; +constexpr double MassNuTau = 0.0; +constexpr double MassNuTauBar = 0.0; +constexpr double MassGamma = 0.0; +constexpr double MassZ0 = 91.1876; +constexpr double MassWPlus = 80.377; +constexpr double MassWMinus = 80.377; +constexpr double MassPi0 = 0.1349768; +constexpr double MassK0Long = 0.497611; +constexpr double MassPiPlus = 0.1395704; +constexpr double MassPiMinus = 0.1395704; +constexpr double MassProton = 0.9382721; +constexpr double MassProtonBar = 0.9382721; +constexpr double MassNeutron = 0.9395654; +constexpr double MassNeutronBar = 0.9395654; +constexpr double MassK0Short = 0.497611; +constexpr double MassK0 = 0.497611; +constexpr double MassK0Bar = 0.497611; +constexpr double MassKPlus = 0.493677; +constexpr double MassKMinus = 0.493677; +constexpr double MassLambda0 = 1.115683; +constexpr double MassLambda0Bar = 1.115683; +constexpr double MassLambda1520 = 1.519; +constexpr double MassSigmaMinus = 1.197449; +constexpr double MassSigmaBarPlus = 1.197449; +constexpr double MassSigmaPlus = 1.18937; +constexpr double MassSigmaBarMinus = 1.18937; +constexpr double MassSigma0 = 1.192642; +constexpr double MassSigma0Bar = 1.192642; +constexpr double MassXiMinus = 1.32171; +constexpr double MassXiPlusBar = 1.32171; +constexpr double MassOmegaMinus = 1.67245; +constexpr double MassOmegaPlusBar = 1.67245; + +// END OF THE GENERATED BLOCK + +// legacy names +constexpr double MassPhoton = MassGamma; +constexpr double MassMuon = MassMuonMinus; +constexpr double MassPionCharged = MassPiPlus; +constexpr double MassPionNeutral = MassPi0; +constexpr double MassKaonCharged = MassKPlus; +constexpr double MassKaonNeutral = MassK0; +constexpr double MassLambda = MassLambda0; +constexpr double MassHyperhydrog4 = MassHyperHydrogen4; +constexpr double MassHyperhelium4 = MassHyperHelium4; +constexpr double MassHyperhelium4sigma = MassHyperHelium4Sigma; + +// Light speed +constexpr float LightSpeedCm2S = 299792458.e2; // C in cm/s +constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns +constexpr float LightSpeedCm2PS = LightSpeedCm2S * 1e-12; // C in cm/ps + +// Light speed inverse +constexpr float invLightSpeedCm2PS = 1. / LightSpeedCm2PS; // 1/C in ps/cm + +} // namespace o2::constants::physics #endif diff --git a/Common/Constants/include/CommonConstants/make_pdg_header.py b/Common/Constants/include/CommonConstants/make_pdg_header.py new file mode 100755 index 0000000000000..f83c44bb401db --- /dev/null +++ b/Common/Constants/include/CommonConstants/make_pdg_header.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +"""! +@brief Generates and updates the body of a C++ header with PDG codes and particle masses. +@author Vít Kučera , Inha University +@date 2023-09-21 +""" + +import os +from ctypes import c_bool +from enum import Enum + +try: + import ROOT # pylint: disable=import-error + from ROOT import o2 +except (ModuleNotFoundError, ImportError) as exc: + raise OSError("O2 environment is not loaded.") from exc + + +# Enum of PDG_t particles +class PdgROOT(Enum): + kDown = ROOT.kDown + kDownBar = ROOT.kDownBar + kUp = ROOT.kUp + kUpBar = ROOT.kUpBar + kStrange = ROOT.kStrange + kStrangeBar = ROOT.kStrangeBar + kCharm = ROOT.kCharm + kCharmBar = ROOT.kCharmBar + kBottom = ROOT.kBottom + kBottomBar = ROOT.kBottomBar + kTop = ROOT.kTop + kTopBar = ROOT.kTopBar + kGluon = ROOT.kGluon + kElectron = ROOT.kElectron + kPositron = ROOT.kPositron + kNuE = ROOT.kNuE + kNuEBar = ROOT.kNuEBar + kMuonMinus = ROOT.kMuonMinus + kMuonPlus = ROOT.kMuonPlus + kNuMu = ROOT.kNuMu + kNuMuBar = ROOT.kNuMuBar + kTauMinus = ROOT.kTauMinus + kTauPlus = ROOT.kTauPlus + kNuTau = ROOT.kNuTau + kNuTauBar = ROOT.kNuTauBar + kGamma = ROOT.kGamma + kZ0 = ROOT.kZ0 + kWPlus = ROOT.kWPlus + kWMinus = ROOT.kWMinus + kPi0 = ROOT.kPi0 + kK0Long = ROOT.kK0Long + kPiPlus = ROOT.kPiPlus + kPiMinus = ROOT.kPiMinus + kProton = ROOT.kProton + kProtonBar = ROOT.kProtonBar + kNeutron = ROOT.kNeutron + kNeutronBar = ROOT.kNeutronBar + kK0Short = ROOT.kK0Short + kK0 = ROOT.kK0 + kK0Bar = ROOT.kK0Bar + kKPlus = ROOT.kKPlus + kKMinus = ROOT.kKMinus + kLambda0 = ROOT.kLambda0 + kLambda0Bar = ROOT.kLambda0Bar + kLambda1520 = ROOT.kLambda1520 + kSigmaMinus = ROOT.kSigmaMinus + kSigmaBarPlus = ROOT.kSigmaBarPlus + kSigmaPlus = ROOT.kSigmaPlus + kSigmaBarMinus = ROOT.kSigmaBarMinus + kSigma0 = ROOT.kSigma0 + kSigma0Bar = ROOT.kSigma0Bar + kXiMinus = ROOT.kXiMinus + kXiPlusBar = ROOT.kXiPlusBar + kOmegaMinus = ROOT.kOmegaMinus + kOmegaPlusBar = ROOT.kOmegaPlusBar + + +# Enum of additional particles +class Pdg(Enum): + kEta = 221 + kOmega = 223 + kEtaPrime = 331 + kB0 = 511 + kB0Bar = -511 + kBPlus = 521 + kBCPlus = 541 + kBS = 531 + kBSBar = -531 + kD0 = 421 + kD0Bar = -421 + kD0StarPlus = 10411 + kD0Star0 = 10421 + kD1Plus = 20413 + kD10 = 20423 + kD2StarPlus = 415 + kD2Star0 = 425 + kDMinus = -411 + kDPlus = 411 + kDS = 431 + kDSBar = -431 + kDSStar = 433 + kDS1 = 10433 + kDS1Star2700 = 30433 + kDS1Star2860 = 40433 + kDS2Star = 435 + kDS3Star2860 = 437 + kDStar = 413 + kDStar0 = 423 + kChiC1 = 20443 + kJPsi = 443 + kLambdaB0 = 5122 + kLambdaCPlus = 4122 + kOmegaC0 = 4332 + kK0Star892 = 313 + kKPlusStar892 = 323 + kPhi = 333 + kSigmaC0 = 4112 + kSigmaCPlusPlus = 4222 + kSigmaCStar0 = 4114 + kSigmaCStarPlusPlus = 4224 + kX3872 = 9920443 + kXi0 = 3322 + kXiB0 = 5232 + kXiCCPlusPlus = 4422 + kXiCPlus = 4232 + kXiC0 = 4132 + kXiC3055Plus = 4325 + kXiC3080Plus = 4326 + kXiC3055_0 = 4315 + kXiC3080_0 = 4316 + kDeuteron = 1000010020 + kTriton = 1000010030 + kHelium3 = 1000020030 + kAlpha = 1000020040 + kLithium4 = 1000030040 + kHyperTriton = 1010010030 + kHyperHydrogen4 = 1010010040 + kHyperHelium4 = 1010020040 + kHyperHelium5 = 1010020050 + kHyperHelium4Sigma = 1110020040 + kLambda1520_Py = 102134 # PYTHIA code different from PDG + + +dbPdg = o2.O2DatabasePDG + + +def mass(code): + """Returns particle mass from o2::O2DatabasePDG.""" + # Missing particles should be added in O2DatabasePDG.h. + success = c_bool(True) + return dbPdg.Mass(code, success) + + +def declare_mass(pdg, mass_type="double") -> str: + """Returns a C++ declaration of a particle mass constant.""" + return f"constexpr {mass_type} Mass{pdg.name[1:]} = {mass(pdg.value)};" + + +def main(): + """Main function""" + + path_header = "PhysicsConstants.h" + name_script = os.path.basename(__file__) + + # Comment at the beginning of the output + block_begin = "// BEGINNING OF THE GENERATED BLOCK." + # Comment at the end of the output + block_end = "// END OF THE GENERATED BLOCK" + # Preamble with instructions + block_preamble = ( + "// DO NOT EDIT THIS BLOCK DIRECTLY!" + f"\n// It has been generated by the {name_script} script." + "\n// For modifications, edit the script and generate this block again." + ) + # Start of enum declarations of additional particles + enum_o2_head = ( + "/// \\brief Declarations of named PDG codes of particles missing in ROOT PDG_t" + "\n/// \\note Follow kCamelCase naming convention" + "\n/// \\link https://root.cern/doc/master/TPDGCode_8h.html" + "\nenum Pdg {" + ) + # End of enum declarations of additional particles + enum_o2_foot = "};" + # Documentation string for mass declarations of additional particles + mass_o2_head = "/// \\brief Declarations of masses for additional particles" + # Documentation string for mass declarations of PDG_t particles + mass_root_head = "/// \\brief Declarations of masses for particles in ROOT PDG_t" + + # Get header content before and after the generated block. + print(f'File "{path_header}" will be updated.') + try: + with open(path_header, encoding="utf-8") as file: + content_old = file.readlines() + except OSError as exc: + raise OSError(f'Failed to open file "{path_header}".') from exc + lines_header_before: list[str] = [] + lines_header_after: list[str] = [] + got_block_begin = False + got_block_end = False + for line in content_old: + line = line.strip() + if line == block_begin: + got_block_begin = True + if not got_block_begin: + lines_header_before.append(line) + if got_block_end: + lines_header_after.append(line) + if line == block_end: + got_block_end = True + if not got_block_begin: + raise ValueError("Did not find the beginning of the block.") + if not got_block_end: + raise ValueError("Did not find the end of the block.") + + # Additional particles + lines_enum_o2: list[str] = [enum_o2_head] + lines_mass_o2: list[str] = [mass_o2_head] + for pdg_o2 in Pdg: + lines_enum_o2.append(f" {pdg_o2.name} = {pdg_o2.value},") + lines_mass_o2.append(declare_mass(pdg_o2)) + lines_enum_o2[-1] = lines_enum_o2[-1][:-1] # Remove the last comma. + lines_enum_o2.append(enum_o2_foot) + + # PDG_t particles + lines_mass_root: list[str] = [mass_root_head] + for pdg_root in PdgROOT: + lines_mass_root.append(declare_mass(pdg_root)) + + # Header body + content_new = "\n".join( + ( + *lines_header_before, + block_begin, + block_preamble, + "", + *lines_enum_o2, + "", + *lines_mass_o2, + "", + *lines_mass_root, + "", + block_end, + *lines_header_after, + "", + ) + ) + # print(content_new) + + # Overwrite the input file. + try: + with open(path_header, "w", encoding="utf-8") as file: + file.write(content_new) + print(f'File "{path_header}" has been overwritten.') + except OSError as exc: + raise OSError(f'Failed to write to file "{path_header}".') from exc + + +if __name__ == "__main__": + main() diff --git a/Common/DCAFitter/CMakeLists.txt b/Common/DCAFitter/CMakeLists.txt index e15c2885f2259..c0b2d0dca1026 100644 --- a/Common/DCAFitter/CMakeLists.txt +++ b/Common/DCAFitter/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) + o2_add_library(DCAFitter TARGETVARNAME targetName SOURCES src/DCAFitterN.cxx @@ -20,8 +22,7 @@ o2_add_library(DCAFitter O2::DetectorsBase) o2_target_root_dictionary(DCAFitter - HEADERS include/DCAFitter/HelixHelper.h - include/DCAFitter/DCAFitterN.h + HEADERS include/DCAFitter/DCAFitterN.h include/DCAFitter/FwdDCAFitterN.h) if (OpenMP_CXX_FOUND) @@ -38,3 +39,5 @@ o2_add_test( LABELS vertexing ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) + +add_subdirectory(GPU) diff --git a/Common/DCAFitter/GPU/CMakeLists.txt b/Common/DCAFitter/GPU/CMakeLists.txt new file mode 100644 index 0000000000000..faf15f8aab3df --- /dev/null +++ b/Common/DCAFitter/GPU/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +if (CUDA_ENABLED) +add_subdirectory(cuda) +endif() + +if (HIP_ENABLED) +add_subdirectory(hip) +endif() \ No newline at end of file diff --git a/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h b/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h new file mode 100644 index 0000000000000..3aa5ead805acd --- /dev/null +++ b/Common/DCAFitter/GPU/DeviceInterface/GPUInterface.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief Helper interface to the GPU device, meant to be compatible with manual allocation/streams and GPUReconstruction ones. +/// \author matteo.concas@cern.ch + +#ifndef DCAFITTER_GPU_INTERFACE +#define DCAFITTER_GPU_INTERFACE + +#include +#include +#include + +namespace o2 +{ +namespace vertexing +{ +namespace device +{ + +#if !defined(__HIPCC__) && !defined(__CUDACC__) +typedef struct _dummyStream { +} Stream; +#else +#ifdef __HIPCC__ +typedef hipStream_t Stream; +#else +typedef cudaStream_t Stream; +#endif +#endif + +class GPUInterface +{ + public: + GPUInterface(GPUInterface& other) = delete; + void operator=(const GPUInterface&) = delete; + + static GPUInterface* Instance(); + + // APIs + void registerBuffer(void*, size_t); + void unregisterBuffer(void* addr); + void allocDevice(void**, size_t); + void freeDevice(void*); + Stream& getStream(unsigned short N = 0); + Stream& getNextStream(); + + protected: + GPUInterface(size_t N = 1); + ~GPUInterface(); + + void resize(size_t); + + std::atomic mLastUsedStream{0}; + static GPUInterface* sGPUInterface; + std::vector mPool{}; + std::vector mStreams{}; +}; + +} // namespace device +} // namespace vertexing +} // namespace o2 +#endif diff --git a/Common/DCAFitter/GPU/cuda/CMakeLists.txt b/Common/DCAFitter/GPU/cuda/CMakeLists.txt new file mode 100644 index 0000000000000..6b89207279fe0 --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DCAFitterCUDA + TARGETVARNAME targetName + SOURCES DCAFitterN.cu + GPUInterface.cu + PUBLIC_INCLUDE_DIRECTORIES ../../include + PUBLIC_INCLUDE_DIRECTORIES ../ + PUBLIC_LINK_LIBRARIES O2::MathUtils + O2::ReconstructionDataFormats + O2::DetectorsBase + PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider) +set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) +# add_compile_options(-lineinfo) + +#o2_add_test(DCAFitterNCUDA +# SOURCES test/testDCAFitterNGPU.cxx +# PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats +# O2::DCAFitterCUDA +# O2::DCAFitter +# ROOT::Core +# ROOT::Physics +# COMPONENT_NAME gpu +# LABELS vertexing +# ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage +# VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) diff --git a/Common/DCAFitter/GPU/cuda/DCAFitterN.cu b/Common/DCAFitter/GPU/cuda/DCAFitterN.cu new file mode 100644 index 0000000000000..ed7e8acba5397 --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/DCAFitterN.cu @@ -0,0 +1,280 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __HIPCC__ +#include "hip/hip_runtime.h" +#else +#include +#endif + +#include + +#include "GPUCommonDef.h" +#include "DCAFitter/DCAFitterN.h" +#include "DeviceInterface/GPUInterface.h" + +#define gpuCheckError(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__); \ + } +inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) +{ + if (code != cudaSuccess) { + std::cout << "GPUassert: " << cudaGetErrorString(code) << " " << file << " " << line << std::endl; + if (abort) { + throw std::runtime_error("GPU assert failed."); + } + } +} +namespace o2::vertexing::device +{ +namespace kernel +{ +GPUg() void warmUpGpuKernel() +{ + unsigned int tid = blockIdx.x * blockDim.x + threadIdx.x; + float ia, ib; + ia = ib = 0.0f; + ib += ia + tid; +} + +template +GPUg() void printKernel(Fitter* fitter) +{ + if (threadIdx.x == 0) { + printf(" =============== GPU DCA Fitter %d prongs =================\n", Fitter::getNProngs()); + fitter->print(); + printf(" =========================================================\n"); + } +} + +template +GPUg() void initFitters(Fitter* fitters, unsigned int off, unsigned int N) +{ + for (auto iThread{blockIdx.x * blockDim.x + threadIdx.x + 1}; iThread < N; iThread += blockDim.x * gridDim.x) { + fitters[iThread + off] = fitters[off]; + } +} + +template +GPUg() void processKernel(Fitter* fitter, int* res, Tr*... tracks) +{ + *res = fitter->process(*tracks...); +} + +template +GPUg() void processBatchKernel(Fitter* fitters, int* results, unsigned int off, unsigned int N, Tr*... tracks) +{ + for (auto iThread{blockIdx.x * blockDim.x + threadIdx.x}; iThread < N; iThread += blockDim.x * gridDim.x) { + results[iThread + off] = fitters[iThread + off].process(tracks[iThread + off]...); + } +} + +} // namespace kernel + +/// CPU handlers +template +void print(const int nBlocks, + const int nThreads, + Fitter& fitter) +{ + Fitter* fitter_device; + gpuCheckError(cudaMalloc(reinterpret_cast(&fitter_device), sizeof(Fitter))); + gpuCheckError(cudaMemcpy(fitter_device, &fitter, sizeof(Fitter), cudaMemcpyHostToDevice)); + + kernel::printKernel<<>>(fitter_device); + + gpuCheckError(cudaPeekAtLastError()); + gpuCheckError(cudaDeviceSynchronize()); +} + +template +int process(const int nBlocks, + const int nThreads, + Fitter& fitter, + Tr&... args) +{ + Fitter* fitter_device; + std::array tracks_device; + int result, *result_device; + + gpuCheckError(cudaMalloc(reinterpret_cast(&fitter_device), sizeof(Fitter))); + gpuCheckError(cudaMalloc(reinterpret_cast(&result_device), sizeof(int))); + + int iArg{0}; + ([&] { + gpuCheckError(cudaMalloc(reinterpret_cast(&(tracks_device[iArg])), sizeof(o2::track::TrackParCov))); + gpuCheckError(cudaMemcpy(tracks_device[iArg], &args, sizeof(o2::track::TrackParCov), cudaMemcpyHostToDevice)); + ++iArg; + }(), + ...); + + gpuCheckError(cudaMemcpy(fitter_device, &fitter, sizeof(Fitter), cudaMemcpyHostToDevice)); + + std::apply([&](auto&&... args) { kernel::processKernel<<>>(fitter_device, result_device, args...); }, tracks_device); + + gpuCheckError(cudaPeekAtLastError()); + gpuCheckError(cudaDeviceSynchronize()); + + gpuCheckError(cudaMemcpy(&result, result_device, sizeof(int), cudaMemcpyDeviceToHost)); + gpuCheckError(cudaMemcpy(&fitter, fitter_device, sizeof(Fitter), cudaMemcpyDeviceToHost)); + iArg = 0; + ([&] { + gpuCheckError(cudaMemcpy(&args, tracks_device[iArg], sizeof(o2::track::TrackParCov), cudaMemcpyDeviceToHost)); + gpuCheckError(cudaFree(tracks_device[iArg])); + ++iArg; + }(), + ...); + + gpuCheckError(cudaFree(fitter_device)); + gpuCheckError(cudaFree(result_device)); + + return result; +} + +template +void processBulk(const int nBlocks, + const int nThreads, + const int nBatches, + std::vector& fitters, + std::vector& results, + std::vector&... args) +{ + auto* gpuInterface = GPUInterface::Instance(); + kernel::warmUpGpuKernel<<<1, 1, 0, gpuInterface->getNextStream()>>>(); + + // Benchmarking events + std::vector ioUp(nBatches), ioDown(nBatches), kerElapsed(nBatches); + std::vector startIOUp(nBatches), endIOUp(nBatches), startIODown(nBatches), endIODown(nBatches), startKer(nBatches), endKer(nBatches); + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventCreate(&startIOUp[iBatch])); + gpuCheckError(cudaEventCreate(&endIOUp[iBatch])); + gpuCheckError(cudaEventCreate(&startIODown[iBatch])); + gpuCheckError(cudaEventCreate(&endIODown[iBatch])); + gpuCheckError(cudaEventCreate(&startKer[iBatch])); + gpuCheckError(cudaEventCreate(&endKer[iBatch])); + } + + // Tracks + std::array tracks_device; + int iArg{0}; + ([&] { + gpuInterface->registerBuffer(reinterpret_cast(args.data()), sizeof(Tr) * args.size()); + gpuInterface->allocDevice(reinterpret_cast(&(tracks_device[iArg])), sizeof(Tr) * args.size()); + ++iArg; + }(), + ...); + + // Fitters + gpuInterface->registerBuffer(reinterpret_cast(fitters.data()), sizeof(Fitter) * fitters.size()); + Fitter* fitters_device; + gpuInterface->allocDevice(reinterpret_cast(&fitters_device), sizeof(Fitter) * fitters.size()); + + // Results + gpuInterface->registerBuffer(reinterpret_cast(results.data()), sizeof(int) * fitters.size()); + int* results_device; + gpuInterface->allocDevice(reinterpret_cast(&results_device), sizeof(int) * fitters.size()); + + // R.R. Computation + int totalSize = fitters.size(); + int batchSize = totalSize / nBatches; + int remainder = totalSize % nBatches; + + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + auto& stream = gpuInterface->getNextStream(); + auto offset = iBatch * batchSize + std::min(iBatch, remainder); + auto nFits = batchSize + (iBatch < remainder ? 1 : 0); + + gpuCheckError(cudaEventRecord(startIOUp[iBatch], stream)); + gpuCheckError(cudaMemcpyAsync(fitters_device + offset, fitters.data() + offset, sizeof(Fitter) /* * nFits */, cudaMemcpyHostToDevice, stream)); // copying just the first element of the buffer + iArg = 0; + ([&] { + gpuCheckError(cudaMemcpyAsync(tracks_device[iArg] + offset, args.data() + offset, sizeof(Tr) * nFits, cudaMemcpyHostToDevice, stream)); + ++iArg; + }(), + ...); + gpuCheckError(cudaEventRecord(endIOUp[iBatch], stream)); + + gpuCheckError(cudaEventRecord(startKer[iBatch], stream)); + kernel::initFitters<<>>(fitters_device, offset, nFits); + std::apply([&](auto&&... args) { kernel::processBatchKernel<<>>(fitters_device, results_device, offset, nFits, args...); }, tracks_device); + gpuCheckError(cudaEventRecord(endKer[iBatch], stream)); + + gpuCheckError(cudaPeekAtLastError()); + iArg = 0; + gpuCheckError(cudaEventRecord(startIODown[iBatch], stream)); + ([&] { + gpuCheckError(cudaMemcpyAsync(args.data() + offset, tracks_device[iArg] + offset, sizeof(Tr) * nFits, cudaMemcpyDeviceToHost, stream)); + ++iArg; + }(), + ...); + + gpuCheckError(cudaMemcpyAsync(fitters.data() + offset, fitters_device + offset, sizeof(Fitter) * nFits, cudaMemcpyDeviceToHost, stream)); + gpuCheckError(cudaMemcpyAsync(results.data() + offset, results_device + offset, sizeof(int) * nFits, cudaMemcpyDeviceToHost, stream)); + gpuCheckError(cudaEventRecord(endIODown[iBatch], stream)); + } + + ([&] { gpuInterface->unregisterBuffer(args.data()); }(), ...); + + for (auto* tracksD : tracks_device) { + gpuInterface->freeDevice(tracksD); + } + + gpuInterface->freeDevice(fitters_device); + gpuInterface->freeDevice(results_device); + gpuInterface->unregisterBuffer(fitters.data()); + gpuInterface->unregisterBuffer(results.data()); + + // Do benchmarks + gpuCheckError(cudaDeviceSynchronize()); + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventElapsedTime(&ioUp[iBatch], startIOUp[iBatch], endIOUp[iBatch])); + gpuCheckError(cudaEventElapsedTime(&kerElapsed[iBatch], startKer[iBatch], endKer[iBatch])); + gpuCheckError(cudaEventElapsedTime(&ioDown[iBatch], startIODown[iBatch], endIODown[iBatch])); + } + + float totalUp = std::accumulate(ioUp.begin(), ioUp.end(), 0.f); + float totalDown = std::accumulate(ioDown.begin(), ioDown.end(), 0.f); + float totalKernels = std::accumulate(kerElapsed.begin(), kerElapsed.end(), 0.f); + LOGP(info, "Config: {} batches, {} blocks, {} threads", nBatches, nBlocks, nThreads); + LOGP(info, "Total I/O time: Up {} ms Avg {} ms, Down {} ms Avg {} ms", totalUp, totalUp / float(nBatches), totalDown, totalDown / (float)nBatches); + LOGP(info, "Total Kernel time: {} ms Avg {} ms", totalKernels, totalKernels / (float)nBatches); + + for (int iBatch{0}; iBatch < nBatches; ++iBatch) { + gpuCheckError(cudaEventDestroy(startIOUp[iBatch])); + gpuCheckError(cudaEventDestroy(endIOUp[iBatch])); + gpuCheckError(cudaEventDestroy(startIODown[iBatch])); + gpuCheckError(cudaEventDestroy(endIODown[iBatch])); + gpuCheckError(cudaEventDestroy(startKer[iBatch])); + gpuCheckError(cudaEventDestroy(endKer[iBatch])); + } +} + +template void processBulk(const int, + const int, + const int, + std::vector>&, + std::vector&, + std::vector&, + std::vector&); +template void processBulk(const int, + const int, + const int, + std::vector>&, + std::vector&, + std::vector&, + std::vector&, + std::vector&); +template int process(const int, const int, o2::vertexing::DCAFitterN<2>&, o2::track::TrackParCov&, o2::track::TrackParCov&); +template int process(const int, const int, o2::vertexing::DCAFitterN<3>&, o2::track::TrackParCov&, o2::track::TrackParCov&, o2::track::TrackParCov&); +template void print(const int, const int, o2::vertexing::DCAFitterN<2>&); +template void print(const int, const int, o2::vertexing::DCAFitterN<3>&); +} // namespace o2::vertexing::device \ No newline at end of file diff --git a/Common/DCAFitter/GPU/cuda/GPUInterface.cu b/Common/DCAFitter/GPU/cuda/GPUInterface.cu new file mode 100644 index 0000000000000..09f9cdc595dcd --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/GPUInterface.cu @@ -0,0 +1,108 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief Helper interface to the GPU device, meant to be compatible with manual allocation/streams and GPUReconstruction ones. +/// \author matteo.concas@cern.ch + +#ifdef __HIPCC__ +#include "hip/hip_runtime.h" +#else +#include +#endif + +#include +#include + +#include "DeviceInterface/GPUInterface.h" + +#define gpuCheckError(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__); \ + } +#define gpuCheckErrorSoft(x) \ + { \ + gpuAssert((x), __FILE__, __LINE__, false); \ + } +inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) +{ + if (code != cudaSuccess) { + std::cerr << "GPUassert: " << cudaGetErrorString(code) << " " << file << " " << line << std::endl; + if (abort) { + throw std::runtime_error("GPU assert failed."); + } + } +} + +namespace o2::vertexing::device +{ + +GPUInterface::GPUInterface(size_t N) +{ + resize(N); + for (auto& st : mStreams) { + gpuCheckError(cudaStreamCreate(&st)); + } +} + +GPUInterface::~GPUInterface() +{ + for (auto& st : mStreams) { + gpuCheckError(cudaStreamDestroy(st)); + } +} + +void GPUInterface::resize(size_t N) +{ + mPool.resize(N); + mStreams.resize(N); +} + +void GPUInterface::registerBuffer(void* addr, size_t bufferSize) +{ + gpuCheckError(cudaHostRegister(addr, bufferSize, cudaHostRegisterDefault)); +} + +void GPUInterface::unregisterBuffer(void* addr) +{ + gpuCheckError(cudaHostUnregister(addr)); +} + +GPUInterface* GPUInterface::sGPUInterface = nullptr; +GPUInterface* GPUInterface::Instance() +{ + if (sGPUInterface == nullptr) { + const auto* envValue = std::getenv("GPUINTERFACE_NSTREAMS"); + sGPUInterface = new GPUInterface(envValue == nullptr ? 8 : std::stoi(envValue)); + } + return sGPUInterface; +} + +void GPUInterface::allocDevice(void** addrPtr, size_t bufferSize) +{ + gpuCheckError(cudaMalloc(addrPtr, bufferSize)); +} + +void GPUInterface::freeDevice(void* addr) +{ + gpuCheckError(cudaFree(addr)); +} + +Stream& GPUInterface::getStream(unsigned short N) +{ + return mStreams[N % mStreams.size()]; +} + +Stream& GPUInterface::getNextStream() +{ + unsigned short next = mLastUsedStream.fetch_add(1) % mStreams.size(); // wrap-around + automatic wrap-around beyond 65535 + return mStreams[next]; +} +} // namespace o2::vertexing::device \ No newline at end of file diff --git a/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx b/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx new file mode 100644 index 0000000000000..e3f882dc755cb --- /dev/null +++ b/Common/DCAFitter/GPU/cuda/test/testDCAFitterNGPU.cxx @@ -0,0 +1,1086 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test DCAFitterN class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include + +#include "DCAFitter/DCAFitterN.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace vertexing +{ + +using Vec3D = ROOT::Math::SVector; + +template +float checkResults(o2::utils::TreeStreamRedirector& outs, std::string& treeName, FITTER& fitter, + Vec3D& vgen, TLorentzVector& genPar, const std::vector& dtMass) +{ + int nCand = fitter.getNCandidates(); + std::array p; + float distMin = 1e9; + bool absDCA = fitter.getUseAbsDCA(); + bool useWghDCA = fitter.getWeightedFinalPCA(); + for (int ic = 0; ic < nCand; ic++) { + const auto& vtx = fitter.getPCACandidate(ic); + auto df = vgen; + df -= vtx; + + TLorentzVector moth, prong; + for (int i = 0; i < fitter.getNProngs(); i++) { + const auto& trc = fitter.getTrack(i, ic); + trc.getPxPyPzGlo(p); + prong.SetVectM({p[0], p[1], p[2]}, dtMass[i]); + moth += prong; + } + auto nIter = fitter.getNIterations(ic); + auto chi2 = fitter.getChi2AtPCACandidate(ic); + double dst = TMath::Sqrt(df[0] * df[0] + df[1] * df[1] + df[2] * df[2]); + distMin = dst < distMin ? dst : distMin; + auto parentTrack = fitter.createParentTrackParCov(ic); + outs << treeName.c_str() << "cand=" << ic << "ncand=" << nCand << "nIter=" << nIter << "chi2=" << chi2 + << "genPart=" << genPar << "recPart=" << moth + << "genX=" << vgen[0] << "genY=" << vgen[1] << "genZ=" << vgen[2] + << "dx=" << df[0] << "dy=" << df[1] << "dz=" << df[2] << "dst=" << dst + << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack; + for (int i = 0; i < fitter.getNProngs(); i++) { + outs << treeName.c_str() << fmt::format("prong{}=", i).c_str() << fitter.getTrack(i, ic); + } + outs << treeName.c_str() << "\n"; + } + return distMin; +} + +TLorentzVector generate(Vec3D& vtx, std::vector& vctr, float bz, + TGenPhaseSpace& genPHS, double parMass, const std::vector& dtMass, std::vector forceQ) +{ + const float errYZ = 1e-2, errSlp = 1e-3, errQPT = 2e-2; + std::array covm = { + errYZ * errYZ, + 0., errYZ * errYZ, + 0, 0., errSlp * errSlp, + 0., 0., 0., errSlp * errSlp, + 0., 0., 0., 0., errQPT * errQPT}; + bool accept = true; + TLorentzVector parent, d0, d1, d2; + do { + accept = true; + double y = gRandom->Rndm() - 0.5; + double pt = 0.1 + gRandom->Rndm() * 3; + double mt = TMath::Sqrt(parMass * parMass + pt * pt); + double pz = mt * TMath::SinH(y); + double phi = gRandom->Rndm() * TMath::Pi() * 2; + double en = mt * TMath::CosH(y); + double rdec = 10.; // radius of the decay + vtx[0] = rdec * TMath::Cos(phi); + vtx[1] = rdec * TMath::Sin(phi); + vtx[2] = rdec * pz / pt; + parent.SetPxPyPzE(pt * TMath::Cos(phi), pt * TMath::Sin(phi), pz, en); + int nd = dtMass.size(); + genPHS.SetDecay(parent, nd, dtMass.data()); + genPHS.Generate(); + vctr.clear(); + float p[4]; + for (int i = 0; i < nd; i++) { + auto* dt = genPHS.GetDecay(i); + if (dt->Pt() < 0.05) { + accept = false; + break; + } + dt->GetXYZT(p); + float s, c, x; + std::array params; + o2::math_utils::sincos(dt->Phi(), s, c); + o2::math_utils::rotateZInv(vtx[0], vtx[1], x, params[0], s, c); + + params[1] = vtx[2]; + params[2] = 0.; // since alpha = phi + params[3] = 1. / TMath::Tan(dt->Theta()); + params[4] = (i % 2 ? -1. : 1.) / dt->Pt(); + covm[14] = errQPT * errQPT * params[4] * params[4]; + // + // randomize + float r1, r2; + gRandom->Rannor(r1, r2); + params[0] += r1 * errYZ; + params[1] += r2 * errYZ; + gRandom->Rannor(r1, r2); + params[2] += r1 * errSlp; + params[3] += r2 * errSlp; + params[4] *= gRandom->Gaus(1., errQPT); + if (forceQ[i] == 0) { + params[4] = 0.; // impose straight track + } + auto& trc = vctr.emplace_back(x, dt->Phi(), params, covm); + float rad = forceQ[i] == 0 ? 600. : TMath::Abs(1. / trc.getCurvature(bz)); + if (!trc.propagateTo(trc.getX() + (gRandom->Rndm() - 0.5) * rad * 0.05, bz) || + !trc.rotate(trc.getAlpha() + (gRandom->Rndm() - 0.5) * 0.2)) { + printf("Failed to randomize "); + trc.print(); + } + } + } while (!accept); + + return parent; +} + +#ifdef DO_SINGLE_THREAD_TEST +BOOST_AUTO_TEST_CASE(DCAFitterNProngs) +{ + // gRandom->Delete(); + // gRandom = new TRandom(42); + o2::utils::TreeStreamRedirector outStream("dcafitterNTest.root"); + + TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; + constexpr double pion = 0.13957; + constexpr double k0 = 0.49761; + constexpr double kch = 0.49368; + constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; + std::vector k0dec = {pion, pion}; + std::vector dchdec = {pion, kch, pion}; + std::vector vctracks; + Vec3D vtxGen; + + double bz = 5.0; + // 2 prongs vertices + { + LOG(info) << "Processing 2-prong Helix - Helix case"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2a", treeName2AW = "pr2aw", treeName2W = "pr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundA : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 2 prongs vertices with collinear tracks (gamma conversion) + { + LOG(info) << "Processing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "gpr2a", treeName2AW = "gpr2aw", treeName2W = "gpr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, gamma, gammadec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, gammadec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, gammadec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, gammadec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundA ? nfoundA : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix from gamma conversion"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 2.1); + BOOST_CHECK(meanDAW < 2.1); + BOOST_CHECK(meanDW < 2.1); + } + + // 2 prongs vertices with one of charges set to 0: Helix : Line + { + std::vector forceQ{1, 1}; + LOG(info) << "Processing 2-prong Helix - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2aHL", treeName2AW = "pr2awHL", treeName2W = "pr2wHL"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[iev % 2] = 1; + forceQ[1 - iev % 2] = 0; + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); + swA.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices: Helix : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 2 prongs vertices with both of charges set to 0: Line : Line + { + std::vector forceQ{0, 0}; + LOG(info) << "Processing 2-prong Line - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName2A = "pr2aLL", treeName2AW = "pr2awLL", treeName2W = "pr2wLL"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[0] = forceQ[1] = 0; + auto genParent = generate(vtxGen, vctracks, bz, genPHS, k0, k0dec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, k0dec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, k0dec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, k0dec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices: Line : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + // 3 prongs vertices + { + std::vector forceQ{1, 1, 1}; + + o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::string treeName3A = "pr3a", treeName3AW = "pr3aw", treeName3W = "pr3w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, dch, dchdec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName3A, ft, vtxGen, genParent, dchdec); + meanDA += minD; + nfoundA++; + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName3AW, ft, vtxGen, genParent, dchdec); + meanDAW += minD; + nfoundAW++; + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = device::process(1, 1, ft, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName3W, ft, vtxGen, genParent, dchdec); + meanDW += minD; + nfoundW++; + } + } + + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 3-prong vertices"; + LOG(info) << "3-prongs with abs.dist minimization: eff = " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " Total time: " << swA.CpuTime(); + LOG(info) << "3-prongs with abs.dist but wghPCA: eff = " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " Total time: " << swAW.CpuTime(); + LOG(info) << "3-prongs with wgh.dist minimization: eff = " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " Total time: " << swW.CpuTime(); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 0.1); + BOOST_CHECK(meanDAW < 0.1); + BOOST_CHECK(meanDW < 0.1); + } + + outStream.Close(); +} +#endif + +BOOST_AUTO_TEST_CASE(DCAFitterNProngsBulk) +{ + const char* nThreadsEnvVarName = "DCAFITTERGPU_TEST_NTHREADS"; + const char* nBlocksEnvVarName = "DCAFITTERGPU_TEST_NBLOCKS"; + const char* nBatchesEnvVarName = "DCAFITTERGPU_TEST_NBATCHES"; + const char* nTestsEnvVarName = "DCAFITTERGPU_TEST_NTESTS"; + int nBlocks = std::getenv(nBlocksEnvVarName) == nullptr ? 30 : std::stoi(std::getenv(nBlocksEnvVarName)); + int nThreads = std::getenv(nThreadsEnvVarName) == nullptr ? 256 : std::stoi(std::getenv(nThreadsEnvVarName)); + int nBatches = std::getenv(nBatchesEnvVarName) == nullptr ? 8 : std::stoi(std::getenv(nBatchesEnvVarName)); + int NTest = std::getenv(nTestsEnvVarName) == nullptr ? 100001 : std::stoi(std::getenv(nTestsEnvVarName)); + + o2::utils::TreeStreamRedirector outStreamB("dcafitterNTestBulk.root"); + + TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; + constexpr double pion = 0.13957; + constexpr double k0 = 0.49761; + constexpr double kch = 0.49368; + constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; + std::vector k0dec = {pion, pion}; + std::vector dchdec = {pion, kch, pion}; + std::vector> vctracks(3, std::vector(NTest)); + std::vector vtxGen(NTest); + + double bz = 5.0; + { // 2 prongs vertices bulk processing + LOG(info) << "\n\nBulk-processing 2-prong Helix - Helix case"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aBulk", treeName2AWbulk = "pr2awBulk", treeName2Wbulk = "pr2wBulk"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + // + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOGP(info, "Bulk-processed {} 2-prong vertices Helix : Helix", NTest); + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + { // 2 prongs vertices bulk processing for gamma conversion + LOG(info) << "\n\nBulk-processing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "gpr2aBulk", treeName2AWbulk = "gpr2awBulk", treeName2Wbulk = "gpr2wBulk"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, gamma, gammadec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDAb += minDb; + nfoundAb++; + } + } + // + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], gammadec); + meanDWb += minDb; + nfoundWb++; + } + } + // + + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOGP(info, "Bulk-processed {} 2-prong vertices Helix : Helix from gamma conversion", NTest); + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 2.1); + BOOST_CHECK(meanDAWb < 2.1); + BOOST_CHECK(meanDWb < 2.1); + } + + // 2 prongs vertices bulk processing with one of charges set to 0: Helix : Line + { + std::vector forceQ{1, 1}; + LOG(info) << "\n\nBulk-processing 2-prong Helix - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aHLb", treeName2AWbulk = "pr2awHLb", treeName2Wbulk = "pr2wHLb"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + forceQ[iev % 2] = 1; + forceQ[1 - iev % 2] = 0; + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + + // + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 2-prong vertices: Helix : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + // 2 prongs vertices with both of charges set to 0: Line : Line + { + std::vector forceQ{0, 0}; + LOG(info) << "\n\nBulk-processing 2-prong Line - Line case"; + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName2Abulk = "pr2aLL", treeName2AWbulk = "pr2awLL", treeName2Wbulk = "pr2wLL"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + forceQ[0] = forceQ[1] = 0; + std::vector vc(2); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, k0, k0dec, forceQ); + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + } + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swWb.Stop(); + + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName2Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], k0dec); + meanDWb += minDb; + nfoundWb++; + } + } + // ft.print(); + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 2-prong vertices: Line : Line"; + LOG(info) << "2-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + + // Bulk-process 3 prongs vertices + { + LOG(info) << "\n\nBulk-processing 3-prongs"; + std::vector forceQ{1, 1, 1}; + + o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + + std::vector> fitters_host(NTest); + std::vector genParents(NTest); + + std::string treeName3Abulk = "pr3a", treeName3AWbulk = "pr3aw", treeName3Wbulk = "pr3w"; + TStopwatch swAb, swAWb, swWb; + int nfoundAb = 0, nfoundAWb = 0, nfoundWb = 0; + double meanDAb = 0, meanDAWb = 0, meanDWb = 0; + swAb.Stop(); + swAWb.Stop(); + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + std::vector vc(3); + genParents[iev] = generate(vtxGen[iev], vc, bz, genPHS, dch, dchdec, forceQ); + + vctracks[0][iev] = vc[0]; + vctracks[1][iev] = vc[1]; + vctracks[2][iev] = vc[2]; + } + + ft.setUseAbsDCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + swAb.Start(false); + std::vector ncAb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAb[iev] << " Chi2: " << (ncAb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAb[iev]) { + auto minDb = checkResults(outStreamB, treeName3Abulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDAb += minDb; + nfoundAb++; + } + } + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swAWb.Start(false); + std::vector ncAWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncAWb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swAWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncAWb[iev] << " Chi2: " << (ncAWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncAWb[iev]) { + auto minDb = checkResults(outStreamB, treeName3AWbulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDAWb += minDb; + nfoundAWb++; + } + } + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + std::fill(fitters_host.begin(), fitters_host.end(), ft); + + swWb.Start(false); + std::vector ncWb(NTest, 0); + device::processBulk(nBlocks, nThreads, nBatches, fitters_host, ncWb, vctracks[0], vctracks[1], vctracks[2]); // HERE WE FIT THE VERTICES + swWb.Stop(); + for (int iev = 0; iev < NTest; iev++) { + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncWb[iev] << " Chi2: " << (ncWb[iev] ? fitters_host[iev].getChi2AtPCACandidate(0) : -1); + if (ncWb[iev]) { + auto minDb = checkResults(outStreamB, treeName3Wbulk, fitters_host[iev], vtxGen[iev], genParents[iev], dchdec); + meanDWb += minDb; + nfoundWb++; + } + } + + // ft.print(); + meanDAb /= nfoundAb ? nfoundAb : 1; + meanDAWb /= nfoundAWb ? nfoundAWb : 1; + meanDWb /= nfoundWb ? nfoundWb : 1; + LOG(info) << "Bulk-processed " << NTest << " 3-prong vertices"; + LOG(info) << "3-prongs with abs.dist minimization: eff = " << float(nfoundAb) / NTest + << " mean.dist to truth: " << meanDAb << " Total time: " << swAb.CpuTime() * 1000 << " ms"; + LOG(info) << "3-prongs with abs.dist but wghPCA: eff = " << float(nfoundAWb) / NTest + << " mean.dist to truth: " << meanDAWb << " Total time: " << swAWb.CpuTime() * 1000 << " ms"; + LOG(info) << "3-prongs with wgh.dist minimization: eff = " << float(nfoundWb) / NTest + << " mean.dist to truth: " << meanDWb << " Total time: " << swWb.CpuTime() * 1000 << " ms"; + BOOST_CHECK(nfoundAb > 0.99 * NTest); + BOOST_CHECK(nfoundAWb > 0.99 * NTest); + BOOST_CHECK(nfoundWb > 0.99 * NTest); + BOOST_CHECK(meanDAb < 0.1); + BOOST_CHECK(meanDAWb < 0.1); + BOOST_CHECK(meanDWb < 0.1); + } + outStreamB.Close(); +} + +} // namespace vertexing +} // namespace o2 \ No newline at end of file diff --git a/Common/DCAFitter/GPU/hip/CMakeLists.txt b/Common/DCAFitter/GPU/hip/CMakeLists.txt new file mode 100644 index 0000000000000..5e7821a0b8946 --- /dev/null +++ b/Common/DCAFitter/GPU/hip/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -fgpu-rdc") +o2_add_hipified_library(DCAFitterHIP + SOURCES ../cuda/DCAFitterN.cu + ../cuda/GPUInterface.cu + PUBLIC_INCLUDE_DIRECTORIES ../../include ../ + PUBLIC_LINK_LIBRARIES O2::MathUtils + O2::ReconstructionDataFormats + O2::DetectorsBase + hip::host + PRIVATE_LINK_LIBRARIES O2::GPUTrackingHIPExternalProvider + TARGETVARNAME targetNAme) + +#o2_add_test(DCAFitterNHIP +# SOURCES ../cuda/test/testDCAFitterNGPU.cxx +# PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats +# O2::DCAFitterHIP +# O2::DCAFitter +# ROOT::Core +# ROOT::Physics +# HIPIFIED test +# COMPONENT_NAME gpu +# LABELS vertexing +# ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage +# VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) diff --git a/Common/DCAFitter/README.md b/Common/DCAFitter/README.md index bbb079d144748..e385378d10caf 100644 --- a/Common/DCAFitter/README.md +++ b/Common/DCAFitter/README.md @@ -2,7 +2,7 @@ \page refDetectorsVertexing DCAFitter /doxy --> -## DCAFitterN +# DCAFitterN Templated class to fit the Point of Closest Approach (PCA) of secondary vertex with N prongs. Allows minimization of either absolute or weighted Distances of Closest Approach (DCA) of N tracks to their common PCA. @@ -74,7 +74,60 @@ Extra method `setWeightedFinalPCA(bool)` is provided for the "mixed" mode: if `s but the final V0 position will be calculated using weighted average. One can also recalculate the V0 position by the weighted average method by calling explicitly `ft.recalculatePCAWithErrors(int icand=0)`, w/o prior call of `setWeightedFinalPCA(true)`: this will update the position returned by the `getPCACandidate(int cand = 0)`. -The covariance matrix of the V0 position is calculated as an inversed sum of tracks inversed covariances at respective `X_dca` points. +The covariance matrix of the V0 position is calculated as an inverted sum of tracks inversed covariances at respective `X_dca` points. See ``O2/Common/DCAFitter/test/testDCAFitterN.cxx`` for more extended example. Currently only 2 and 3 prongs permitted, thought this can be changed by modifying ``DCAFitterN::NMax`` constant. + +## Error handling + +It may happen that the track propagation to the the proximity of the PCA fails at the various stage of the fit. In this case the fit is abandoned and the failure flag is set, it can be checked using +isPropagationFailure(int cand = 0)` method. + +Also, due to the linearization errors the covariance matrix of the track propagated to some point may become non-positive defined. +In this case the relevant correlation coefficient of the cov.matrix is redefined to cure the position part of the cov.matrix and further program flow depends on the user settings for `DCAFitterN::setBadCovPolicy(v)`: + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Discard);` : abandon fit (default) + +`DCAFitterN::setBadCovPolicy(DCAFitterN::Override);` : continue fit with overridden cov.matrix + +`DCAFitterN::setBadCovPolicy(DCAFitterN::OverrideAnFlag);` continue fit with overridden cov.matrix but set the propagation failure flag (can be checked using the same `isPropagationFailure(int cand = 0)` method). + +## Fit status +The fitter provides a fit status for each candidate, which can be retrieved using: +``` +FitStatus status = ft.getFitStatus(int cand = 0); +``` +The possible values are: +``` +enum FitStatus : uint8_t { // part of the DCAFitterN class + None, // no status set (should not be possible!) + + /* Good Conditions */ + Converged, // fit converged + MaxIter, // max iterations reached before fit convergence (can still be a good vertex) + + /* Error Conditions */ + NoCrossing, // no reasonable crossing was found + RejRadius, // radius of crossing was not acceptable + RejTrackX, // one candidate track x was below the minimum required radius + RejTrackRoughZ, // rejected by rough cut on tracks Z difference + RejChi2Max, // rejected by maximum chi2 cut + FailProp, // propagation of at least prong to PCA failed + FailInvCov, // inversion of cov.-matrix failed + FailInvWeight, // inversion of Ti weight matrix failed + FailInv2ndDeriv, // inversion of 2nd derivatives failed + FailCorrTracks, // correction of tracks to updated x failed + FailCloserAlt, // alternative PCA is closer +}; +``` +This is allows to track where candiate fit was abondended. +``` +int nc = ft.process(tr0,tr1,tr2); +auto status = ft.getFitStatus(); +if (nc) { + // status can either be FitStatus::Converged or FitStatus::MaxIter +} +// status can be on of the error conditions +``` +A more thorough example is given in `testDCAFitterN.cxx`. diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 70b5d820423d3..2641dec84aed9 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -16,40 +16,42 @@ #ifndef _ALICEO2_DCA_FITTERN_ #define _ALICEO2_DCA_FITTERN_ -#include + +#include "ReconstructionDataFormats/HelixHelper.h" +#include "DetectorsBase/Propagator.h" #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" -#include "DetectorsBase/Propagator.h" namespace o2 { namespace vertexing { + ///__________________________________________________________________________________ ///< Inverse cov matrix (augmented by a dummy X error) of the point defined by the track struct TrackCovI { float sxx, syy, syz, szz; - TrackCovI(const o2::track::TrackParCov& trc, float xerrFactor = 1.) { set(trc, xerrFactor); } + GPUdDefault() TrackCovI() = default; - TrackCovI() = default; - - void set(const o2::track::TrackParCov& trc, float xerrFactor = 1) + GPUd() bool set(const o2::track::TrackParCov& trc, float xerrFactor = 1.f) { // we assign Y error to X for DCA calculation // (otherwise for quazi-collinear tracks the X will not be constrained) float cyy = trc.getSigmaY2(), czz = trc.getSigmaZ2(), cyz = trc.getSigmaZY(), cxx = cyy * xerrFactor; float detYZ = cyy * czz - cyz * cyz; - if (detYZ > 0.) { - auto detYZI = 1. / detYZ; - sxx = 1. / cxx; - syy = czz * detYZI; - syz = -cyz * detYZI; - szz = cyy * detYZI; - } else { - throw std::runtime_error("invalid track covariance"); + bool res = true; + if (detYZ <= 0.) { + cyz = o2::gpu::GPUCommonMath::Sqrt(cyy * czz) * (cyz > 0 ? 0.98f : -0.98f); + detYZ = cyy * czz - cyz * cyz; + res = false; } + auto detYZI = 1. / detYZ; + sxx = 1. / cxx; + syy = czz * detYZI; + syz = -cyz * detYZI; + szz = cyy * detYZI; + return res; } }; @@ -57,11 +59,11 @@ struct TrackCovI { ///< Derivative (up to 2) of the TrackParam position over its running param X struct TrackDeriv { float dydx, dzdx, d2ydx2, d2zdx2; - TrackDeriv() = default; - TrackDeriv(const o2::track::TrackPar& trc, float bz) { set(trc, bz); } - void set(const o2::track::TrackPar& trc, float bz) + GPUdDefault() TrackDeriv() = default; + GPUd() TrackDeriv(const o2::track::TrackPar& trc, float bz) { set(trc, bz); } + GPUd() void set(const o2::track::TrackPar& trc, float bz) { - float snp = trc.getSnp(), csp = std::sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz) * cspI; + float snp = trc.getSnp(), csp = o2::gpu::GPUCommonMath::Sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz) * cspI; dydx = snp * cspI; // = snp/csp dzdx = trc.getTgl() * cspI; // = tgl/csp d2ydx2 = crv2c * cspI * cspI; // = crv/csp^3 @@ -69,6 +71,26 @@ struct TrackDeriv { } }; +///__________________________________________________________________________ +///< Log log-throttling helper +struct LogLogThrottler { + size_t evCount{0}; + size_t nextLog{1}; + GPUdi() bool needToLog() + { + if (++evCount > nextLog) { + nextLog *= 2; + return true; + } + return false; + } + GPUdi() void clear() + { + evCount = 0; + nextLog = 1; + } +}; + template class DCAFitterN { @@ -81,12 +103,12 @@ class DCAFitterN using TrackAuxPar = o2::track::TrackAuxPar; using CrossInfo = o2::track::CrossInfo; - using Vec3D = ROOT::Math::SVector; - using VecND = ROOT::Math::SVector; - using MatSym3D = ROOT::Math::SMatrix>; - using MatStd3D = ROOT::Math::SMatrix>; - using MatSymND = ROOT::Math::SMatrix>; - using MatStdND = ROOT::Math::SMatrix>; + using Vec3D = o2::math_utils::SVector; + using VecND = o2::math_utils::SVector; + using MatSym3D = o2::math_utils::SMatrix>; + using MatStd3D = o2::math_utils::SMatrix>; + using MatSymND = o2::math_utils::SMatrix>; + using MatStdND = o2::math_utils::SMatrix>; using TrackCoefVtx = MatStd3D; using ArrTrack = std::array; // container for prongs (tracks) at single vertex cand. using ArrTrackCovI = std::array; // container for inv.cov.matrices at single vertex cand. @@ -95,6 +117,35 @@ class DCAFitterN using ArrTrPos = std::array; // container of Track positions public: + enum BadCovPolicy : uint8_t { // if encountering non-positive defined cov. matrix, the choice is: + Discard = 0, // stop evaluation + Override = 1, // override correlation coef. to have cov.matrix pos.def and continue + OverrideAndFlag = 2 // override correlation coef. to have cov.matrix pos.def, set mPropFailed flag of corresponding candidate to true and continue (up to the user to check the flag) + }; + + enum FitStatus : uint8_t { // fit status of crossing hypothesis + None, // no status set (should not be possible!) + + /* Good Conditions */ + Converged, // fit converged + MaxIter, // max iterations reached before fit convergence + + /* Error Conditions */ + NoCrossing, // no reasaonable crossing was found + RejRadius, // radius of crossing was not acceptable + RejTrackX, // one candidate track x was below the mimimum required radius + RejTrackRoughZ, // rejected by rough cut on tracks Z difference + RejChi2Max, // rejected by maximum chi2 cut + FailProp, // propagation of at least prong to PCA failed + FailInvCov, // inversion of cov.-matrix failed + FailInvWeight, // inversion of Ti weight matrix failed + FailInv2ndDeriv, // inversion of 2nd derivatives failed + FailCorrTracks, // correction of tracks to updated x failed + FailCloserAlt, // alternative PCA is closer + // + NStatusesDefined + }; + static constexpr int getNProngs() { return N; } DCAFitterN() = default; @@ -105,29 +156,37 @@ class DCAFitterN //========================================================================= ///< return PCA candidate, by default best on is provided (no check for the index validity) - const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } - const auto getPCACandidatePos(int cand = 0) const + GPUd() const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } + GPUd() const auto getPCACandidatePos(int cand = 0) const { const auto& vd = mPCA[mOrder[cand]]; - return std::array{float(vd[0]), float(vd[1]), float(vd[2])}; + return std::array{static_cast(vd[0]), static_cast(vd[1]), static_cast(vd[2])}; } + ///< return position of quality-ordered candidate in the internal structures + int getCandidatePosition(int cand = 0) const { return mOrder[cand]; } + ///< return Chi2 at PCA candidate (no check for its validity) float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } ///< prepare copies of tracks at the V0 candidate (no check for the candidate validity) /// must be called before getTrack(i,cand) query - bool propagateTracksToVertex(int cand = 0); + GPUd() bool propagateTracksToVertex(int cand = 0); ///< check if propagation of tracks to candidate vertex was done - bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } + GPUd() bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } + + ///< check if propagation of tracks to candidate vertex failed + bool isPropagationFailure(int cand = 0) const { return mPropFailed[mOrder[cand]]; } ///< track param propagated to V0 candidate (no check for the candidate validity) /// propagateTracksToVertex must be called in advance Track& getTrack(int i, int cand = 0) { if (!mTrPropDone[mOrder[cand]]) { +#ifndef GPUCA_GPUCODE_DEVICE throw std::runtime_error("propagateTracksToVertex was not called yet"); +#endif } return mCandTr[mOrder[cand]][i]; } @@ -135,107 +194,116 @@ class DCAFitterN const Track& getTrack(int i, int cand = 0) const { if (!mTrPropDone[mOrder[cand]]) { +#ifndef GPUCA_GPUCODE_DEVICE throw std::runtime_error("propagateTracksToVertex was not called yet"); +#endif } return mCandTr[mOrder[cand]][i]; } ///< create parent track param with errors for decay vertex - o2::track::TrackParCov createParentTrackParCov(int cand = 0, bool sectorAlpha = true) const; + GPUd() o2::track::TrackParCov createParentTrackParCov(int cand = 0, bool sectorAlpha = true) const; ///< create parent track param w/o errors for decay vertex - o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const; + GPUd() o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const; ///< calculate on the fly track param (no cov mat) at candidate, check isValid to make sure propagation was successful - o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0) const; + GPUd() o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0); ///< recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used - bool recalculatePCAWithErrors(int cand = 0); + GPUd() bool recalculatePCAWithErrors(int cand = 0); - MatSym3D calcPCACovMatrix(int cand = 0) const; + GPUd() MatSym3D calcPCACovMatrix(int cand = 0) const; std::array calcPCACovMatrixFlat(int cand = 0) const { auto m = calcPCACovMatrix(cand); - return {float(m(0, 0)), float(m(1, 0)), float(m(1, 1)), float(m(2, 0)), float(m(2, 1)), float(m(2, 2))}; + return {static_cast(m(0, 0)), static_cast(m(1, 0)), static_cast(m(1, 1)), static_cast(m(2, 0)), static_cast(m(2, 1)), static_cast(m(2, 2))}; } const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } + GPUdi() FitStatus getFitStatus(int cand = 0) const noexcept { return mFitStatus[mOrder[cand]]; } + ///< return number of iterations during minimization (no check for its validity) - int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } - void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } - void setMaxIter(int n = 20) { mMaxIter = n > 2 ? n : 2; } - void setMaxR(float r = 200.) { mMaxR2 = r * r; } - void setMaxDZIni(float d = 4.) { mMaxDZIni = d; } - void setMaxDXYIni(float d = 4.) { mMaxDXYIni = d > 0 ? d : 1e9; } - void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } - void setBz(float bz) { mBz = std::abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } - void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } - void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } - void setUseAbsDCA(bool v) { mUseAbsDCA = v; } - void setWeightedFinalPCA(bool v) { mWeightedFinalPCA = v; } - void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } - void setMatCorrType(o2::base::Propagator::MatCorrType m = o2::base::Propagator::MatCorrType::USEMatCorrLUT) { mMatCorr = m; } - void setUsePropagator(bool v) { mUsePropagator = v; } - void setRefitWithMatCorr(bool v) { mRefitWithMatCorr = v; } - void setMaxSnp(float s) { mMaxSnp = s; } - void setMaxStep(float s) { mMaxStep = s; } - void setMinXSeed(float x) { mMinXSeed = x; } - - int getNCandidates() const { return mCurHyp; } - int getMaxIter() const { return mMaxIter; } - float getMaxR() const { return std::sqrt(mMaxR2); } - float getMaxDZIni() const { return mMaxDZIni; } - float getMaxDXYIni() const { return mMaxDXYIni; } - float getMaxChi2() const { return mMaxChi2; } - float getMinParamChange() const { return mMinParamChange; } - float getBz() const { return mBz; } - float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } - bool getUseAbsDCA() const { return mUseAbsDCA; } - bool getWeightedFinalPCA() const { return mWeightedFinalPCA; } - bool getPropagateToPCA() const { return mPropagateToPCA; } - o2::base::Propagator::MatCorrType getMatCorrType() const { return mMatCorr; } - bool getUsePropagator() const { return mUsePropagator; } - bool getRefitWithMatCorr() const { return mRefitWithMatCorr; } - float getMaxSnp() const { return mMaxSnp; } - float getMasStep() const { return mMaxStep; } - float getMinXSeed() const { return mMinXSeed; } + GPUdi() int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } + GPUdi() void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } + GPUdi() void setMaxIter(int n = 20) { mMaxIter = n > 2 ? n : 2; } + GPUdi() void setMaxR(float r = 200.) { mMaxR2 = r * r; } + GPUdi() void setMaxDZIni(float d = 4.) { mMaxDZIni = d; } + GPUdi() void setMaxDXYIni(float d = 4.) { mMaxDXYIni = d > 0 ? d : 1e9; } + GPUdi() void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } + GPUdi() void setBz(float bz) { mBz = o2::gpu::GPUCommonMath::Abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } + GPUdi() void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } + GPUdi() void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } + GPUdi() void setUseAbsDCA(bool v) { mUseAbsDCA = v; } + GPUdi() void setWeightedFinalPCA(bool v) { mWeightedFinalPCA = v; } + GPUdi() void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } + GPUdi() void setMatCorrType(o2::base::Propagator::MatCorrType m = o2::base::Propagator::MatCorrType::USEMatCorrLUT) { mMatCorr = m; } + GPUdi() void setUsePropagator(bool v) { mUsePropagator = v; } + GPUdi() void setRefitWithMatCorr(bool v) { mRefitWithMatCorr = v; } + GPUdi() void setMaxSnp(float s) { mMaxSnp = s; } + GPUdi() void setMaxStep(float s) { mMaxStep = s; } + GPUdi() void setMinXSeed(float x) { mMinXSeed = x; } + GPUdi() void setCollinear(bool isCollinear) { mIsCollinear = isCollinear; } + + GPUdi() int getNCandidates() const { return mCurHyp; } + GPUdi() int getMaxIter() const { return mMaxIter; } + GPUdi() float getMaxR() const { return o2::gpu::GPUCommonMath::Sqrt(mMaxR2); } + GPUdi() float getMaxDZIni() const { return mMaxDZIni; } + GPUdi() float getMaxDXYIni() const { return mMaxDXYIni; } + GPUdi() float getMaxChi2() const { return mMaxChi2; } + GPUdi() float getMinParamChange() const { return mMinParamChange; } + GPUdi() float getBz() const { return mBz; } + GPUdi() float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } + GPUdi() bool getUseAbsDCA() const { return mUseAbsDCA; } + GPUdi() bool getWeightedFinalPCA() const { return mWeightedFinalPCA; } + GPUdi() bool getPropagateToPCA() const { return mPropagateToPCA; } + GPUdi() o2::base::Propagator::MatCorrType getMatCorrType() const { return mMatCorr; } + GPUdi() bool getUsePropagator() const { return mUsePropagator; } + GPUdi() bool getRefitWithMatCorr() const { return mRefitWithMatCorr; } + GPUdi() float getMaxSnp() const { return mMaxSnp; } + GPUdi() float getMasStep() const { return mMaxStep; } + GPUdi() float getMinXSeed() const { return mMinXSeed; } template - int process(const Tr&... args); - void print() const; + GPUd() int process(const Tr&... args); + GPUd() void print() const; + + GPUdi() int getFitterID() const { return mFitterID; } + GPUdi() void setFitterID(int i) { mFitterID = i; } + GPUdi() size_t getCallID() const { return mCallID; } protected: - bool calcPCACoefs(); - bool calcInverseWeight(); - void calcResidDerivatives(); - void calcResidDerivativesNoErr(); - void calcRMatrices(); - void calcChi2Derivatives(); - void calcChi2DerivativesNoErr(); - void calcPCA(); - void calcPCANoErr(); - void calcTrackResiduals(); - void calcTrackDerivatives(); - double calcChi2() const; - double calcChi2NoErr() const; - bool correctTracks(const VecND& corrX); - bool minimizeChi2(); - bool minimizeChi2NoErr(); - bool roughDZCut() const; - bool closerToAlternative() const; - bool propagateToX(o2::track::TrackParCov& t, float x) const; - bool propagateParamToX(o2::track::TrackPar& t, float x) const; - - static double getAbsMax(const VecND& v); + GPUd() bool calcPCACoefs(); + GPUd() bool calcInverseWeight(); + GPUd() void calcResidDerivatives(); + GPUd() void calcResidDerivativesNoErr(); + GPUd() void calcRMatrices(); + GPUd() void calcChi2Derivatives(); + GPUd() void calcChi2DerivativesNoErr(); + GPUd() void calcPCA(); + GPUd() void calcPCANoErr(); + GPUd() void calcTrackResiduals(); + GPUd() void calcTrackDerivatives(); + GPUd() double calcChi2() const; + GPUd() double calcChi2NoErr() const; + GPUd() bool correctTracks(const VecND& corrX); + GPUd() bool minimizeChi2(); + GPUd() bool minimizeChi2NoErr(); + GPUd() bool roughDZCut() const; + GPUd() bool closerToAlternative() const; + GPUd() bool propagateToX(o2::track::TrackParCov& t, float x); + GPUd() bool propagateParamToX(o2::track::TrackPar& t, float x); + + GPUd() static double getAbsMax(const VecND& v); ///< track param positions at V0 candidate (no check for the candidate validity) - const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } + GPUdi() const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } ///< track X-param at V0 candidate (no check for the candidate validity) - float getTrackX(int i, int cand = 0) const { return getTrackPos(i, cand)[0]; } + GPUd() float getTrackX(int i, int cand = 0) const { return getTrackPos(i, cand)[0]; } - MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame + GPUd() MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame { MatStd3D mat; mat(2, 2) = 1; @@ -245,7 +313,7 @@ class DCAFitterN return mat; } - MatSym3D getTrackCovMatrix(int i, int cand = 0) const // generate covariance matrix of track position, adding fake X error + GPUd() 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; @@ -256,28 +324,46 @@ class DCAFitterN return mat; } - void assign(int) {} + GPUd() void assign(int) {} template - void assign(int i, const T& t, const Tr&... args) + GPUd() void assign(int i, const T& t, const Tr&... args) { +#ifndef GPUCA_GPUCODE_DEVICE static_assert(std::is_convertible(), "Wrong track type"); +#endif mOrigTrPtr[i] = &t; assign(i + 1, args...); } - void clear() + GPUdi() void clear() { mCurHyp = 0; mAllowAltPreference = true; + mOrder.fill(0); + mPropFailed.fill(false); + mTrPropDone.fill(false); + mNIters.fill(0); + mChi2.fill(-1); + mFitStatus.fill(FitStatus::None); } - static void setTrackPos(Vec3D& pnt, const Track& tr) + GPUdi() static void setTrackPos(Vec3D& pnt, const Track& tr) { pnt[0] = tr.getX(); pnt[1] = tr.getY(); pnt[2] = tr.getZ(); } + GPUdi() void clearLogThrottlers() + { + mLoggerBadCov.clear(); + mLoggerBadInv.clear(); + mLoggerBadProp.clear(); + } + + void setBadCovPolicy(BadCovPolicy v) { mBadCovPolicy = v; } + BadCovPolicy getBadCovPolicy() const { return mBadCovPolicy; } + private: // vectors of 1st derivatives of track local residuals over X parameters std::array, N> mDResidDx; @@ -301,18 +387,25 @@ class DCAFitterN std::array mPCA; // PCA for each vertex candidate std::array mChi2 = {0}; // Chi2 at PCA candidate std::array mNIters; // number of iterations for each seed - std::array mTrPropDone; // Flag that the tracks are fully propagated to PCA - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T + std::array mTrPropDone{}; // Flag that the tracks are fully propagated to PCA + std::array mPropFailed{}; // Flag that some propagation failed for this PCA candidate + LogLogThrottler mLoggerBadCov{}; + LogLogThrottler mLoggerBadInv{}; + LogLogThrottler mLoggerBadProp{}; + MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T std::array mOrder{0}; int mCurHyp = 0; int mCrossIDCur = 0; int mCrossIDAlt = -1; + BadCovPolicy mBadCovPolicy{BadCovPolicy::Discard}; // what to do in case of non-pos-def. cov. matrix, see BadCovPolicy enum + std::array mFitStatus{}; // fit status of each hypothesis fit bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 bool mWeightedFinalPCA = false; // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used bool mPropagateToPCA = true; // create tracks version propagated to PCA bool mUsePropagator = false; // use propagator with 3D B-field, set automatically if material correction is requested bool mRefitWithMatCorr = false; // when doing propagateTracksToVertex, propagate tracks to V0 with material corrections and rerun minimization again + bool mIsCollinear = false; // use collinear fits when there 2 crossing points o2::base::Propagator::MatCorrType mMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // material corrections type int mMaxIter = 20; // max number of iterations float mBz = 0; // bz field, to be set by user @@ -326,24 +419,27 @@ class DCAFitterN float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold float mMaxSnp = 0.95; // Max snp for propagation with Propagator float mMaxStep = 2.0; // Max step for propagation with Propagator - - ClassDefNV(DCAFitterN, 1); + int mFitterID = 0; // locat fitter ID (mostly for debugging) + size_t mCallID = 0; + ClassDefNV(DCAFitterN, 3); }; ///_________________________________________________________________________ template template -int DCAFitterN::process(const Tr&... args) +GPUd() int DCAFitterN::process(const Tr&... args) { // This is a main entry point: fit PCA of N tracks + mCallID++; static_assert(sizeof...(args) == N, "incorrect number of input tracks"); assign(0, args...); clear(); for (int i = 0; i < N; i++) { mTrAux[i].set(*mOrigTrPtr[i], mBz); } - if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni)) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing + if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni, mIsCollinear)) { // even for N>2 it should be enough to test just 1 loop + mFitStatus[mCurHyp] = FitStatus::NoCrossing; + return 0; } if (mUseAbsDCA) { calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization @@ -361,13 +457,11 @@ int DCAFitterN::process(const Tr&... args) for (int ic = 0; ic < mCrossings.nDCA; ic++) { // check if radius is acceptable if (mCrossings.xDCA[ic] * mCrossings.xDCA[ic] + mCrossings.yDCA[ic] * mCrossings.yDCA[ic] > mMaxR2) { + mFitStatus[mCurHyp] = FitStatus::RejRadius; continue; } mCrossIDCur = ic; mCrossIDAlt = (mCrossings.nDCA == 2 && mAllowAltPreference) ? 1 - ic : -1; // works for max 2 crossings - mNIters[mCurHyp] = 0; - mTrPropDone[mCurHyp] = false; - mChi2[mCurHyp] = -1.; mPCA[mCurHyp][0] = mCrossings.xDCA[ic]; mPCA[mCurHyp][1] = mCrossings.yDCA[ic]; @@ -383,7 +477,7 @@ int DCAFitterN::process(const Tr&... args) for (int i = mCurHyp; i--;) { // order in quality for (int j = i; j--;) { if (mChi2[mOrder[i]] < mChi2[mOrder[j]]) { - std::swap(mOrder[i], mOrder[j]); + o2::gpu::GPUCommonMath::Swap(mOrder[i], mOrder[j]); } } } @@ -392,16 +486,16 @@ int DCAFitterN::process(const Tr&... args) recalculatePCAWithErrors(i); } } - return mCurHyp; } //__________________________________________________________________________ template -bool DCAFitterN::calcPCACoefs() +GPUd() bool DCAFitterN::calcPCACoefs() { //< calculate Ti matrices for global vertex decomposition to V = sum_{0::calcPCACoefs() miei[1][0] = taux.s * tcov.sxx; miei[1][1] = taux.c * tcov.syy; miei[1][2] = taux.c * tcov.syz; - // miei[2][0] = 0; + miei[2][0] = 0; miei[2][1] = tcov.syz; miei[2][2] = tcov.szz; mTrCFVT[mCurHyp][i] = mWeightInv * miei; @@ -424,7 +518,7 @@ bool DCAFitterN::calcPCACoefs() //__________________________________________________________________________ template -bool DCAFitterN::calcInverseWeight() +GPUd() bool DCAFitterN::calcInverseWeight() { //< calculate [sum_{0::calcInverseWeight() //__________________________________________________________________________ template -void DCAFitterN::calcResidDerivatives() +GPUd() void DCAFitterN::calcResidDerivatives() { //< calculate matrix of derivatives for weighted chi2: residual i vs parameter X of track j MatStd3D matMT; @@ -492,12 +586,12 @@ void DCAFitterN::calcResidDerivatives() dr2[2] += trDx.d2zdx2; } } // track over which we differentiate - } // residual being differentiated + } // residual being differentiated } //__________________________________________________________________________ template -void DCAFitterN::calcResidDerivativesNoErr() +GPUd() void DCAFitterN::calcResidDerivativesNoErr() { //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter X of track j constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv @@ -542,12 +636,12 @@ void DCAFitterN::calcResidDerivativesNoErr() dr2ji[2] = -trDxi.d2zdx2 * NInv; } // track over which we differentiate - } // residual being differentiated + } // residual being differentiated } //__________________________________________________________________________ template -void DCAFitterN::calcRMatrices() +GPUd() void DCAFitterN::calcRMatrices() { //< calculate Rij = 1/N M_i^T * M_j matrices (rotation from j-th track to i-th track frame) for (int i = N; i--;) { @@ -562,7 +656,7 @@ void DCAFitterN::calcRMatrices() //__________________________________________________________________________ template -void DCAFitterN::calcChi2Derivatives() +GPUd() void DCAFitterN::calcChi2Derivatives() { //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters X, see EQ.Chi2 in the ref std::array, N> covIDrDx; // tempory vectors of covI_j * dres_j/dx_i @@ -580,7 +674,7 @@ void DCAFitterN::calcChi2Derivatives() cidr[1] = covI.syy * dr1[1] + covI.syz * dr1[2]; cidr[2] = covI.syz * dr1[1] + covI.szz * dr1[2]; // calculate res_i * covI_j * dres_j/dx_i - dchi1 += ROOT::Math::Dot(res, cidr); + dchi1 += o2::math_utils::Dot(res, cidr); } } // chi2 2nd derivative @@ -591,7 +685,7 @@ void DCAFitterN::calcChi2Derivatives() for (int k = N; k--;) { const auto& dr1j = mDResidDx[k][j]; // vector of k-th residuals 1st derivative over X param of track j const auto& cidrkj = covIDrDx[i][k]; // vector covI_k * dres_k/dx_i - dchi2 += ROOT::Math::Dot(dr1j, cidrkj); + dchi2 += o2::math_utils::Dot(dr1j, cidrkj); if (k == j) { const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k const auto& covI = mTrcEInv[mCurHyp][k]; // inverse cov matrix of track k @@ -605,7 +699,7 @@ void DCAFitterN::calcChi2Derivatives() //__________________________________________________________________________ template -void DCAFitterN::calcChi2DerivativesNoErr() +GPUd() void DCAFitterN::calcChi2DerivativesNoErr() { //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters X, see (6) in the ref for (int i = N; i--;) { @@ -614,13 +708,13 @@ void DCAFitterN::calcChi2DerivativesNoErr() for (int j = N; j--;) { const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j const auto& dr1 = mDResidDx[j][i]; // vector of j-th residuals 1st derivative over X param of track i - dchi1 += ROOT::Math::Dot(res, dr1); + dchi1 += o2::math_utils::Dot(res, dr1); if (i >= j) { // symmetrix matrix // chi2 2nd derivative auto& dchi2 = mD2Chi2Dx2[i][j]; // D2Chi2/Dx_i/Dx_j = sum_k { Dres_k/Dx_j * covI_k * Dres_k/Dx_i + res_k * covI_k * D2res_k/Dx_i/Dx_j } - dchi2 = ROOT::Math::Dot(mTrRes[mCurHyp][i], mD2ResidDx2[i][j]); + dchi2 = o2::math_utils::Dot(mTrRes[mCurHyp][i], mD2ResidDx2[i][j]); for (int k = N; k--;) { - dchi2 += ROOT::Math::Dot(mDResidDx[k][i], mDResidDx[k][j]); + dchi2 += o2::math_utils::Dot(mDResidDx[k][i], mDResidDx[k][j]); } } } @@ -629,7 +723,7 @@ void DCAFitterN::calcChi2DerivativesNoErr() //___________________________________________________________________ template -void DCAFitterN::calcPCA() +GPUd() void DCAFitterN::calcPCA() { // calculate point of closest approach for N prongs mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1]; @@ -640,7 +734,7 @@ void DCAFitterN::calcPCA() //___________________________________________________________________ template -bool DCAFitterN::recalculatePCAWithErrors(int cand) +GPUd() bool DCAFitterN::recalculatePCAWithErrors(int cand) { // recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used if (isPropagateTracksToVertexDone(cand) && !propagateTracksToVertex(cand)) { @@ -650,7 +744,23 @@ bool DCAFitterN::recalculatePCAWithErrors(int cand) mCurHyp = mOrder[cand]; if (mUseAbsDCA) { for (int i = N; i--;) { - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor)) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + } + mFitStatus[mCurHyp] = FitStatus::FailInvCov; + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (!calcPCACoefs()) { mCurHyp = saveCurHyp; @@ -665,7 +775,7 @@ bool DCAFitterN::recalculatePCAWithErrors(int cand) //___________________________________________________________________ template -void DCAFitterN::calcPCANoErr() +GPUd() void DCAFitterN::calcPCANoErr() { // calculate point of closest approach for N prongs w/o errors auto& pca = mPCA[mCurHyp]; @@ -687,15 +797,15 @@ void DCAFitterN::calcPCANoErr() //___________________________________________________________________ template -ROOT::Math::SMatrix> DCAFitterN::calcPCACovMatrix(int cand) const +GPUd() o2::math_utils::SMatrix> DCAFitterN::calcPCACovMatrix(int cand) const { // calculate covariance matrix for the point of closest approach MatSym3D covm; int nAdded = 0; for (int i = N; i--;) { // calculate sum of inverses - // MatSym3D covTr = ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); + // MatSym3D covTr = o2::math_utils::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); // RS by using Similarity(mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)) we underestimate the error, use simple rotation - MatSym3D covTr = ROOT::Math::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand)); + MatSym3D covTr = o2::math_utils::Similarity(getTrackRotMatrix(i), getTrackCovMatrix(i, cand)); if (covTr.Invert()) { covm += covTr; nAdded++; @@ -707,14 +817,15 @@ ROOT::Math::SMatrix> DCAFitterN -void DCAFitterN::calcTrackResiduals() +GPUd() void DCAFitterN::calcTrackResiduals() { // calculate residuals Vec3D vtxLoc; @@ -728,7 +839,7 @@ void DCAFitterN::calcTrackResiduals() //___________________________________________________________________ template -inline void DCAFitterN::calcTrackDerivatives() +GPUdi() void DCAFitterN::calcTrackDerivatives() { // calculate track derivatives over X param for (int i = N; i--;) { @@ -738,7 +849,7 @@ inline void DCAFitterN::calcTrackDerivatives() //___________________________________________________________________ template -inline double DCAFitterN::calcChi2() const +GPUdi() double DCAFitterN::calcChi2() const { // calculate current chi2 double chi2 = 0; @@ -752,7 +863,7 @@ inline double DCAFitterN::calcChi2() const //___________________________________________________________________ template -inline double DCAFitterN::calcChi2NoErr() const +GPUdi() double DCAFitterN::calcChi2NoErr() const { // calculate current chi2 of abs. distance minimization double chi2 = 0; @@ -765,7 +876,7 @@ inline double DCAFitterN::calcChi2NoErr() const //___________________________________________________________________ template -bool DCAFitterN::correctTracks(const VecND& corrX) +GPUd() bool DCAFitterN::correctTracks(const VecND& corrX) { // propagate tracks to updated X for (int i = N; i--;) { @@ -780,7 +891,7 @@ bool DCAFitterN::correctTracks(const VecND& corrX) //___________________________________________________________________ template -bool DCAFitterN::propagateTracksToVertex(int icand) +GPUd() bool DCAFitterN::propagateTracksToVertex(int icand) { // propagate tracks to current vertex int ord = mOrder[icand]; @@ -817,7 +928,7 @@ bool DCAFitterN::propagateTracksToVertex(int icand) //___________________________________________________________________ template -inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) const +GPUdi() o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) { // propagate tracks param only to current vertex (if not already done) int ord = mOrder[icand]; @@ -828,16 +939,16 @@ inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int trc.invalidate(); } } - return std::move(trc); + return trc; } //___________________________________________________________________ template -inline double DCAFitterN::getAbsMax(const VecND& v) +GPUdi() double DCAFitterN::getAbsMax(const VecND& v) { double mx = -1; for (int i = N; i--;) { - auto vai = std::abs(v[i]); + auto vai = o2::gpu::GPUCommonMath::Abs(v[i]); if (mx < vai) { mx = vai; } @@ -847,20 +958,41 @@ inline double DCAFitterN::getAbsMax(const VecND& v) //___________________________________________________________________ template -bool DCAFitterN::minimizeChi2() +GPUd() bool DCAFitterN::minimizeChi2() { // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA for (int i = N; i--;) { mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame - if (x < mMinXSeed || !propagateToX(mCandTr[mCurHyp][i], x)) { + if (x < mMinXSeed) { + mFitStatus[mCurHyp] = FitStatus::RejTrackX; return false; } - 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 (!propagateToX(mCandTr[mCurHyp][i], x)) { + return false; + } + setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor)) { // prepare inverse cov.matrices at starting point + if (mLoggerBadCov.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): overrode invalid track covariance from %s\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].asString().c_str()); +#else + printf("fitter %d: error (%ld muted): overrode invalid track covariance cyy:%e czz:%e cyz:%e\n", + mFitterID, mLoggerBadCov.evCount, mCandTr[mCurHyp][i].getSigmaY2(), mCandTr[mCurHyp][i].getSigmaZ2(), mCandTr[mCurHyp][i].getSigmaZY()); +#endif + } + mFitStatus[mCurHyp] = FitStatus::FailInvCov; + if (mBadCovPolicy == Discard) { + return false; + } else if (mBadCovPolicy == OverrideAndFlag) { + mPropFailed[mCurHyp] = true; + } // otherwise, just use overridden errors w/o flagging + } } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference + mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ; return false; } @@ -877,15 +1009,20 @@ bool DCAFitterN::minimizeChi2() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { - LOG(error) << "InversionFailed"; + if (mLoggerBadInv.needToLog()) { + printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount); + } + mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv; return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; if (!correctTracks(dx)) { + mFitStatus[mCurHyp] = FitStatus::FailCorrTracks; return false; } calcPCA(); // updated PCA if (mCrossIDAlt >= 0 && closerToAlternative()) { + mFitStatus[mCurHyp] = FitStatus::FailCloserAlt; mAllowAltPreference = false; return false; } @@ -893,30 +1030,43 @@ bool DCAFitterN::minimizeChi2() chi2Upd = calcChi2(); // updated chi2 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { chi2 = chi2Upd; + mFitStatus[mCurHyp] = FitStatus::Converged; break; // converged } chi2 = chi2Upd; } while (++mNIters[mCurHyp] < mMaxIter); + if (mNIters[mCurHyp] == mMaxIter) { + mFitStatus[mCurHyp] = FitStatus::MaxIter; + } // mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; + if (mChi2[mCurHyp] >= mMaxChi2) { + mFitStatus[mCurHyp] = FitStatus::RejChi2Max; + return false; + } + return true; } //___________________________________________________________________ template -bool DCAFitterN::minimizeChi2NoErr() +GPUd() bool DCAFitterN::minimizeChi2NoErr() { // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed for (int i = N; i--;) { mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; auto x = mTrAux[i].c * mPCA[mCurHyp][0] + mTrAux[i].s * mPCA[mCurHyp][1]; // X of PCA in the track frame - if (x < mMinXSeed || !propagateParamToX(mCandTr[mCurHyp][i], x)) { + if (x < mMinXSeed) { + mFitStatus[mCurHyp] = FitStatus::RejTrackX; + return false; + } + if (!propagateParamToX(mCandTr[mCurHyp][i], x)) { return false; } setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions } if (mMaxDZIni > 0 && !roughDZCut()) { // apply rough cut on tracks Z difference + mFitStatus[mCurHyp] = FitStatus::RejTrackRoughZ; return false; } @@ -930,15 +1080,20 @@ bool DCAFitterN::minimizeChi2NoErr() // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 if (!mD2Chi2Dx2.Invert()) { - LOG(error) << "InversionFailed"; + if (mLoggerBadInv.needToLog()) { + printf("fitter %d: error (%ld muted): Inversion failed\n", mFitterID, mLoggerBadCov.evCount); + } + mFitStatus[mCurHyp] = FitStatus::FailInv2ndDeriv; return false; } VecND dx = mD2Chi2Dx2 * mDChi2Dx; if (!correctTracks(dx)) { + mFitStatus[mCurHyp] = FitStatus::FailCorrTracks; return false; } calcPCANoErr(); // updated PCA if (mCrossIDAlt >= 0 && closerToAlternative()) { + mFitStatus[mCurHyp] = FitStatus::FailCloserAlt; mAllowAltPreference = false; return false; } @@ -946,24 +1101,32 @@ bool DCAFitterN::minimizeChi2NoErr() chi2Upd = calcChi2NoErr(); // updated chi2 if (getAbsMax(dx) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { chi2 = chi2Upd; + mFitStatus[mCurHyp] = FitStatus::Converged; break; // converged } chi2 = chi2Upd; } while (++mNIters[mCurHyp] < mMaxIter); + if (mNIters[mCurHyp] == mMaxIter) { + mFitStatus[mCurHyp] = FitStatus::MaxIter; + } // mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; + if (mChi2[mCurHyp] >= mMaxChi2) { + mFitStatus[mCurHyp] = FitStatus::RejChi2Max; + return false; + } + return true; } //___________________________________________________________________ template -bool DCAFitterN::roughDZCut() const +GPUd() bool DCAFitterN::roughDZCut() const { // apply rough cut on DZ between the tracks in the seed point bool accept = true; for (int i = N; accept && i--;) { for (int j = i; j--;) { - if (std::abs(mCandTr[mCurHyp][i].getZ() - mCandTr[mCurHyp][j].getZ()) > mMaxDZIni) { + if (o2::gpu::GPUCommonMath::Abs(mCandTr[mCurHyp][i].getZ() - mCandTr[mCurHyp][j].getZ()) > mMaxDZIni) { accept = false; break; } @@ -974,7 +1137,7 @@ bool DCAFitterN::roughDZCut() const //___________________________________________________________________ template -bool DCAFitterN::closerToAlternative() const +GPUd() bool DCAFitterN::closerToAlternative() const { // check if the point current PCA point is closer to the seeding XY point being tested or to alternative see (if any) auto dxCur = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDCur], dyCur = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDCur]; @@ -984,17 +1147,34 @@ bool DCAFitterN::closerToAlternative() const //___________________________________________________________________ template -void DCAFitterN::print() const +GPUd() void DCAFitterN::print() const { - LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode"; - LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2; +#ifndef GPUCA_GPUCODE_DEVICE + LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode, collinear tracks mode: " << (mIsCollinear ? "ON" : "OFF"); + LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2 << " MatCorrType: " << int(mMatCorr); LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDZIni; + LOG(info) << "PropagateToPCA:" << mPropagateToPCA << " WeightedFinalPCA:" << mWeightedFinalPCA << " UsePropagator:" << mUsePropagator << " RefitWithMatCorr:" << mRefitWithMatCorr; + std::string rep{}; + for (int i = 0; i < mCrossings.nDCA; i++) { + rep += fmt::format("seed{}:{}/{} ", i, mTrPropDone[i], mPropFailed[i]); + } + LOG(info) << "Last call: NCand:" << mCurHyp << " from " << mCrossings.nDCA << " seeds, prop.done/failed: " << rep; +#else + if (mUseAbsDCA) { + printf("%d-prong vertex fitter in abs. distance minimization mode\n", N); + } else { + printf("%d-prong vertex fitter in weighted distance minimization mode\n", N); + } + printf("Bz: %1.f MaxIter: %3.d MaxChi2: %2.3f\n", mBz, mMaxIter, mMaxChi2); + printf("Stopping condition: Max.param change < %2.3f Rel.Chi2 change > %2.3f\n", mMinParamChange, mMinRelChi2Change); + printf("Discard candidates for : Rvtx > %2.3f DZ between tracks > %2.3f\n", getMaxR(), mMaxDZIni); +#endif } //___________________________________________________________________ template -o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, bool sectorAlpha) const +GPUd() o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, bool sectorAlpha) const { const auto& trP = getTrack(0, cand); const auto& trN = getTrack(1, cand); @@ -1023,12 +1203,12 @@ o2::track::TrackParCov DCAFitterN::createParentTrackParCov(int cand, covV[3] = covVtxV(2, 0); covV[4] = covVtxV(2, 1); covV[5] = covVtxV(2, 2); - return std::move(o2::track::TrackParCov(getPCACandidatePos(cand), pvecV, covV, q, sectorAlpha)); + return o2::track::TrackParCov(getPCACandidatePos(cand), pvecV, covV, q, sectorAlpha); } //___________________________________________________________________ template -o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool sectorAlpha) const +GPUd() o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool sectorAlpha) const { const auto& trP = getTrack(0, cand); const auto& trN = getTrack(1, cand); @@ -1045,34 +1225,76 @@ o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool q += trc.getCharge(); } const std::array vertex = {(float)wvtx[0], (float)wvtx[1], (float)wvtx[2]}; - return std::move(o2::track::TrackPar(vertex, pvecV, q, sectorAlpha)); + return o2::track::TrackPar(vertex, pvecV, q, sectorAlpha); } //___________________________________________________________________ template -inline bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) const +GPUdi() bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#ifndef GPUCA_GPUCODE + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { - return t.propagateParamTo(x, mBz); + res = t.propagateParamTo(x, mBz); + } + if (!res) { + mFitStatus[mCurHyp] = FitStatus::FailProp; + mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation failed for %s\n", mFitterID, mLoggerBadProp.evCount, t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation failed\n", mFitterID, mLoggerBadProp.evCount); +#endif + } } + return res; } //___________________________________________________________________ template -inline bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) const +GPUdi() bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#ifndef GPUCA_GPUCODE + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { - return t.propagateTo(x, mBz); + res = t.propagateTo(x, mBz); } + if (!res) { + mFitStatus[mCurHyp] = FitStatus::FailProp; + mPropFailed[mCurHyp] = true; + if (mLoggerBadProp.needToLog()) { +#ifndef GPUCA_GPUCODE + printf("fitter %d: error (%ld muted): propagation failed for %s\n", mFitterID, mLoggerBadProp.evCount, t.asString().c_str()); +#else + printf("fitter %d: error (%ld muted): propagation failed\n", mFitterID, mLoggerBadProp.evCount); +#endif + } + } + return res; } using DCAFitter2 = DCAFitterN<2, o2::track::TrackParCov>; using DCAFitter3 = DCAFitterN<3, o2::track::TrackParCov>; +namespace device +{ +template +void print(const int nBlocks, const int nThreads, Fitter& ft); + +template +int process(const int nBlocks, const int nThreads, Fitter&, Tr&... args); + +template +void processBulk(const int nBlocks, const int nThreads, const int nBatches, std::vector& fitters, std::vector& results, std::vector
&... args); +} // namespace device + } // namespace vertexing } // namespace o2 #endif // _ALICEO2_DCA_FITTERN_ diff --git a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h index cd1742e24fa72..d5bc6631575af 100644 --- a/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h @@ -20,7 +20,7 @@ #include "MathUtils/Cartesian.h" #include "ReconstructionDataFormats/TrackFwd.h" #include "ReconstructionDataFormats/Track.h" -#include "DCAFitter/HelixHelper.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" diff --git a/Common/DCAFitter/include/DCAFitter/HelixHelper.h b/Common/DCAFitter/include/DCAFitter/HelixHelper.h deleted file mode 100644 index 87575367e1af5..0000000000000 --- a/Common/DCAFitter/include/DCAFitter/HelixHelper.h +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file HelixHelper.h -/// \brief Helper classes for helical tracks manipulations -/// \author ruben.shahoyan@cern.ch - -#ifndef _ALICEO2_HELIX_HELPER_ -#define _ALICEO2_HELIX_HELPER_ - -#include "CommonConstants/MathConstants.h" -#include "MathUtils/Utils.h" -#include "MathUtils/Primitive2D.h" - -namespace o2 -{ -namespace track -{ - -///__________________________________________________________________________ -//< precalculated track radius, center, alpha sin,cos and their combinations -struct TrackAuxPar : public o2::math_utils::CircleXYf_t { - float c, s, cc, ss, cs; // cos ans sin of track alpha and their products - - TrackAuxPar() = default; - - template - TrackAuxPar(const T& trc, float bz) - { - set(trc, bz); - } - float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) - float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) - - template - void set(const T& trc, float bz) - { - trc.getCircleParams(bz, *this, s, c); - cc = c * c; - ss = s * s; - cs = c * s; - } - ClassDefNV(TrackAuxPar, 1); -}; - -//__________________________________________________________ -//< crossing coordinates of 2 circles -struct CrossInfo { - static constexpr float MaxDistXYDef = 10.; - float xDCA[2] = {}; - float yDCA[2] = {}; - int nDCA = 0; - - int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef) - { - const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A - const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; - float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; - float dist2 = xDist * xDist + yDist * yDist, dist = std::sqrt(dist2), rsum = trcA.rC + trcB.rC; - if (std::abs(dist) < 1e-12) { - return nDCA; // circles are concentric? - } - if (dist > rsum) { // circles don't touch, chose a point in between - // the parametric equation of lines connecting the centers is - // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) - if (dist - rsum > maxDistXY) { // too large distance - return nDCA; - } - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } else if (dist + trcB.rC < trcA.rC) { // the small circle is nestled into large one w/o touching - // select the point of closest approach of 2 circles - notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC); - } else { // 2 intersection points - // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that - // the 1st one is centered in origin - if (std::abs(xDist) < std::abs(yDist)) { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - xDCA[0] = (-ab + det) / (1. + b * b); - yDCA[0] = a + b * xDCA[0] + trcA.yC; - xDCA[0] += trcA.xC; - xDCA[1] = (-ab - det) / (1. + b * b); - yDCA[1] = a + b * xDCA[1] + trcA.yC; - xDCA[1] += trcA.xC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } else { - float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; - float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); - if (det > 0.) { - det = std::sqrt(det); - yDCA[0] = (-ab + det) / (1. + bb); - xDCA[0] = a + b * yDCA[0] + trcA.xC; - yDCA[0] += trcA.yC; - yDCA[1] = (-ab - det) / (1. + bb); - xDCA[1] = a + b * yDCA[1] + trcA.xC; - yDCA[1] += trcA.yC; - nDCA = 2; - } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case - notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); - } - } - } - return nDCA; - } - - void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign) - { - // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: - // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist - // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... - // There are 2 special cases: - // (a) small circle is inside the large one: provide rBSign as -trcB.rC - // (b) circle are side by side: provide rBSign as trcB.rC - nDCA = 1; - auto t2d = (dist + trcA.rC - rBSign) / dist; - xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); - yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); - } - - template - int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of 2 straight lines - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 - - float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! - float dy = trax1.yC - trax0.yC; // - float dz = tr1.getZ() - tr0.getZ(); - auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 - auto csp0i = std::sqrt(csp0i2); - auto tgp0 = tr0.getSnp() * csp0i; - float kx0 = trax0.c - trax0.s * tgp0; - float ky0 = trax0.s + trax0.c * tgp0; - float kz0 = tr0.getTgl() * csp0i; - auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 - auto csp1i = std::sqrt(csp1i2); - auto tgp1 = tr1.getSnp() * std::sqrt(csp1i2); - float kx1 = trax1.c - trax1.s * tgp1; - float ky1 = trax1.s + trax1.c * tgp1; - float kz1 = tr1.getTgl() * csp1i; - /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach - /// Leads to system - /// A Dx = B with Dx = {dx0, dx1} - /// with A = - /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... - /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 - /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } - /// - float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); - float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); - float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; - if (std::abs(det) > o2::constants::math::Almost0) { - auto detI = 1. / det; - auto t0 = det0 * detI; - auto t1 = det1 * detI; - float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; - dx += addx1 - addx0; // recalculate XY distance at DCA - dy += addy1 - addy0; - if (dx * dx + dy * dy > maxDistXY * maxDistXY) { - return nDCA; - } - xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; - yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, - const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - /// closest approach of line and circle - /// TrackParam propagation can be parameterized in lab in a form - /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) - /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) - /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp - /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab - /// frame (filled by TrackAuxPar for straight line tracks). - /// - /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) - /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) - /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) - /// zL(t) = zL + t Kz; Kz = tgl / csp - /// Note that Kx^2 + Ky^2 = 1 / csp^2 - - const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) - const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line - const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line - - // solve quadratic equation of line crossing the circle - float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center - float dy = traxL.yC - traxH.yC; // Y... - // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 - auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 - auto cspi = std::sqrt(cspi2); - auto tgp = trcL.getSnp() * cspi; - float kx = traxL.c - traxL.s * tgp; - float ky = traxL.s + traxL.c * tgp; - double dk = dx * kx + dy * ky; - double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); - if (det > 0) { // 2 crossings - det = std::sqrt(det); - float t0 = (-dk + det) * cspi2; - float t1 = (-dk - det) * cspi2; - xDCA[0] = traxL.xC + kx * t0; - yDCA[0] = traxL.yC + ky * t0; - xDCA[1] = traxL.xC + kx * t1; - yDCA[1] = traxL.yC + ky * t1; - nDCA = 2; - } else { - // there is no crossing, find the point of the closest approach on the line which is closest to the circle center - float t = -dk * cspi2; - float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle - float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = std::sqrt(dxc * dxc + dyc * dyc); - if (dist - traxH.rC > maxDistXY) { // too large distance - return nDCA; - } - float drcf = traxH.rC / dist; // radius / distance to circle center - float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; - xDCA[0] = (xL + xH) * 0.5; - yDCA[0] = (yL + yH) * 0.5; - nDCA = 1; - } - return nDCA; - } - - template - int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - // calculate up to 2 crossings between 2 circles - nDCA = 0; - if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines - nDCA = circlesCrossInfo(trax0, trax1, maxDistXY); - } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines - nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } else { - nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); - } - // - return nDCA; - } - - CrossInfo() = default; - - template - CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) - { - set(trax0, tr0, trax1, tr1, maxDistXY); - } - ClassDefNV(CrossInfo, 1); -}; - -} // namespace track -} // namespace o2 - -#endif diff --git a/Common/DCAFitter/src/DCAFitterLinkDef.h b/Common/DCAFitter/src/DCAFitterLinkDef.h index 3589ffe559e96..6883369c1b9b6 100644 --- a/Common/DCAFitter/src/DCAFitterLinkDef.h +++ b/Common/DCAFitter/src/DCAFitterLinkDef.h @@ -18,9 +18,6 @@ #pragma link C++ class o2::vertexing::DCAFitterN < 2, o2::track::TrackParCov> + ; #pragma link C++ class o2::vertexing::DCAFitterN < 3, o2::track::TrackParCov> + ; -#pragma link C++ class o2::track::TrackAuxPar + ; -#pragma link C++ class o2::track::CrossInfo + ; - #pragma link C++ function o2::vertexing::DCAFitter2::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&); #pragma link C++ function o2::vertexing::DCAFitter3::process(const o2::track::TrackParCov&, const o2::track::TrackParCov&, const o2::track::TrackParCov&); diff --git a/Common/DCAFitter/test/testDCAFitterN.cxx b/Common/DCAFitter/test/testDCAFitterN.cxx index 7fef1f6889284..bd00b5bed841e 100644 --- a/Common/DCAFitter/test/testDCAFitterN.cxx +++ b/Common/DCAFitter/test/testDCAFitterN.cxx @@ -61,7 +61,11 @@ float checkResults(o2::utils::TreeStreamRedirector& outs, std::string& treeName, << "genPart=" << genPar << "recPart=" << moth << "genX=" << vgen[0] << "genY=" << vgen[1] << "genZ=" << vgen[2] << "dx=" << df[0] << "dy=" << df[1] << "dz=" << df[2] << "dst=" << dst - << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack << "\n"; + << "useAbsDCA=" << absDCA << "useWghDCA=" << useWghDCA << "parent=" << parentTrack; + for (int i = 0; i < fitter.getNProngs(); i++) { + outs << treeName.c_str() << fmt::format("prong{}=", i).c_str() << fitter.getTrack(i, ic); + } + outs << treeName.c_str() << "\n"; } return distMin; } @@ -130,7 +134,7 @@ TLorentzVector generate(Vec3D& vtx, std::vector& vctr, f float rad = forceQ[i] == 0 ? 600. : TMath::Abs(1. / trc.getCurvature(bz)); if (!trc.propagateTo(trc.getX() + (gRandom->Rndm() - 0.5) * rad * 0.05, bz) || !trc.rotate(trc.getAlpha() + (gRandom->Rndm() - 0.5) * 0.2)) { - printf("Failed to randomize "); + LOGP(error, "Failed to randomize "); trc.print(); } } @@ -139,26 +143,47 @@ TLorentzVector generate(Vec3D& vtx, std::vector& vctr, f return parent; } +static constexpr int NFitStatus{14}; +using FitStatusArray = std::array, NFitStatus>; +static constexpr const char* FitStatusNames[NFitStatus] = { + "None", "Converged", "MaxIter", "NoCrossing", "RejRadius", "RejTrackX", "RejTrackRoughZ", "RejChi2Max", + "FailProp", "FailInvConv", "FailInvWeight", "FailInv2ndDeriv", "FailCorrTracks", "FailCloserAlt"}; +inline void printStat(const FitStatusArray& a) +{ + LOGP(info, "FitStatus summary : ....A / ..AWD / ...WD (A=abs.dist;AWD=abs.wghPCA.dist;WD=wgh.dist)"); + for (int i{0}; i < NFitStatus; ++i) { + LOGP(info, "{:2d}={:20s}: {:5d} / {:5d} / {:5d}", i, FitStatusNames[i], a[i][0], a[i][1], a[i][2]); + } + BOOST_CHECK(a[0][0] == 0); // ensure coverage of all possible states + BOOST_CHECK(a[0][1] == 0); + BOOST_CHECK(a[0][2] == 0); +} + BOOST_AUTO_TEST_CASE(DCAFitterNProngs) { constexpr int NTest = 10000; o2::utils::TreeStreamRedirector outStream("dcafitterNTest.root"); TGenPhaseSpace genPHS; + constexpr double ele = 0.00051; + constexpr double gamma = 2 * ele + 1e-6; constexpr double pion = 0.13957; constexpr double k0 = 0.49761; constexpr double kch = 0.49368; constexpr double dch = 1.86965; + std::vector gammadec = {ele, ele}; std::vector k0dec = {pion, pion}; std::vector dchdec = {pion, kch, pion}; std::vector vctracks; + FitStatusArray fitstat; Vec3D vtxGen; double bz = 5.0; // 2 prongs vertices { - LOG(info) << "Processing 2-prong Helix - Helix case"; + LOG(info) << "\n\nProcessing 2-prong Helix - Helix case"; std::vector forceQ{1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); @@ -189,6 +214,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -201,6 +227,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -213,30 +240,121 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; - meanDAW /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); + } + + // 2 prongs vertices with collinear tracks (gamma conversion) + { + LOG(info) << "\n\nProcessing 2-prong Helix - Helix case gamma conversion"; + std::vector forceQ{1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter + ft.setBz(bz); + ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + ft.setMaxR(200); // do not consider V0 seeds with 2D circles crossing above this R. This is default anyway + ft.setMaxDZIni(4); // do not consider V0 seeds with tracks Z-distance exceeding this. This is default anyway + ft.setMaxDXYIni(4); // do not consider V0 seeds with tracks XY-distance exceeding this. This is default anyway + ft.setMinParamChange(1e-3); // stop iterations if max correction is below this value. This is default anyway + ft.setMinRelChi2Change(0.9); // stop iterations if chi2 improves by less that this factor + ft.setMaxChi2(); + ft.setCollinear(true); + + std::string treeName2A = "gpr2a", treeName2AW = "gpr2aw", treeName2W = "gpr2w"; + TStopwatch swA, swAW, swW; + int nfoundA = 0, nfoundAW = 0, nfoundW = 0; + double meanDA = 0, meanDAW = 0, meanDW = 0; + swA.Stop(); + swAW.Stop(); + swW.Stop(); + for (int iev = 0; iev < NTest; iev++) { + auto genParent = generate(vtxGen, vctracks, bz, genPHS, gamma, gammadec, forceQ); + + ft.setUseAbsDCA(true); + swA.Start(false); + int ncA = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swA.Stop(); + LOG(debug) << "fit abs.dist " << iev << " NC: " << ncA << " Chi2: " << (ncA ? ft.getChi2AtPCACandidate(0) : -1); + if (ncA) { + auto minD = checkResults(outStream, treeName2A, ft, vtxGen, genParent, gammadec); + meanDA += minD; + nfoundA++; + } + ++fitstat[ft.getFitStatus()][0]; + + ft.setUseAbsDCA(true); + ft.setWeightedFinalPCA(true); + swAW.Start(false); + int ncAW = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swAW.Stop(); + LOG(debug) << "fit abs.dist with final weighted DCA " << iev << " NC: " << ncAW << " Chi2: " << (ncAW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncAW) { + auto minD = checkResults(outStream, treeName2AW, ft, vtxGen, genParent, gammadec); + meanDAW += minD; + nfoundAW++; + } + ++fitstat[ft.getFitStatus()][1]; + + ft.setUseAbsDCA(false); + ft.setWeightedFinalPCA(false); + swW.Start(false); + int ncW = ft.process(vctracks[0], vctracks[1]); // HERE WE FIT THE VERTICES + swW.Stop(); + LOG(debug) << "fit wgh.dist " << iev << " NC: " << ncW << " Chi2: " << (ncW ? ft.getChi2AtPCACandidate(0) : -1); + if (ncW) { + auto minD = checkResults(outStream, treeName2W, ft, vtxGen, genParent, gammadec); + meanDW += minD; + nfoundW++; + } + ++fitstat[ft.getFitStatus()][2]; + } + // ft.print(); + meanDA /= nfoundA ? nfoundA : 1; + meanDAW /= nfoundAW ? nfoundAW : 1; + meanDW /= nfoundW ? nfoundW : 1; + LOG(info) << "Processed " << NTest << " 2-prong vertices Helix : Helix from gamma conversion"; + LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; + LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); + BOOST_CHECK(nfoundA > 0.99 * NTest); + BOOST_CHECK(nfoundAW > 0.99 * NTest); + BOOST_CHECK(nfoundW > 0.99 * NTest); + BOOST_CHECK(meanDA < 2.1); + BOOST_CHECK(meanDAW < 2.1); + BOOST_CHECK(meanDW < 2.1); + ft.print(); } // 2 prongs vertices with one of charges set to 0: Helix : Line { std::vector forceQ{1, 1}; - LOG(info) << "Processing 2-prong Helix - Line case"; + LOG(info) << "\n\nProcessing 2-prong Helix - Line case"; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway @@ -267,6 +385,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -279,6 +398,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -291,30 +411,35 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices: Helix : Line"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } // 2 prongs vertices with both of charges set to 0: Line : Line { std::vector forceQ{0, 0}; - LOG(info) << "Processing 2-prong Line - Line case"; + LOG(info) << "\n\nProcessing 2-prong Line - Line case"; + std::memset(fitstat.data(), 0, sizeof(fitstat)); + o2::vertexing::DCAFitterN<2> ft; // 2 prong fitter ft.setBz(bz); ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway @@ -344,6 +469,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -356,6 +482,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -368,29 +495,34 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 2-prong vertices: Line : Line"; LOG(info) << "2-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "2-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } // 3 prongs vertices { + LOG(info) << "\n\nProcessing 3-prong vertices"; std::vector forceQ{1, 1, 1}; + std::memset(fitstat.data(), 0, sizeof(fitstat)); o2::vertexing::DCAFitterN<3> ft; // 3 prong fitter ft.setBz(bz); @@ -420,6 +552,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -432,6 +565,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -444,26 +578,28 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDW += minD; nfoundW++; } + ++fitstat[ft.getFitStatus()][2]; } - ft.print(); + // ft.print(); meanDA /= nfoundA ? nfoundA : 1; meanDAW /= nfoundAW ? nfoundAW : 1; meanDW /= nfoundW ? nfoundW : 1; LOG(info) << "Processed " << NTest << " 3-prong vertices"; LOG(info) << "3-prongs with abs.dist minization: eff= " << float(nfoundA) / NTest - << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime(); + << " mean.dist to truth: " << meanDA << " CPU time: " << swA.CpuTime() * 1000 << " ms"; LOG(info) << "3-prongs with abs.dist but wghPCA: eff= " << float(nfoundAW) / NTest - << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime(); + << " mean.dist to truth: " << meanDAW << " CPU time: " << swAW.CpuTime() * 1000 << " ms"; LOG(info) << "3-prongs with wgh.dist minization: eff= " << float(nfoundW) / NTest - << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime(); + << " mean.dist to truth: " << meanDW << " CPU time: " << swW.CpuTime() * 1000 << " ms"; + printStat(fitstat); BOOST_CHECK(nfoundA > 0.99 * NTest); BOOST_CHECK(nfoundAW > 0.99 * NTest); BOOST_CHECK(nfoundW > 0.99 * NTest); BOOST_CHECK(meanDA < 0.1); BOOST_CHECK(meanDAW < 0.1); BOOST_CHECK(meanDW < 0.1); + ft.print(); } - outStream.Close(); } diff --git a/Common/Field/include/Field/MagFieldFast.h b/Common/Field/include/Field/MagFieldFast.h index acff8f528ad06..ae6431a477923 100644 --- a/Common/Field/include/Field/MagFieldFast.h +++ b/Common/Field/include/Field/MagFieldFast.h @@ -57,7 +57,7 @@ class MagFieldFast bool Field(const math_utils::Point3D xyz, double bxyz[3]) const; bool GetBcomp(EDim comp, const double xyz[3], double& b) const; bool GetBcomp(EDim comp, const float xyz[3], float& b) const; - bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; + bool GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const; bool GetBcomp(EDim comp, const math_utils::Point3D xyz, float& b) const; bool GetBx(const double xyz[3], double& bx) const { return GetBcomp(kX, xyz, bx); } @@ -66,6 +66,8 @@ class MagFieldFast bool GetBy(const float xyz[3], float& by) const { return GetBcomp(kY, xyz, by); } bool GetBz(const double xyz[3], double& bz) const { return GetBcomp(kZ, xyz, bz); } bool GetBz(const float xyz[3], float& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, double& bz) const { return GetBcomp(kZ, xyz, bz); } + bool GetBz(const math_utils::Point3D xyz, float& bz) const { return GetBcomp(kZ, xyz, bz); } void setFactorSol(float v = 1.f) { mFactorSol = v; } float getFactorSol() const { return mFactorSol; } diff --git a/Common/Field/src/MagFieldFast.cxx b/Common/Field/src/MagFieldFast.cxx index e652dffa396c5..02ef9c153d189 100644 --- a/Common/Field/src/MagFieldFast.cxx +++ b/Common/Field/src/MagFieldFast.cxx @@ -145,7 +145,7 @@ bool MagFieldFast::GetBcomp(EDim comp, const double xyz[3], double& b) const } //_______________________________________________________________________ -bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const +bool MagFieldFast::GetBcomp(EDim comp, const math_utils::Point3D xyz, double& b) const { // get field int zSeg, rSeg, quadrant; @@ -238,7 +238,7 @@ bool MagFieldFast::Field(const math_utils::Point3D xyz, double bxyz[3]) bool MagFieldFast::GetSegment(float x, float y, float z, int& zSeg, int& rSeg, int& quadrant) const { // get segment of point location - const float zGridSpaceInv = 1.f / (kSolZMax * 2 / kNSolZRanges); + const float zGridSpaceInv = 1.f / (kSolZMax * 2 / (float)kNSolZRanges); zSeg = -1; if (z < kSolZMax) { if (z > -kSolZMax) { diff --git a/Common/Field/src/MagneticField.cxx b/Common/Field/src/MagneticField.cxx index 260580f07132e..5df6bbc0b0d34 100644 --- a/Common/Field/src/MagneticField.cxx +++ b/Common/Field/src/MagneticField.cxx @@ -539,7 +539,7 @@ void MagneticField::rescaleField(float l3Cur, float diCur, bool uniform, int con float sclL3 = l3Cur, sclDip = diCur; MagFieldParam::BMap_t map = getFieldMapScale(sclL3, sclDip, uniform); if (map != mMapType) { - LOGP(fatal, "Provided L3current={} DipCurrent={} uniform={} leads to map type {}, incompatible with loaded {}", l3Cur, diCur, uniform, map, mMapType); + LOGP(fatal, "Provided L3current={} DipCurrent={} uniform={} leads to map type {}, incompatible with loaded {}", l3Cur, diCur, uniform, (int)map, (int)mMapType); } setFactorSolenoid(sclL3); setFactorDipole(sclDip); @@ -558,18 +558,29 @@ MagFieldParam::BMap_t MagneticField::getFieldMapScale(float& l3, float& dip, boo MagFieldParam::BMap_t map = MagFieldParam::k5kG; float sclL3, sclDip; + float l3sav = l3, dipsav = dip; float l3Pol = l3 > 0 ? 1 : -1; float diPol = dip > 0 ? 1 : -1; l3 = TMath::Abs(l3); dip = TMath::Abs(dip); + static bool overrideL3 = std::getenv("O2_OVERRIDE_L3_CURRENT") != nullptr; + static bool overrideDIP = std::getenv("O2_OVERRIDE_DIPOLE_CURRENT") != nullptr; + static bool warnL3Done = false, warnDipDone = false, warnPolarityDone = false; if (TMath::Abs((sclDip = dip / diNominalCurrent) - 1.) > tolerance && !uniform) { if (dip <= zero) { sclDip = 0.; // some small current.. -> Dipole OFF } else { - LOG(fatal) << "MagneticField::createFieldMap: Wrong dipole current (" << dip << " A)!"; + if (!overrideDIP) { + LOG(fatal) << "MagneticField::createFieldMap: Wrong dipole current (" << dipsav << " A)!"; + } else { + if (!warnDipDone) { + LOGP(error, "Dipole current was overridden to unsupported value {}", dipsav); + warnDipDone = true; + } + } } } if (uniform) { @@ -587,15 +598,33 @@ MagFieldParam::BMap_t MagneticField::getFieldMapScale(float& l3, float& dip, boo sclDip = 0; map = MagFieldParam::k5kGUniform; } else { - LOG(fatal) << "MagneticField::createFieldMap: Wrong L3 current (" << l3 << " A)!"; + if (!overrideL3) { + LOG(fatal) << "MagneticField::createFieldMap: Wrong L3 current (" << l3sav << " A)!"; + } else { + if (!warnL3Done) { + LOGP(error, "L3 current was overridden to unsupported value {}", l3sav); + warnL3Done = true; + } + map = MagFieldParam::k5kG; + sclL3 = l3 / l3NominalCurrent1; + } } } if (sclDip != 0 && map != MagFieldParam::k5kGUniform) { if ((l3 <= zero) || ((convention == kConvLHC && l3Pol != diPol) || (convention == kConvDCS2008 && l3Pol == diPol))) { - LOG(fatal) << "MagneticField::createFieldMap: Wrong combination for L3/Dipole polarities (" - << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " - << getPolarityConvention(); + if (overrideL3 || overrideDIP) { + if (!warnPolarityDone) { + LOG(error) << "Overriden currents have wrong combination for L3/Dipole polarities (" + << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " + << getPolarityConvention(); + warnPolarityDone = true; + } + } else { + LOG(fatal) << "MagneticField::createFieldMap: Wrong combination for L3/Dipole polarities (" + << (l3Pol > 0 ? '+' : '-') << "/" << (diPol > 0 ? '+' : '-') << ") for convention " + << getPolarityConvention(); + } } } l3 = (l3Pol < 0) ? -sclL3 : sclL3; diff --git a/Common/ML/CMakeLists.txt b/Common/ML/CMakeLists.txt new file mode 100644 index 0000000000000..0ed52e1a23e20 --- /dev/null +++ b/Common/ML/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ML + SOURCES src/OrtInterface.cxx + TARGETVARNAME targetName + PRIVATE_LINK_LIBRARIES O2::GPUCommon onnxruntime::onnxruntime) + +# Pass ORT variables as a preprocessor definition +target_compile_definitions(${targetName} PRIVATE + $<$:ORT_ROCM_BUILD> + $<$:ORT_CUDA_BUILD> + $<$:ORT_MIGRAPHX_BUILD> + $<$:ORT_TENSORRT_BUILD>) diff --git a/Common/ML/include/ML/3rdparty/GPUORTFloat16.h b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h new file mode 100644 index 0000000000000..75e146d872cd1 --- /dev/null +++ b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h @@ -0,0 +1,887 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// This code was created from: +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_float16.h +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_cxx_api.h + +#ifndef GPUORTFLOAT16_H +#define GPUORTFLOAT16_H + +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#include +#include +#endif + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2 +{ + +namespace OrtDataType +{ + +namespace detail +{ + +enum class endian { +#if defined(_WIN32) + little = 0, + big = 1, + native = little, +#elif defined(__GNUC__) || defined(__clang__) + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__, +#else +#error OrtDataType::detail::endian is not implemented in this environment. +#endif +}; + +static_assert( + endian::native == endian::little || endian::native == endian::big, + "Only little-endian or big-endian native byte orders are supported."); + +} // namespace detail + +/// +/// Shared implementation between public and internal classes. CRTP pattern. +/// +template +struct Float16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + GPUd() constexpr static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + GPUd() float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7C00U; + static constexpr uint16_t kPositiveInfinityBits = 0x7C00U; + static constexpr uint16_t kNegativeInfinityBits = 0xFC00U; + static constexpr uint16_t kPositiveQNaNBits = 0x7E00U; + static constexpr uint16_t kNegativeQNaNBits = 0xFE00U; + static constexpr uint16_t kEpsilonBits = 0x4170U; + static constexpr uint16_t kMinValueBits = 0xFBFFU; // Minimum normal number + static constexpr uint16_t kMaxValueBits = 0x7BFFU; // Largest normal number + static constexpr uint16_t kOneBits = 0x3C00U; + static constexpr uint16_t kMinusOneBits = 0xBC00U; + + uint16_t val{0}; + + GPUdDefault() Float16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + GPUd() bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + GPUd() bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + GPUd() bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + GPUd() bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + GPUd() bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + GPUd() bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + GPUd() bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + GPUd() bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + GPUd() bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + GPUd() static bool AreZero(const Float16Impl& lhs, const Float16Impl& rhs) noexcept + { + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } + + GPUd() bool operator==(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is not equal to anything, including itself. + return false; + } + return val == rhs.val; + } + + GPUd() bool operator!=(const Float16Impl& rhs) const noexcept { return !(*this == rhs); } + + GPUd() bool operator<(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is unordered with respect to everything, including itself. + return false; + } + + const bool left_is_negative = IsNegative(); + if (left_is_negative != rhs.IsNegative()) { + // When the signs of left and right differ, we know that left is less than right if it is + // the negative value. The exception to this is if both values are zero, in which case IEEE + // says they should be equal, even if the signs differ. + return left_is_negative && !AreZero(*this, rhs); + } + return (val != rhs.val) && ((val < rhs.val) ^ left_is_negative); + } +}; + +// The following Float16_t conversions are based on the code from +// Eigen library. + +// The conversion routines are Copyright (c) Fabian Giesen, 2016. +// The original license follows: +// +// Copyright (c) Fabian Giesen, 2016 +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +namespace detail +{ +union float32_bits { + unsigned int u; + float f; +}; +}; // namespace detail + +template +GPUdi() constexpr uint16_t Float16Impl::ToUint16Impl(float v) noexcept +{ + detail::float32_bits f{}; + f.f = v; + + constexpr detail::float32_bits f32infty = {255 << 23}; + constexpr detail::float32_bits f16max = {(127 + 16) << 23}; + constexpr detail::float32_bits denorm_magic = {((127 - 15) + (23 - 10) + 1) << 23}; + constexpr unsigned int sign_mask = 0x80000000u; + uint16_t val = static_cast(0x0u); + + unsigned int sign = f.u & sign_mask; + f.u ^= sign; + + // NOTE all the integer compares in this function can be safely + // compiled into signed compares since all operands are below + // 0x80000000. Important if you want fast straight SSE2 code + // (since there's no unsigned PCMPGTD). + + if (f.u >= f16max.u) { // result is Inf or NaN (all exponent bits set) + val = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + } else { // (De)normalized number or zero + if (f.u < (113 << 23)) { // resulting FP16 is subnormal or zero + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + + // and one integer subtract of the bias later, we have our final float! + val = static_cast(f.u - denorm_magic.u); + } else { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + + // update exponent, rounding bias part 1 + // Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but + // without arithmetic overflow. + f.u += 0xc8000fffU; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + val = static_cast(f.u >> 13); + } + } + + val |= static_cast(sign >> 16); + return val; +} + +template +GPUdi() float Float16Impl::ToFloatImpl() const noexcept +{ + constexpr detail::float32_bits magic = {113 << 23}; + constexpr unsigned int shifted_exp = 0x7c00 << 13; // exponent mask after shift + detail::float32_bits o{}; + + o.u = (val & 0x7fff) << 13; // exponent/mantissa bits + unsigned int exp = shifted_exp & o.u; // just the exponent + o.u += (127 - 15) << 23; // exponent adjust + + // handle exponent special cases + if (exp == shifted_exp) { // Inf/NaN? + o.u += (128 - 16) << 23; // extra exp adjust + } else if (exp == 0) { // Zero/Denormal? + o.u += 1 << 23; // extra exp adjust + o.f -= magic.f; // re-normalize + } + + // Attempt to workaround the Internal Compiler Error on ARM64 + // for bitwise | operator, including std::bitset +#if (defined _MSC_VER) && (defined _M_ARM || defined _M_ARM64 || defined _M_ARM64EC) + if (IsNegative()) { + return -o.f; + } +#else + // original code: + o.u |= (val & 0x8000U) << 16U; // sign bit +#endif + return o.f; +} + +/// Shared implementation between public and internal classes. CRTP pattern. +template +struct BFloat16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + GPUd() static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + GPUd() float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7F80U; + static constexpr uint16_t kPositiveInfinityBits = 0x7F80U; + static constexpr uint16_t kNegativeInfinityBits = 0xFF80U; + static constexpr uint16_t kPositiveQNaNBits = 0x7FC1U; + static constexpr uint16_t kNegativeQNaNBits = 0xFFC1U; + static constexpr uint16_t kSignaling_NaNBits = 0x7F80U; + static constexpr uint16_t kEpsilonBits = 0x0080U; + static constexpr uint16_t kMinValueBits = 0xFF7FU; + static constexpr uint16_t kMaxValueBits = 0x7F7FU; + static constexpr uint16_t kRoundToNearest = 0x7FFFU; + static constexpr uint16_t kOneBits = 0x3F80U; + static constexpr uint16_t kMinusOneBits = 0xBF80U; + + uint16_t val{0}; + + GPUdDefault() BFloat16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + GPUd() bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + GPUd() bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + GPUd() bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + GPUd() bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + GPUd() bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + GPUd() bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + GPUd() bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + GPUd() bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + GPUd() bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + GPUd() Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + GPUd() Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + GPUd() static bool AreZero(const BFloat16Impl& lhs, const BFloat16Impl& rhs) noexcept + { + // IEEE defines that positive and negative zero are equal, this gives us a quick equality check + // for two values by or'ing the private bits together and stripping the sign. They are both zero, + // and therefore equivalent, if the resulting value is still zero. + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } +}; + +template +GPUdi() uint16_t BFloat16Impl::ToUint16Impl(float v) noexcept +{ + uint16_t result; + if (o2::gpu::CAMath::IsNaN(v)) { + result = kPositiveQNaNBits; + } else { + auto get_msb_half = [](float fl) { + uint16_t res; +#ifdef GPUCA_GPUCODE + o2::gpu::CAMath::memcpy(&res, reinterpret_cast(&fl) + sizeof(uint16_t), sizeof(uint16_t)); +#else +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memcpy(&res, reinterpret_cast(&fl) + sizeof(uint16_t), sizeof(uint16_t)); + } else { + std::memcpy(&res, &fl, sizeof(uint16_t)); + } +#endif + return res; + }; + + uint16_t upper_bits = get_msb_half(v); + union { + uint32_t U32; + float F32; + }; + F32 = v; + U32 += (upper_bits & 1) + kRoundToNearest; + result = get_msb_half(F32); + } + return result; +} + +template +GPUdi() float BFloat16Impl::ToFloatImpl() const noexcept +{ +#ifndef __FAST_MATH__ + if (IsNaN()) { + return o2::gpu::CAMath::QuietNaN(); + } +#endif + float result; + char* const first = reinterpret_cast(&result); + char* const second = first + sizeof(uint16_t); +#ifdef GPUCA_GPUCODE + first[0] = first[1] = 0; + o2::gpu::CAMath::memcpy(second, &val, sizeof(uint16_t)); +#else +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memset(first, 0, sizeof(uint16_t)); + std::memcpy(second, &val, sizeof(uint16_t)); + } else { + std::memcpy(first, &val, sizeof(uint16_t)); + std::memset(second, 0, sizeof(uint16_t)); + } +#endif + return result; +} + +/** \brief IEEE 754 half-precision floating point data type + * + * \details This struct is used for converting float to float16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::Float16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector fp16_values; + * fp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(fp16_values), + * [](float value) { return Ort::Float16_t(value); }); + * + * \endcode + */ +struct Float16_t : OrtDataType::Float16Impl { + private: + /// + /// Constructor from a 16-bit representation of a float16 value + /// No conversion is done here. + /// + /// 16-bit representation + constexpr explicit Float16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::Float16Impl; + + /// + /// Default constructor + /// + GPUdDefault() Float16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of float16. + /// + /// uint16_t bit representation of float16 + /// new instance of Float16_t + GPUd() constexpr static Float16_t FromBits(uint16_t v) noexcept { return Float16_t(v); } + + /// + /// __ctor from float. Float is converted into float16 16-bit representation. + /// + /// float value + GPUd() explicit Float16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + GPUd() float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts Float16_t to float. + /// + GPUdi() explicit operator float() const noexcept { return ToFloat(); } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; +}; + +static_assert(sizeof(Float16_t) == sizeof(uint16_t), "Sizes must match"); + +/** \brief bfloat16 (Brain Floating Point) data type + * + * \details This struct is used for converting float to bfloat16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::BFloat16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector bfp16_values; + * bfp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(bfp16_values), + * [](float value) { return Ort::BFloat16_t(value); }); + * + * \endcode + */ +struct BFloat16_t : OrtDataType::BFloat16Impl { + private: + /// + /// Constructor from a uint16_t representation of bfloat16 + /// used in FromBits() to escape overload resolution issue with + /// constructor from float. + /// No conversion is done. + /// + /// 16-bit bfloat16 value + constexpr explicit BFloat16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::BFloat16Impl; + + GPUdDefault() BFloat16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of bfloat16. + /// + /// uint16_t bit representation of bfloat16 + /// new instance of BFloat16_t + GPUd() static constexpr BFloat16_t FromBits(uint16_t v) noexcept { return BFloat16_t(v); } + + /// + /// __ctor from float. Float is converted into bfloat16 16-bit representation. + /// + /// float value + GPUd() explicit BFloat16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + GPUd() float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts BFloat16_t to float. + /// + GPUdi() explicit operator float() const noexcept { return ToFloat(); } + + // We do not have an inherited impl for the below operators + // as the internal class implements them a little differently + bool operator==(const BFloat16_t& rhs) const noexcept; + bool operator!=(const BFloat16_t& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const BFloat16_t& rhs) const noexcept; +}; + +static_assert(sizeof(BFloat16_t) == sizeof(uint16_t), "Sizes must match"); + +} // namespace OrtDataType + +} // namespace o2 +#endif diff --git a/Common/ML/include/ML/OrtInterface.h b/Common/ML/include/ML/OrtInterface.h new file mode 100644 index 0000000000000..987ce8fb4d6dd --- /dev/null +++ b/Common/ML/include/ML/OrtInterface.h @@ -0,0 +1,130 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file OrtInterface.h +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#ifndef O2_ML_ORTINTERFACE_H +#define O2_ML_ORTINTERFACE_H + +// C++ and system includes +#include +#include +#include +#include +#include +#include + +// O2 includes +#include "GPUCommonLogger.h" + +namespace Ort +{ +struct SessionOptions; +struct MemoryInfo; +struct Env; +} // namespace Ort + +namespace o2::ml +{ + +class OrtModel +{ + + public: + // Constructors & destructors + OrtModel(); + OrtModel(std::unordered_map optionsMap); + void init(std::unordered_map optionsMap); + virtual ~OrtModel(); + + // General purpose + void initOptions(std::unordered_map optionsMap); + void initEnvironment(); + void initSession(); + void initSessionFromBuffer(const char* buffer, size_t bufferSize); + void memoryOnDevice(int32_t = 0); + bool isInitialized() { return mInitialized; } + void resetSession(); + + // Getters + std::vector> getNumInputNodes() const { return mInputShapes; } + std::vector> getNumOutputNodes() const { return mOutputShapes; } + std::vector getInputNames() const { return mInputNames; } + std::vector getOutputNames() const { return mOutputNames; } + Ort::SessionOptions* getSessionOptions(); + Ort::MemoryInfo* getMemoryInfo(); + Ort::Env* getEnv(); + int32_t getIntraOpNumThreads() const { return mIntraOpNumThreads; } + int32_t getInterOpNumThreads() const { return mInterOpNumThreads; } + + // Setters + void setDeviceId(int32_t id) { mDeviceId = id; } + void setIO(); + void setActiveThreads(int threads) { mIntraOpNumThreads = threads; } + void setIntraOpNumThreads(int threads) + { + if (mDeviceType == "CPU") { + mIntraOpNumThreads = threads; + } + } + void setInterOpNumThreads(int threads) + { + if (mDeviceType == "CPU") { + mInterOpNumThreads = threads; + } + } + void setEnv(Ort::Env*); + + // Conversion + template + std::vector v2v(std::vector
&, bool = true); + + // Inferencing + template // class I is the input data type, e.g. float, class O is the output data type, e.g. OrtDataType::Float16_t from O2/Common/ML/include/ML/GPUORTFloat16.h + std::vector inference(std::vector&); + + template + std::vector inference(std::vector>&); + + template + void inference(I*, int64_t, O*); + + template + void inference(I**, int64_t, O*); + + void release(bool = false); + + private: + // ORT variables -> need to be hidden as pImpl + struct OrtVariables; + std::unique_ptr mPImplOrt; + + // Input & Output specifications of the loaded network + std::vector mInputNamesChar, mOutputNamesChar; + std::vector mInputNames, mOutputNames; + std::vector> mInputShapes, mOutputShapes, mInputShapesCopy, mOutputShapesCopy; // Input shapes + std::vector mInputSizePerNode, mOutputSizePerNode; // Output shapes + int32_t mInputsTotal = 0, mOutputsTotal = 0; // Total number of inputs and outputs + + // Environment settings + bool mInitialized = false, mDeterministicMode = false; + std::string mModelPath, mEnvName = "", mDeviceType = "CPU", mThreadAffinity = ""; // device options should be cpu, rocm, migraphx, cuda + int32_t mIntraOpNumThreads = 1, mInterOpNumThreads = 1, mDeviceId = -1, mEnableProfiling = 0, mLoggingLevel = 0, mAllocateDeviceMemory = 0, mEnableOptimizations = 0; + + std::string printShape(const std::vector&); + std::string printShape(const std::vector>&, std::vector&); +}; + +} // namespace o2::ml + +#endif // O2_ML_ORTINTERFACE_H diff --git a/Common/ML/src/OrtInterface.cxx b/Common/ML/src/OrtInterface.cxx new file mode 100644 index 0000000000000..8f88ab18dacbd --- /dev/null +++ b/Common/ML/src/OrtInterface.cxx @@ -0,0 +1,499 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file OrtInterface.cxx +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#include "ML/OrtInterface.h" +#include "ML/3rdparty/GPUORTFloat16.h" + +// ONNX includes +#include + +#include + +namespace o2 +{ + +namespace ml +{ + +OrtModel::OrtModel() = default; +OrtModel::OrtModel(std::unordered_map optionsMap) { init(optionsMap); } +OrtModel::~OrtModel() = default; +void OrtModel::init(std::unordered_map optionsMap) +{ + initOptions(optionsMap); + initEnvironment(); +} + +struct OrtModel::OrtVariables { // The actual implementation is hidden in the .cxx file + // ORT runtime objects + Ort::RunOptions runOptions; + std::unique_ptr env = nullptr; + std::unique_ptr session = nullptr; ///< ONNX session + Ort::SessionOptions sessionOptions; + Ort::AllocatorWithDefaultOptions allocator; + Ort::MemoryInfo memoryInfo = Ort::MemoryInfo("Cpu", OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemType::OrtMemTypeDefault); + std::unique_ptr ioBinding = nullptr; +}; + +// General purpose +void OrtModel::initOptions(std::unordered_map optionsMap) +{ + mPImplOrt = std::make_unique(); + + // Load from options map + if (!optionsMap.contains("model-path")) { + LOG(fatal) << "(ORT) Model path must be contained in options map!"; + } + + if (!optionsMap["model-path"].empty()) { + mModelPath = optionsMap["model-path"]; + mDeviceType = (optionsMap.contains("device-type") ? optionsMap["device-type"] : "CPU"); + mDeviceId = (optionsMap.contains("device-id") ? std::stoi(optionsMap["device-id"]) : -1); + mAllocateDeviceMemory = (optionsMap.contains("allocate-device-memory") ? std::stoi(optionsMap["allocate-device-memory"]) : 0); + mIntraOpNumThreads = (optionsMap.contains("intra-op-num-threads") ? std::stoi(optionsMap["intra-op-num-threads"]) : 0); + mInterOpNumThreads = (optionsMap.contains("inter-op-num-threads") ? std::stoi(optionsMap["inter-op-num-threads"]) : 0); + mLoggingLevel = (optionsMap.contains("logging-level") ? std::stoi(optionsMap["logging-level"]) : 0); + mEnableProfiling = (optionsMap.contains("enable-profiling") ? std::stoi(optionsMap["enable-profiling"]) : 0); + mEnableOptimizations = (optionsMap.contains("enable-optimizations") ? std::stoi(optionsMap["enable-optimizations"]) : 0); + mEnvName = (optionsMap.contains("onnx-environment-name") ? optionsMap["onnx-environment-name"] : "onnx_model_inference"); + mDeterministicMode = (optionsMap.contains("deterministic-compute") ? std::stoi(optionsMap["deterministic-compute"]) : 0); + + if (mDeviceType == "CPU") { + (mPImplOrt->sessionOptions).SetIntraOpNumThreads(mIntraOpNumThreads); + (mPImplOrt->sessionOptions).SetInterOpNumThreads(mInterOpNumThreads); + if (mIntraOpNumThreads > 1 || mInterOpNumThreads > 1) { + (mPImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_PARALLEL); + } else if (mIntraOpNumThreads == 1) { + (mPImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL); + } + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) CPU execution provider set with " << mIntraOpNumThreads << " (mIntraOpNumThreads) and " << mInterOpNumThreads << " (mInterOpNumThreads) threads"; + } + } + + // OrtROCMProviderOptions rocm_options{}; + // (mPImplOrt->sessionOptions).AppendExecutionProvider_ROCM(rocm_options); + + (mPImplOrt->sessionOptions).DisableMemPattern(); + (mPImplOrt->sessionOptions).DisableCpuMemArena(); + + if (mEnableProfiling) { + if (optionsMap.contains("profiling-output-path")) { + (mPImplOrt->sessionOptions).EnableProfiling((optionsMap["profiling-output-path"] + "/ORT_LOG_").c_str()); + } else { + LOG(warning) << "(ORT) If profiling is enabled, optionsMap[\"profiling-output-path\"] should be set. Disabling profiling for now."; + (mPImplOrt->sessionOptions).DisableProfiling(); + } + } else { + (mPImplOrt->sessionOptions).DisableProfiling(); + } + + if (mDeterministicMode > 0) { + (mPImplOrt->sessionOptions).AddConfigEntry("session_options.use_deterministic_compute", "1"); + } + + (mPImplOrt->sessionOptions).SetGraphOptimizationLevel(GraphOptimizationLevel(mEnableOptimizations)); + (mPImplOrt->sessionOptions).SetLogSeverityLevel(OrtLoggingLevel(mLoggingLevel)); + + mInitialized = true; + } else { + LOG(fatal) << "(ORT) Model path cannot be empty!"; + } +} + +void OrtModel::initEnvironment() +{ + mPImplOrt->env = std::make_unique( + OrtLoggingLevel(mLoggingLevel), + (mEnvName.empty() ? "ORT" : mEnvName.c_str()), + // Integrate ORT logging into Fairlogger + [](void* param, OrtLoggingLevel severity, const char* category, const char* logid, const char* code_location, const char* message) { + if (severity == ORT_LOGGING_LEVEL_VERBOSE) { + LOG(debug) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_INFO) { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_WARNING) { + LOG(warning) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_ERROR) { + LOG(error) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else if (severity == ORT_LOGGING_LEVEL_FATAL) { + LOG(fatal) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } else { + LOG(info) << "(ORT) [" << logid << "|" << category << "|" << code_location << "]: " << message; + } + }, + (void*)3); + (mPImplOrt->env)->DisableTelemetryEvents(); // Disable telemetry events +} + +void OrtModel::initSessionFromBuffer(const char* buffer, size_t bufferSize) +{ + mPImplOrt->sessionOptions.AddConfigEntry("session.load_model_format", "ONNX"); + mPImplOrt->sessionOptions.AddConfigEntry("session.use_ort_model_bytes_directly", "1"); + + mPImplOrt->session = std::make_unique(*mPImplOrt->env, + buffer, + bufferSize, + mPImplOrt->sessionOptions); + mPImplOrt->ioBinding = std::make_unique(*mPImplOrt->session); + + setIO(); + + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Model loaded successfully from buffer! (inputs: " << printShape(mInputShapes, mInputNames) << ", outputs: " << printShape(mOutputShapes, mInputNames) << ")"; + } +} + +void OrtModel::initSession() +{ + if (mAllocateDeviceMemory) { + memoryOnDevice(mDeviceId); + } + mPImplOrt->session = std::make_unique(*mPImplOrt->env, mModelPath.c_str(), mPImplOrt->sessionOptions); + mPImplOrt->ioBinding = std::make_unique(*mPImplOrt->session); + + setIO(); + + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Model loaded successfully! (inputs: " << printShape(mInputShapes, mInputNames) << ", outputs: " << printShape(mOutputShapes, mInputNames) << ")"; + } +} + +void OrtModel::memoryOnDevice(int32_t deviceIndex) +{ + if (deviceIndex >= 0) { + (mPImplOrt->runOptions).AddConfigEntry("disable_synchronize_execution_providers", "1"); + (mPImplOrt->sessionOptions).AddConfigEntry("session.use_device_allocator_for_initializers", "1"); // See kOrtSessionOptionsUseDeviceAllocatorForInitializers, https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h + (mPImplOrt->sessionOptions).AddConfigEntry("session.use_env_allocators", "1"); // This should enable to use the volatile memory allocation defined in O2/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx; not working yet: ONNX still assigns new memory at init time + (mPImplOrt->sessionOptions).AddConfigEntry("session_options.enable_cpu_mem_arena", "0"); // This should enable to use the volatile memory allocation defined in O2/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx; not working yet: ONNX still assigns new memory at init time + // Arena memory shrinkage comes at performance cost + // For now prefer to use single allocation, enabled by O2/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu -> SetONNXGPUStream -> rocm_options.arena_extend_strategy = 0; + (mPImplOrt->runOptions).AddConfigEntry("memory.enable_memory_arena_shrinkage", ("gpu:" + std::to_string(deviceIndex)).c_str()); // See kOrtRunOptionsConfigEnableMemoryArenaShrinkage, https://github.com/microsoft/onnxruntime/blob/90c263f471bbce724e77d8e62831d3a9fa838b2f/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h#L27 + + std::string dev_mem_str = ""; + if (mDeviceType == "ROCM") { + dev_mem_str = "HipPinned"; + } + if (mDeviceType == "CUDA") { + dev_mem_str = "Cuda"; + } + mPImplOrt->memoryInfo = Ort::MemoryInfo(dev_mem_str.c_str(), OrtAllocatorType::OrtDeviceAllocator, deviceIndex, OrtMemType::OrtMemTypeDefault); + if (mLoggingLevel < 2) { + LOG(info) << "(ORT) Memory info set to on-device memory for device type " << mDeviceType << " with ID " << deviceIndex << " and mPImplOrt pointer " << mPImplOrt; + } + } +} + +void OrtModel::resetSession() +{ + mPImplOrt->session = std::make_unique(*(mPImplOrt->env), mModelPath.c_str(), mPImplOrt->sessionOptions); +} + +// Getters +Ort::SessionOptions* OrtModel::getSessionOptions() +{ + return &mPImplOrt->sessionOptions; +} + +Ort::MemoryInfo* OrtModel::getMemoryInfo() +{ + return &mPImplOrt->memoryInfo; +} + +Ort::Env* OrtModel::getEnv() +{ + return (mPImplOrt->env).get(); +} + +template +std::vector OrtModel::v2v(std::vector& input, bool clearInput) +{ + if constexpr (std::is_same_v) { + return input; + } else { + std::vector output(input.size()); + std::transform(std::begin(input), std::end(input), std::begin(output), [](I f) { return O(f); }); + if (clearInput) { + input.clear(); + } + return output; + } +} + +void OrtModel::setIO() +{ + for (size_t i = 0; i < (mPImplOrt->session)->GetInputCount(); ++i) { + mInputNames.push_back((mPImplOrt->session)->GetInputNameAllocated(i, mPImplOrt->allocator).get()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetInputCount(); ++i) { + mInputShapes.emplace_back((mPImplOrt->session)->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetOutputCount(); ++i) { + mOutputNames.push_back((mPImplOrt->session)->GetOutputNameAllocated(i, mPImplOrt->allocator).get()); + } + for (size_t i = 0; i < (mPImplOrt->session)->GetOutputCount(); ++i) { + mOutputShapes.emplace_back((mPImplOrt->session)->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + + mInputNamesChar.resize(mInputNames.size(), nullptr); + std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(mInputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + mOutputNamesChar.resize(mOutputNames.size(), nullptr); + std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(mOutputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + + mInputShapesCopy = mInputShapes; + mOutputShapesCopy = mOutputShapes; + mInputSizePerNode.resize(mInputShapes.size(), 1); + mOutputSizePerNode.resize(mOutputShapes.size(), 1); + mInputsTotal = 1; + for (size_t i = 0; i < mInputShapes.size(); ++i) { + if (mInputShapes[i].size() > 0) { + for (size_t j = 1; j < mInputShapes[i].size(); ++j) { + if (mInputShapes[i][j] > 0) { + mInputsTotal *= mInputShapes[i][j]; + mInputSizePerNode[i] *= mInputShapes[i][j]; + } + } + } + } + mOutputsTotal = 1; + for (size_t i = 0; i < mOutputShapes.size(); ++i) { + if (mOutputShapes[i].size() > 0) { + for (size_t j = 1; j < mOutputShapes[i].size(); ++j) { + if (mOutputShapes[i][j] > 0) { + mOutputsTotal *= mOutputShapes[i][j]; + mOutputSizePerNode[i] *= mOutputShapes[i][j]; + } + } + } + } +} + +void OrtModel::setEnv(Ort::Env* env) +{ + mPImplOrt->env.reset(env); +} + +// Inference +template +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape = mInputShapes[0]; + inputShape[0] = input.size(); + for (size_t i = 1; i < mInputShapes[0].size(); ++i) { + inputShape[0] /= mInputShapes[0][i]; + } + std::vector inputTensor; + if constexpr (std::is_same_v) { + inputTensor.emplace_back(Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + } else { + inputTensor.emplace_back(Ort::Value::CreateTensor(mPImplOrt->memoryInfo, input.data(), input.size(), inputShape.data(), inputShape.size())); + } + // input.clear(); + auto outputTensors = (mPImplOrt->session)->Run(mPImplOrt->runOptions, mInputNamesChar.data(), inputTensor.data(), inputTensor.size(), mOutputNamesChar.data(), mOutputNamesChar.size()); + O* outputValues = outputTensors[0].template GetTensorMutableData(); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template std::vector o2::ml::OrtModel::inference(std::vector&); +template std::vector o2::ml::OrtModel::inference(std::vector&); +template std::vector o2::ml::OrtModel::inference(std::vector&); + +template +void OrtModel::inference(I* input, int64_t input_size, O* output) +{ + // std::vector providers = Ort::GetAvailableProviders(); + // for (const auto& provider : providers) { + // LOG(info) << "Available Execution Provider: " << provider; + // } + std::vector inputShape{input_size, (int64_t)mInputShapes[0][1]}; + Ort::Value inputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + inputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(input), input_size * mInputShapes[0][1], inputShape.data(), inputShape.size()); + } else { + inputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, input, input_size * mInputShapes[0][1], inputShape.data(), inputShape.size()); + } + (mPImplOrt->ioBinding)->BindInput(mInputNames[0].c_str(), inputTensor); + + std::vector outputShape{input_size, mOutputShapes[0][1]}; + Ort::Value outputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + outputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, reinterpret_cast(output), input_size * mOutputShapes[0][1], outputShape.data(), outputShape.size()); + } else { + outputTensor = Ort::Value::CreateTensor(mPImplOrt->memoryInfo, output, input_size * mOutputShapes[0][1], outputShape.data(), outputShape.size()); + } + (mPImplOrt->ioBinding)->BindOutput(mOutputNames[0].c_str(), outputTensor); + + (mPImplOrt->session)->Run(mPImplOrt->runOptions, *mPImplOrt->ioBinding); + // mPImplOrt->session->Run( + // mPImplOrt->runOptions, + // mInputNamesChar.data(), + // &inputTensor, + // mInputNamesChar.size(), + // mOutputNamesChar.data(), + // &outputTensor, + // mOutputNamesChar.size()); +} + +template void OrtModel::inference(OrtDataType::Float16_t*, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(OrtDataType::Float16_t*, int64_t, float*); +template void OrtModel::inference(float*, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(float*, int64_t, float*); + +template +void OrtModel::inference(I** input, int64_t input_size, O* output) +{ + std::vector inputTensors(mInputShapesCopy.size()); + + for (size_t i = 0; i < mInputShapesCopy.size(); ++i) { + + mInputShapesCopy[i][0] = input_size; // batch-size + mOutputShapesCopy[i][0] = input_size; // batch-size + + if constexpr (std::is_same_v) { + inputTensors[i] = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(input[i]), + mInputSizePerNode[i] * input_size, + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size()); + } else { + inputTensors[i] = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + input[i], + mInputSizePerNode[i] * input_size, + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size()); + } + } + + Ort::Value outputTensor = Ort::Value(nullptr); + if constexpr (std::is_same_v) { + outputTensor = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(output), + mOutputSizePerNode[0] * input_size, // assumes that there is only one output node + mOutputShapesCopy[0].data(), + mOutputShapesCopy[0].size()); + } else { + outputTensor = Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + output, + mOutputSizePerNode[0] * input_size, // assumes that there is only one output node + mOutputShapesCopy[0].data(), + mOutputShapesCopy[0].size()); + } + + // === Run inference === + mPImplOrt->session->Run( + mPImplOrt->runOptions, + mInputNamesChar.data(), + inputTensors.data(), + mInputNamesChar.size(), + mOutputNamesChar.data(), + &outputTensor, + mOutputNamesChar.size()); +} + +template void OrtModel::inference(OrtDataType::Float16_t**, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(OrtDataType::Float16_t**, int64_t, float*); +template void OrtModel::inference(float**, int64_t, OrtDataType::Float16_t*); +template void OrtModel::inference(float**, int64_t, float*); + +template +std::vector OrtModel::inference(std::vector>& inputs) +{ + std::vector input_tensors; + + for (size_t i = 0; i < inputs.size(); ++i) { + + mInputShapesCopy[i][0] = inputs[i].size() / mInputSizePerNode[i]; // batch-size + + if constexpr (std::is_same_v) { + input_tensors.emplace_back( + Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + reinterpret_cast(inputs[i].data()), + mInputSizePerNode[i] * mInputShapesCopy[i][0], + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size())); + } else { + input_tensors.emplace_back( + Ort::Value::CreateTensor( + mPImplOrt->memoryInfo, + inputs[i].data(), + mInputSizePerNode[i] * mInputShapesCopy[i][0], + mInputShapesCopy[i].data(), + mInputShapesCopy[i].size())); + } + } + + int32_t totalOutputSize = mOutputsTotal * mInputShapesCopy[0][0]; + + // === Run inference === + auto output_tensors = mPImplOrt->session->Run( + mPImplOrt->runOptions, + mInputNamesChar.data(), + input_tensors.data(), + input_tensors.size(), + mOutputNamesChar.data(), + mOutputNamesChar.size()); + + // === Extract output values === + O* output_data = output_tensors[0].template GetTensorMutableData(); + std::vector output_vec(output_data, output_data + totalOutputSize); + output_tensors.clear(); + return output_vec; +} + +template std::vector OrtModel::inference(std::vector>&); +template std::vector OrtModel::inference(std::vector>&); + +// Release session +void OrtModel::release(bool profilingEnabled) +{ + mPImplOrt.reset(); +} + +// private +std::string OrtModel::printShape(const std::vector& v) +{ + std::stringstream ss(""); + for (size_t i = 0; i < v.size() - 1; i++) { + ss << v[i] << "x"; + } + ss << v[v.size() - 1]; + return ss.str(); +} + +std::string OrtModel::printShape(const std::vector>& v, std::vector& n) +{ + std::stringstream ss(""); + for (size_t i = 0; i < v.size(); i++) { + ss << n[i] << " -> ("; + for (size_t j = 0; j < v[i].size() - 1; j++) { + ss << v[i][j] << "x"; + } + ss << v[i][v[i].size() - 1] << "); "; + } + return ss.str(); +} + +} // namespace ml + +} // namespace o2 diff --git a/Common/MathUtils/CMakeLists.txt b/Common/MathUtils/CMakeLists.txt index 04fc2e9915028..d618bb8549175 100644 --- a/Common/MathUtils/CMakeLists.txt +++ b/Common/MathUtils/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library( src/Chebyshev3D.cxx src/Chebyshev3DCalc.cxx src/SymMatrixSolver.cxx + src/Tsallis.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist FairLogger::FairLogger @@ -23,7 +24,8 @@ o2_add_library( O2::GPUCommon ROOT::GenVector ROOT::Geom - Vc::Vc) + Vc::Vc + Boost::boost) o2_target_root_dictionary( MathUtils @@ -37,7 +39,9 @@ o2_target_root_dictionary( include/MathUtils/RandomRing.h include/MathUtils/Primitive2D.h include/MathUtils/SMatrixGPU.h - include/MathUtils/SymMatrixSolver.h) + include/MathUtils/SymMatrixSolver.h + include/MathUtils/Tsallis.h + include/MathUtils/LegendrePols.h) o2_add_test( CachingTF1 diff --git a/Common/MathUtils/include/MathUtils/BetheBlochAleph.h b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h new file mode 100644 index 0000000000000..bd72faffb0503 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef AliceO2_COMMON_BETHEBLOCH_H_ +#define AliceO2_COMMON_BETHEBLOCH_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::common +{ + +template +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +{ + T beta = bg / o2::gpu::GPUCommonMath::Sqrt(static_cast(1.) + bg * bg); + + T aa = o2::gpu::GPUCommonMath::Pow(beta, kp4); + T bb = o2::gpu::GPUCommonMath::Pow(static_cast(1.) / bg, kp5); + bb = o2::gpu::GPUCommonMath::Log(kp3 + bb); + + return (kp2 - aa - bb) * kp1 / aa; +} + +} // namespace o2::common + +#endif diff --git a/Common/MathUtils/include/MathUtils/Cartesian.h b/Common/MathUtils/include/MathUtils/Cartesian.h index 14509def6efb1..9b917707835a6 100644 --- a/Common/MathUtils/include/MathUtils/Cartesian.h +++ b/Common/MathUtils/include/MathUtils/Cartesian.h @@ -34,8 +34,8 @@ #include "GPUCommonMath.h" #include "CartesianGPU.h" #include "SMatrixGPU.h" - #endif + #include "GPUROOTCartesianFwd.h" #include "GPUROOTSMatrixFwd.h" @@ -93,6 +93,9 @@ class Rotation2D sn = mSin; } + value_t getCos() const { return mCos; } + value_t getSin() const { return mSin; } + template Point3D operator()(const Point3D& v) const { // local->master @@ -247,6 +250,34 @@ class Transform3D : public ROOT::Math::Transform3D ClassDefNV(Transform3D, 1); }; #endif // Disable for GPU + +// Aliasing of the Dot(SVector a, SVector b) operation between SVectors +#if (!defined(GPUCA_STANDALONE) || !defined(DGPUCA_NO_ROOT)) && !defined(GPUCA_GPUCODE) && !defined(GPUCOMMONRTYPES_H_ACTIVE) +template +inline T Dot(const SVector& lhs, const SVector& rhs) +{ + return ROOT::Math::Dot(lhs, rhs); +} + +template +inline SMatrix> Similarity(const SMatrix& lhs, const SMatrix>& rhs) +{ + return ROOT::Math::Similarity(lhs, rhs); +} +#else +template +GPUdi() T Dot(const SVector& lhs, const SVector& rhs) +{ + return o2::math_utils::detail::Dot(lhs, rhs); +} + +template +GPUdi() SMatrix> Similarity(const SMatrix& lhs, const SMatrix>& rhs) +{ + return o2::math_utils::detail::Similarity(lhs, rhs); +} +#endif // Disable for GPU + } // namespace math_utils } // namespace o2 diff --git a/Common/MathUtils/include/MathUtils/LegendrePols.h b/Common/MathUtils/include/MathUtils/LegendrePols.h new file mode 100644 index 0000000000000..d5e6a259573f4 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/LegendrePols.h @@ -0,0 +1,213 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LegendrePols.h +/// \brief Definition of the NDim Legendre Polynominals +/// \author felix.schlepper@cern.ch + +#ifndef LEGENDRE_NDIM_POLYNOMINAL_H_ +#define LEGENDRE_NDIM_POLYNOMINAL_H_ + +#include "TNamed.h" +#include "Math/IParamFunction.h" +#include "TMatrixD.h" + +#include + +#include + +namespace o2::math_utils +{ + +// Defines the 1D Legendre Polynominals with coefficients: +// w(u) = c_0 + c_1 * u + c_2 * 0.5 * (3 * u * u - 1) + ... +// for u in [-1.0, 1.0] +class Legendre1DPolynominal final : public TNamed, + public ROOT::Math::IParametricFunctionOneDim +{ + public: + Legendre1DPolynominal() = default; + Legendre1DPolynominal(const Legendre1DPolynominal&) = default; + Legendre1DPolynominal(Legendre1DPolynominal&&) = delete; + Legendre1DPolynominal& operator=(const Legendre1DPolynominal&) = default; + Legendre1DPolynominal& operator=(Legendre1DPolynominal&&) = delete; + Legendre1DPolynominal(unsigned int order) : fOrder(order) {} + Legendre1DPolynominal(const std::vector p) + : fOrder(p.size() - 1), fParams(p) {} + + double operator()(double x) const { return DoEvalPar(x, Parameters()); } + double operator()(int i, double x) const + { + return DoEvalParSingle(i, x, Parameters()); + } + + ~Legendre1DPolynominal() final = default; + + const double* Parameters() const final { return &fParams.front(); } + + virtual void SetParameters(const double* p) final + { + fParams = std::vector(p, p + NPar()); + } + + unsigned int NPar() const final { return fParams.size(); } + unsigned int NOrder() const { return fOrder; } + + ROOT::Math::IBaseFunctionOneDim* Clone() const final { return new Legendre1DPolynominal(fParams); } + TObject* Clone(const char* name) const final + { + auto n = new Legendre1DPolynominal(fParams); + n->SetName(name); + return n; + } + + private: + double DoEvalPar(double x, const double* p) const final + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + sum += p[iOrder] * boost::math::legendre_p(iOrder, x); + } + return sum; + } + + double DoEvalParSingle(int i, double x, const double* p) const + { + return p[i] * boost::math::legendre_p(i, x); + } + + unsigned int fOrder{0}; + std::vector fParams; + + ClassDefOverride(o2::math_utils::Legendre1DPolynominal, 1); +}; + +// Defines the 2D Legendre Polynominals with coefficients: +// w(u, v) = c_00 + +// c_10 * u + c_11 * v + +// c_20 * 0.5 * (3 * u * u - 1) + c_21 * u * v + c_22 * (3 * v * v - 1) + +/// .... +// for u&v in [-1.0, 1.0] +class Legendre2DPolynominal final : public TNamed, + public ROOT::Math::IParametricFunctionMultiDim +{ + public: + Legendre2DPolynominal() = default; + Legendre2DPolynominal(unsigned int order) : fOrder(order) {} + Legendre2DPolynominal(const std::vector& p) + : fOrder(p.size() - 1), fParams(p) {} + Legendre2DPolynominal(const TMatrixD& p) : fOrder(p.GetNrows() - 1) + { + fParams = std::vector(NPar()); + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + fParams[getFlatIdx(iOrder, jOrder)] = p(iOrder, jOrder); + } + } + } + ~Legendre2DPolynominal() final = default; + + double operator()(const double* x) const + { + return DoEvalPar(x, Parameters()); + } + double operator()(double x, double y) const { return DoEvalPar(x, y); } + double operator()(int i, int j, const double* x) const + { + return DoEvalParSingle(i, j, x, Parameters()); + } + double operator()(int i, int j, double x, double y) const + { + return DoEvalParSingle(i, j, x, y, Parameters()); + } + + const double* Parameters() const final { return &fParams.front(); } + + void SetParameters(const double* p) final + { + fParams = std::vector(p, p + NPar()); + } + + unsigned int NPar() const final + { + return fOrder * (fOrder + 1) / 2 + fOrder + 1; + } + unsigned int NDim() const final { return 2; } + unsigned int NOrder() const { return fOrder; } + + TMatrixD getCoefficients() const + { + TMatrixD m(fOrder + 1, fOrder + 1); + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + m(iOrder, jOrder) = fParams[getFlatIdx(iOrder, jOrder)]; + } + } + return m; + } + + void printCoefficients() const { getCoefficients().Print(); } + + // Unimplemented + ROOT::Math::IBaseFunctionMultiDim* Clone() const final { return new Legendre2DPolynominal(fParams); } + TObject* Clone(const char* name) const final + { + auto n = new Legendre2DPolynominal(fParams); + n->SetName(name); + return n; + } + + private: + double DoEvalPar(const double* x, const double* p) const final + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + sum += DoEvalParSingle(iOrder, jOrder, x, p); + } + } + return sum; + } + + double DoEvalPar(double x, double y) const + { + double sum{0.0}; + for (unsigned int iOrder{0}; iOrder <= fOrder; ++iOrder) { + for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { + sum += DoEvalParSingle(iOrder, jOrder, x, y, Parameters()); + } + } + return sum; + } + + double DoEvalParSingle(int i, int j, const double* x, const double* p) const + { + return DoEvalParSingle(i, j, x[0], x[1], p); + } + + double DoEvalParSingle(int i, int j, double x, double y, + const double* p) const + { + return p[getFlatIdx(i, j)] * boost::math::legendre_p(j, x) * + boost::math::legendre_p(i - j, y); + } + + inline int getFlatIdx(int i, int j) const { return i * (i + 1) / 2 + j; } + + unsigned int fOrder{0}; + std::vector fParams; + + ClassDefOverride(o2::math_utils::Legendre2DPolynominal, 1); +}; + +} // namespace o2::math_utils + +#endif diff --git a/Common/MathUtils/include/MathUtils/SMatrixGPU.h b/Common/MathUtils/include/MathUtils/SMatrixGPU.h index 2faaf118d1daa..8158a93666a92 100644 --- a/Common/MathUtils/include/MathUtils/SMatrixGPU.h +++ b/Common/MathUtils/include/MathUtils/SMatrixGPU.h @@ -15,10 +15,6 @@ /// Only parts strictly requiring STD library have been changed. /// Also some utilities to have basic checks and printouts working on GPUs have been rewritten. /// -/// Notably only templated implementation of -/// row_offsets_utils::make and row_offsets_utils::do_make -/// has been reworked to support gpustd::array as backend. -/// /// Other than that, the author is not taking any credit on the methodologies implemented /// which have been taken straight from root source code /// @@ -29,11 +25,15 @@ #define ALICEO2_SMATRIX_GPU_H #include "GPUCommonDef.h" -#include "GPUCommonArray.h" #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" +#include "GPUCommonLogger.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#endif -namespace o2::math_utils +namespace o2::math_utils::detail { template struct Check { @@ -66,9 +66,9 @@ class SVectorGPU GPUd() SVectorGPU(); GPUd() SVectorGPU(const SVectorGPU& rhs); - GPUd() const T& operator[](unsigned int i) const; + GPUhd() const T& operator[](unsigned int i) const; + GPUhd() T& operator[](unsigned int i); GPUd() const T& operator()(unsigned int i) const; - GPUd() T& operator[](unsigned int i); GPUd() T& operator()(unsigned int i); GPUd() const T* Array() const; GPUd() T* Array(); @@ -113,19 +113,19 @@ GPUdi() const T* SVectorGPU::end() const } template -GPUdi() const T& SVectorGPU::operator[](unsigned int i) const +GPUhdi() const T& SVectorGPU::operator[](unsigned int i) const { return mArray[i]; } template -GPUdi() const T& SVectorGPU::operator()(unsigned int i) const +GPUhdi() T& SVectorGPU::operator[](unsigned int i) { return mArray[i]; } template -GPUdi() T& SVectorGPU::operator[](unsigned int i) +GPUdi() const T& SVectorGPU::operator()(unsigned int i) const { return mArray[i]; } @@ -140,7 +140,7 @@ template GPUd() SVectorGPU::SVectorGPU() { for (unsigned int i = 0; i < N; ++i) { - mArray[i] = 7; + mArray[i] = 0; } } @@ -204,7 +204,7 @@ GPUdi() SVectorGPU& SVectorGPU::operator-=(const SVectorGPU& r } template -GPUd() SVectorGPU& SVectorGPU::operator+=(const SVectorGPU& rhs) +GPUdi() SVectorGPU& SVectorGPU::operator+=(const SVectorGPU& rhs) { for (unsigned int i = 0; i < D; ++i) { mArray[i] += rhs.apply(i); @@ -283,14 +283,14 @@ struct make_indices : make_indices_impl<0, indices<>, N> { }; template -constexpr auto do_make(F f, indices) -> gpu::gpustd::array +constexpr auto do_make(F f, indices) -> std::array { - gpu::gpustd::array retarr = {f(I0 + I)...}; + std::array retarr = {f(I0 + I)...}; return retarr; } template -constexpr auto make(F f) -> gpu::gpustd::array +constexpr auto make(F f) -> std::array { return do_make(f, typename make_indices::type()); } @@ -303,31 +303,11 @@ class MatRepSymGPU public: typedef T value_type; GPUdDefault() MatRepSymGPU() = default; - GPUdi() T& operator()(unsigned int i, unsigned int j) - { - return mArray[offset(i, j)]; - } - - GPUdi() T const& operator()(unsigned int i, unsigned int j) const - { - return mArray[offset(i, j)]; - } - - GPUdi() T& operator[](unsigned int i) - { - return mArray[off(i)]; - } - - GPUdi() T const& operator[](unsigned int i) const - { - return mArray[off(i)]; - } - - GPUdi() T apply(unsigned int i) const - { - return mArray[off(i)]; - } - + GPUdi() T& operator()(unsigned int i, unsigned int j) { return mArray[offset(i, j)]; } + GPUdi() T const& operator()(unsigned int i, unsigned int j) const { return mArray[offset(i, j)]; } + GPUhdi() T& operator[](unsigned int i) { return mArray[off(i)]; } + GPUdi() T const& operator[](unsigned int i) const { return mArray[off(i)]; } + GPUdi() T apply(unsigned int i) const { return mArray[off(i)]; } GPUdi() T* Array() { return mArray; } GPUdi() const T* Array() const { return mArray; } @@ -469,6 +449,8 @@ class SMatrixGPU GPUdi() SMatrixGPU(SMatrixNoInit) {} GPUd() SMatrixGPU(SMatrixIdentity); GPUd() SMatrixGPU(const SMatrixGPU& rhs); + template + GPUd() SMatrixGPU(const SMatrixGPU& rhs); template GPUd() SMatrixGPU(const Expr& rhs); template @@ -480,14 +462,18 @@ class SMatrixGPU kCols = D2, // columns kSize = D1 * D2 // rows*columns }; - GPUd() T apply(unsigned int i) const; - GPUd() const T* Array() const; - GPUd() T* Array(); + // https://root.cern/doc/master/SMatrix_8icc_source.html#l00627 + GPUd() T apply(unsigned int i) const { return mRep[i]; } + GPUd() const T* Array() const { return mRep.Array(); } + GPUd() T* Array() { return mRep.Array(); } GPUd() iterator begin(); GPUd() iterator end(); GPUd() const T& operator()(unsigned int i, unsigned int j) const; GPUd() T& operator()(unsigned int i, unsigned int j); + template + GPUd() friend X& operator<<(Y& y, const SMatrixGPU&); + class SMatrixRowGPU { public: @@ -517,6 +503,14 @@ class SMatrixGPU GPUd() SMatrixRowGPUconst operator[](unsigned int i) const { return SMatrixRowGPUconst(*this, i); } GPUd() SMatrixRowGPU operator[](unsigned int i) { return SMatrixRowGPU(*this, i); } + template + GPUd() SMatrixGPU& operator+=(const SMatrixGPU& rhs); + GPUd() SMatrixGPU& operator*=(const T& rhs); + template + GPUd() SMatrixGPU& operator*=(const SMatrixGPU& rhs); + template + GPUd() SMatrixGPU& operator*=(const Expr& rhs); + GPUd() bool Invert(); GPUd() bool IsInUse(const T* p) const; @@ -524,6 +518,15 @@ class SMatrixGPU R mRep; }; +#ifndef __OPENCL__ // TODO: current C++ for OpenCL 2021 is at C++17, so no concepts. But we don't need this trick for OpenCL anyway, so we can just hide it. +template + requires(sizeof(typename X::traits_type::pos_type) != 0) // do not provide a template to fair::Logger, etc... (pos_type is a member type of all std::ostream classes) +GPUd() X& operator<<(Y& y, const SMatrixGPU&) +{ + return y; +} +#endif + template GPUdi() SMatrixGPU::SMatrixGPU(SMatrixIdentity) { @@ -547,6 +550,13 @@ GPUdi() SMatrixGPU::SMatrixGPU(const SMatrixGPU& rhs mRep = rhs.mRep; } +template +template +GPUd() SMatrixGPU::SMatrixGPU(const SMatrixGPU& rhs) +{ + operator=(rhs); +} + template GPUdi() T* SMatrixGPU::begin() { @@ -675,7 +685,7 @@ GPUdi() SMatrixGPU& SMatrixGPU::operator=(const Expr template template -GPUdi() SMatrixGPU& SMatrixGPU::operator=(const M& rhs) +GPUdi() SMatrixGPU& SMatrixGPU::operator=(const M & rhs) { mRep = rhs.mRep; return *this; @@ -980,7 +990,7 @@ GPUdi() void Inverter::InvertBunchKaufman(MatRepSymGPU& rhs, int& if // invert D(j:j+1,j:j+1) temp2 = *mjj * *(mjj + j + 1) - *(mjj + j) * *(mjj + j); if (temp2 == 0) { - printf("SymMatrix::bunch_invert: error in pivot choice"); + LOGF(error, "SymMatrix::bunch_invert: error in pivot choice"); } temp2 = 1. / temp2; // this quotient is guaranteed to exist by the choice @@ -1067,10 +1077,10 @@ GPUdi() void Inverter::InvertBunchKaufman(MatRepSymGPU& rhs, int& if } *mjj -= static_cast(temp2); } - } else //2x2 pivot, compute columns j and j-1 of the inverse + } else // 2x2 pivot, compute columns j and j-1 of the inverse { if (piv[j - 1] != 0) { - printf("error in piv %lf \n", piv[j - 1]); + LOGF(error, "error in piv %lf \n", static_cast(piv[j - 1])); } s = 2; if (j < nrow) { @@ -1344,7 +1354,7 @@ GPUdi() int Inverter::DfinvMatrix(MatRepStdGPU& rhs, unsigned int for (unsigned int i = 1; i < n; i++) { unsigned int ni = n - i; mIter mij = mi; - //int j; + // int j; for (unsigned j = 1; j <= i; j++) { s33 = *mij; mIter mikj = mi + n + j - 1; @@ -1398,6 +1408,37 @@ GPUdi() bool SMatrixGPU::Invert() return Inverter::Dinv((*this).mRep); } +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator+=(const SMatrixGPU& rhs) +{ + mRep += rhs.mRep; + return *this; +} + +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const T & rhs) +{ + for (unsigned int i = 0; i < R::kSize; ++i) { + mRep.Array()[i] *= rhs; + } + return *this; +} + +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const SMatrixGPU& rhs) +{ + return operator=(*this* rhs); +} + +template +template +GPUdi() SMatrixGPU& SMatrixGPU::operator*=(const Expr& rhs) +{ + return operator=(*this* rhs); +} + template struct TranspPolicyGPU { enum { @@ -1424,6 +1465,7 @@ class TransposeOpGPU { return mRhs.apply((i % D1) * D2 + i / D1); } + GPUdi() T operator()(unsigned int i, unsigned j) const { return mRhs(j, i); @@ -1467,5 +1509,5 @@ GPUdi() SMatrixGPU> Similarity(const SMatrixGPU1) used to check for sampling + /// \param mass particles mass (use pion if not known) + static bool downsampleTsallisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass = 0.13957); +}; + +} // namespace math_utils +} // namespace o2 + +#endif diff --git a/Common/MathUtils/include/MathUtils/Utils.h b/Common/MathUtils/include/MathUtils/Utils.h index 618d3e379f6d2..79263b4142216 100644 --- a/Common/MathUtils/include/MathUtils/Utils.h +++ b/Common/MathUtils/include/MathUtils/Utils.h @@ -128,17 +128,17 @@ GPUdi() void rotateZd(double xL, double yL, double& xG, double& yG, double snAlp return detail::rotateZ(xL, yL, xG, yG, snAlp, csAlp); } -#ifndef GPUCA_GPUCODE_DEVICE -inline void rotateZInv(float xG, float yG, float& xL, float& yL, float snAlp, float csAlp) +GPUdi() void rotateZInv(float xG, float yG, float& xL, float& yL, float snAlp, float csAlp) { detail::rotateZInv(xG, yG, xL, yL, snAlp, csAlp); } -inline void rotateZInvd(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) +GPUdi() void rotateZInvd(double xG, double yG, double& xL, double& yL, double snAlp, double csAlp) { detail::rotateZInv(xG, yG, xL, yL, snAlp, csAlp); } +#ifndef GPUCA_GPUCODE_DEVICE inline std::tuple rotateZInv(float xG, float yG, float snAlp, float csAlp) { return detail::rotateZInv(xG, yG, snAlp, csAlp); diff --git a/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h index 50a09804da6ed..abb8a716cc5ee 100644 --- a/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h +++ b/Common/MathUtils/include/MathUtils/detail/StatAccumulator.h @@ -66,6 +66,7 @@ struct StatAccumulator { sum += other.sum; sum2 += other.sum2; wsum += other.wsum; + n += other.n; return *this; } diff --git a/Common/MathUtils/include/MathUtils/detail/basicMath.h b/Common/MathUtils/include/MathUtils/detail/basicMath.h index 5dd66182e47d8..1abe6ee878c39 100644 --- a/Common/MathUtils/include/MathUtils/detail/basicMath.h +++ b/Common/MathUtils/include/MathUtils/detail/basicMath.h @@ -16,14 +16,15 @@ #ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ #define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "CommonConstants/MathConstants.h" + #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #endif -#include "GPUCommonArray.h" -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "CommonConstants/MathConstants.h" namespace o2 { @@ -65,7 +66,7 @@ GPUhdi() T abs(T x) template GPUdi() int nint(T x) { - return o2::gpu::GPUCommonMath::Nint(x); + return o2::gpu::GPUCommonMath::Float2IntRn(x); }; template @@ -113,7 +114,11 @@ GPUdi() int nint(double x) template <> GPUdi() bool finite(double x) { +#ifdef __FAST_MATH__ + return false; +#else return std::isfinite(x); +#endif } template <> GPUdi() double log(double x) @@ -126,4 +131,4 @@ GPUdi() double log(double x) } // namespace math_utils } // namespace o2 -#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ */ \ No newline at end of file +#endif /* MATHUTILS_INCLUDE_MATHUTILS_DETAIL_BASICMATH_H_ */ diff --git a/Common/MathUtils/include/MathUtils/detail/trigonometric.h b/Common/MathUtils/include/MathUtils/detail/trigonometric.h index a216b80aaa203..457210202ca54 100644 --- a/Common/MathUtils/include/MathUtils/detail/trigonometric.h +++ b/Common/MathUtils/include/MathUtils/detail/trigonometric.h @@ -16,16 +16,17 @@ #ifndef MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ #define MATHUTILS_INCLUDE_MATHUTILS_DETAIL_TRIGONOMETRIC_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif -#include "GPUCommonArray.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" #include "CommonConstants/MathConstants.h" #include "MathUtils/detail/basicMath.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#include +#include +#endif + namespace o2 { namespace math_utils @@ -156,7 +157,7 @@ GPUhdi() std::tuple rotateZInv(T xG, T yG, T snAlp, T csAlp) #endif template -GPUhdi() void rotateZ(gpu::gpustd::array& xy, T alpha) +GPUhdi() void rotateZ(std::array& xy, T alpha) { // transforms vector in tracking frame alpha to global frame T sin, cos; @@ -196,7 +197,7 @@ GPUhdi() int angle2Sector(T phi) if (phi < 0.f) { sect += o2::constants::math::NSectors - 1; } - return sect; + return sect % o2::constants::math::NSectors; } template diff --git a/Common/MathUtils/include/MathUtils/fit.h b/Common/MathUtils/include/MathUtils/fit.h index 1f7223605a99b..cd5cb415070d3 100644 --- a/Common/MathUtils/include/MathUtils/fit.h +++ b/Common/MathUtils/include/MathUtils/fit.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Rtypes.h" #include "TLinearFitter.h" @@ -59,7 +60,7 @@ template TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, TF1& func, std::string_view option = "") { Foption_t fitOption; - ROOT::Fit::FitOptionsMake(ROOT::Fit::kHistogram, option.data(), fitOption); + ROOT::Fit::FitOptionsMake(ROOT::Fit::EFitObjectType::kHistogram, option.data(), fitOption); ROOT::Fit::DataRange range(xMin, xMax); ROOT::Fit::DataOptions opt; @@ -69,9 +70,9 @@ TFitResultPtr fit(const size_t nBins, const T* arr, const T xMin, const T xMax, // create an empty TFitResult std::shared_ptr tfr(new TFitResult()); // create the fitter from an empty fit result - //std::shared_ptr fitter(new ROOT::Fit::Fitter(std::static_pointer_cast(tfr) ) ); + // std::shared_ptr fitter(new ROOT::Fit::Fitter(std::static_pointer_cast(tfr) ) ); ROOT::Fit::Fitter fitter(tfr); - //ROOT::Fit::FitConfig & fitConfig = fitter->Config(); + // ROOT::Fit::FitConfig & fitConfig = fitter->Config(); const double binWidth = double(xMax - xMin) / double(nBins); @@ -225,8 +226,8 @@ bool medmadGaus(size_t nBins, const T* arr, const T xMin, const T xMax, std::arr /// -1: only one point has been used for the calculation - center of gravity was uesed for calculation /// -4: invalid result!! /// -//template -//Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); +// template +// Double_t fitGaus(const size_t nBins, const T *arr, const T xMin, const T xMax, std::vector& param); template Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, std::vector& param) { @@ -301,7 +302,7 @@ Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, s Double_t chi2 = 0; if (npoints >= 3) { if (npoints == 3) { - //analytic calculation of the parameters for three points + // analytic calculation of the parameters for three points A.Invert(); TMatrixD res(1, 3); res.Mult(A, b); @@ -334,7 +335,7 @@ Double_t fitGaus(const size_t nBins, const T* arr, const T xMin, const T xMax, s } if (npoints == 2) { - //use center of gravity for 2 points + // use center of gravity for 2 points meanCOG /= sumCOG; rms2COG /= sumCOG; param[0] = max; @@ -524,7 +525,7 @@ R median(std::vector v) auto n = v.size() / 2; nth_element(v.begin(), v.begin() + n, v.end()); auto med = R{v[n]}; - if (!(v.size() & 1)) { //If the set size is even + if (!(v.size() & 1)) { // If the set size is even auto max_it = max_element(v.begin(), v.begin() + n); med = R{(*max_it + med) / 2.0}; } @@ -712,6 +713,245 @@ bool LTMUnbinnedSig(const std::vector& data, std::vector& index, std: params[4] = params[3] / std::sqrt(2.0); // error on RMS return true; } + +//___________________________________________________________________ +template +T selKthMin(int k, int np, T* arr) +{ + // Returns the k th smallest value in the array. The input array will be rearranged + // to have this value in location arr[k] , with all smaller elements moved before it + // (in arbitrary order) and all larger elements after (also in arbitrary order). + // From Numerical Recipes in C++ + + int i, j, mid, ir = np - 1, l = 0; + T a; + for (;;) { + if (ir <= l + 1) { + if (ir == l + 1 && arr[ir] < arr[l]) { + std::swap(arr[l], arr[ir]); + } + return arr[k]; + } else { + int mid = (l + ir) >> 1, i = l + 1; + std::swap(arr[mid], arr[i]); + if (arr[i] > arr[ir]) { + std::swap(arr[i], arr[ir]); + } + if (arr[l] > arr[ir]) { + std::swap(arr[l], arr[ir]); + } + if (arr[i] > arr[l]) { + std::swap(arr[i], arr[l]); + } + j = ir; + a = arr[l]; + for (;;) { + do { + i++; + } while (arr[i] < a); + do { + j--; + } while (arr[j] > a); + if (j < i) { + break; + } + std::swap(arr[i], arr[j]); + } + arr[l] = arr[j]; + arr[j] = a; + if (j >= k) { + ir = j - 1; + } + if (j <= k) { + l = i; + } + } + } +} + +//___________________________________________________________________ +template +T MAD2Sigma(int np, T* y) +{ + // Sigma calculated from median absolute deviations, https://en.wikipedia.org/wiki/Median_absolute_deviation + // the input array is modified + if (np < 2) { + return 0; + } + int nph = np >> 1; + float median = (np & 0x1) ? selKthMin(nph, np, y) : 0.5 * (selKthMin(nph - 1, np, y) + selKthMin(nph, np, y)); + // build abs differences to median + for (int i = np; i--;) { + y[i] = std::abs(y[i] - median); + } + // now get median of abs deviations + median = (np & 0x1) ? selKthMin(nph, np, y) : 0.5 * (selKthMin(nph - 1, np, y) + selKthMin(nph, np, y)); + return median * 1.4826; // convert to Gaussian sigma +} + +/// \return returns the index of the closest timestamps to the left and right of the given timestamp +/// \param timestamps vector of timestamps +/// \param timestamp the timestamp to find the closest timestamps for +template +std::optional> findClosestIndices(const std::vector& timestamps, DataTime timestamp) +{ + if (timestamps.empty()) { + LOGP(warning, "Timestamp vector is empty!"); + return std::nullopt; + } + + if (timestamp <= timestamps.front()) { + return std::pair{0, 0}; + } else if (timestamp >= timestamps.back()) { + return std::pair{timestamps.size() - 1, timestamps.size() - 1}; + } + + const auto it = std::lower_bound(timestamps.begin(), timestamps.end(), timestamp); + const size_t idx = std::distance(timestamps.begin(), it); + const auto prevTimestamp = timestamps[idx - 1]; + const auto nextTimestamp = timestamps[idx]; + return std::pair{(idx - 1), idx}; +} + +struct RollingStats { + RollingStats() = default; + RollingStats(const int nValues) + { + median.resize(nValues); + std.resize(nValues); + nPoints.resize(nValues); + closestDistanceL.resize(nValues); + closestDistanceR.resize(nValues); + } + + std::vector median; ///< median of rolling data + std::vector std; ///< std of rolling data + std::vector nPoints; ///< number of points used for the calculation + std::vector closestDistanceL; ///< distance of closest point to the left + std::vector closestDistanceR; ///< distance of closest point to the right + + ClassDefNV(RollingStats, 1); +}; + +/// \brief calculates the rolling statistics of the input data +/// \return returns the rolling statistics +/// \param timeData times of the input data (assumed to be sorted) +/// \param data values of the input data +/// \param times times for which to calculate the rolling statistics +/// \param deltaMax time range for which the rolling statistics is calculated +/// \param mNthreads number of threads to use for the calculation +/// \param minPoints minimum number of points to use for the calculation of the statistics - otherwise use nearest nClosestPoints points weighted with distance +/// \param nClosestPoints number of closest points in case of number of points in given range is smaller than minPoints +template +RollingStats getRollingStatistics(const DataTimeType& timeData, const DataType& data, const DataTime& times, const double deltaMax, const int mNthreads, const size_t minPoints = 4, const size_t nClosestPoints = 4) +{ + // output statistics + const size_t vecSize = times.size(); + RollingStats stats(vecSize); + + if (!std::is_sorted(timeData.begin(), timeData.end())) { + LOGP(error, "Input data is NOT sorted!"); + return stats; + } + + if (timeData.empty()) { + LOGP(error, "Input data is empty!"); + return stats; + } + + const size_t dataSize = data.size(); + const size_t timeDataSize = timeData.size(); + if (timeDataSize != dataSize) { + LOGP(error, "Input data has different sizes {}!={}", timeDataSize, dataSize); + return stats; + } + + auto myThread = [&](int iThread) { + // data in given time window for median calculation + DataType window; + for (size_t i = iThread; i < vecSize; i += mNthreads) { + const double timeI = times[i]; + + // lower index + const double timeStampLower = timeI - deltaMax; + const auto lower = std::lower_bound(timeData.begin(), timeData.end(), timeStampLower); + size_t idxStart = std::distance(timeData.begin(), lower); + + // upper index + const double timeStampUpper = timeI + deltaMax; + const auto upper = std::lower_bound(timeData.begin(), timeData.end(), timeStampUpper); + size_t idxEnd = std::distance(timeData.begin(), upper); + + // closest data point + if (auto idxClosest = findClosestIndices(timeData, timeI)) { + auto [idxLeft, idxRight] = *idxClosest; + const auto closestL = std::abs(timeData[idxLeft] - timeI); + const auto closestR = std::abs(timeData[idxRight] - timeI); + stats.closestDistanceL[i] = closestL; + stats.closestDistanceR[i] = closestR; + + // if no points are in the range use the n closest points - n from the left and n from the right + const size_t reqSize = idxEnd - idxStart; + if (reqSize < minPoints) { + // calculate weighted average + idxStart = (idxRight > nClosestPoints) ? (idxRight - nClosestPoints) : 0; + idxEnd = std::min(data.size(), idxRight + nClosestPoints); + constexpr float epsilon = 1e-6f; + double weightedSum = 0.0; + double weightTotal = 0.0; + for (size_t j = idxStart; j < idxEnd; ++j) { + const double dist = std::abs(timeI - timeData[j]); + const double weight = 1.0 / (dist + epsilon); + weightedSum += weight * data[j]; + weightTotal += weight; + } + stats.median[i] = (weightTotal > 0.) ? (weightedSum / weightTotal) : 0.0f; + } else { + // calculate statistics + stats.nPoints[i] = reqSize; + + if (idxStart >= data.size()) { + stats.median[i] = data.back(); + continue; + } + + if (reqSize <= 1) { + stats.median[i] = data[idxStart]; + continue; + } + + // calculate median + window.clear(); + if (reqSize > window.capacity()) { + window.reserve(static_cast(reqSize * 1.5)); + } + window.insert(window.end(), data.begin() + idxStart, data.begin() + idxEnd); + const size_t middle = window.size() / 2; + std::nth_element(window.begin(), window.begin() + middle, window.end()); + stats.median[i] = (window.size() % 2 == 1) ? window[middle] : ((window[middle - 1] + window[middle]) / 2.0); + + // calculate the stdev + const float mean = std::accumulate(window.begin(), window.end(), 0.0f) / window.size(); + std::transform(window.begin(), window.end(), window.begin(), [mean](const float val) { return val - mean; }); + const float sqsum = std::inner_product(window.begin(), window.end(), window.begin(), 0.0f); + const float stdev = std::sqrt(sqsum / window.size()); + stats.std[i] = stdev; + } + } + } + }; + + std::vector threads(mNthreads); + for (int i = 0; i < mNthreads; i++) { + threads[i] = std::thread(myThread, i); + } + + for (auto& th : threads) { + th.join(); + } + return stats; +} + } // namespace math_utils } // namespace o2 #endif diff --git a/Common/MathUtils/src/MathUtilsLinkDef.h b/Common/MathUtils/src/MathUtilsLinkDef.h index d042a054c559e..0b070e537afcd 100644 --- a/Common/MathUtils/src/MathUtilsLinkDef.h +++ b/Common/MathUtils/src/MathUtilsLinkDef.h @@ -41,5 +41,11 @@ #pragma link C++ class o2::math_utils::IntervalXYd_t + ; #pragma link C++ class o2::math_utils::Bracketf_t + ; #pragma link C++ class o2::math_utils::Bracketd_t + ; +#pragma link C++ class o2::math_utils::Tsallis + ; + +#pragma link C++ class o2::math_utils::Legendre1DPolynominal + ; +#pragma link C++ class o2::math_utils::Legendre2DPolynominal + ; + +#pragma link C++ class o2::math_utils::RollingStats + ; #endif diff --git a/Common/MathUtils/src/Tsallis.cxx b/Common/MathUtils/src/Tsallis.cxx new file mode 100644 index 0000000000000..850c249f0441f --- /dev/null +++ b/Common/MathUtils/src/Tsallis.cxx @@ -0,0 +1,39 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "MathUtils/Tsallis.h" +#include + +namespace o2::math_utils +{ + +float Tsallis::tsallisCharged(float pt, float mass, float sqrts) +{ + const float a = 6.81; + const float b = 59.24; + const float c = 0.082; + const float d = 0.151; + const float mt = std::sqrt(mass * mass + pt * pt); + const float n = a + b / sqrts; + const float T = c + d / sqrts; + const float p0 = n * T; + return std::pow((1. + mt / p0), -n) * pt; +} + +bool Tsallis::downsampleTsallisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass) +{ + const float prob = tsallisCharged(pt, mass, sqrts); + const float probNorm = tsallisCharged(1., mass, sqrts); + weight = prob / probNorm; + return (rnd * (weight * pt * pt)) < factorPt; +} + +} // namespace o2::math_utils diff --git a/Common/SimConfig/CMakeLists.txt b/Common/SimConfig/CMakeLists.txt index aa78dd8ad1a34..f8e007209eacc 100644 --- a/Common/SimConfig/CMakeLists.txt +++ b/Common/SimConfig/CMakeLists.txt @@ -12,10 +12,14 @@ o2_add_library(SimConfig SOURCES src/SimConfig.cxx src/SimParams.cxx + src/SimDLLoader.cxx src/SimUserDecay.cxx - src/DigiParams.cxx src/G4Params.cxx + src/DigiParams.cxx + src/G4Params.cxx + src/DetectorLists.cxx src/MatMapParams.cxx src/InteractionDiamondParam.cxx + src/GlobalProcessCutSimParam.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils O2::DetectorsCommonDataFormats O2::SimulationDataFormat FairRoot::Base Boost::program_options) @@ -24,10 +28,13 @@ o2_add_library(SimConfig o2_target_root_dictionary(SimConfig HEADERS include/SimConfig/SimConfig.h include/SimConfig/SimParams.h + include/SimConfig/SimDLLoader.h include/SimConfig/SimUserDecay.h include/SimConfig/InteractionDiamondParam.h - include/SimConfig/DigiParams.h + include/SimConfig/DigiParams.h include/SimConfig/G4Params.h + include/SimConfig/DetectorLists.h + include/SimConfig/GlobalProcessCutSimParam.h include/SimConfig/MatMapParams.h) o2_add_test(Config diff --git a/Common/SimConfig/include/SimConfig/DetectorLists.h b/Common/SimConfig/include/SimConfig/DetectorLists.h new file mode 100644 index 0000000000000..bdabe71db0872 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/DetectorLists.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_DETECTORLISTS_H_ +#define O2_DETECTORLISTS_H_ + +#include +#include +#include + +#include "Framework/Logger.h" + +namespace o2::conf +{ +// Container defining different general evolutions of the ALICE experiment. Each +// evolution is given a name and a list defining the names of the detectors and +// passive elements present. +using DetectorList_t = std::vector; +using DetectorMap_t = std::unordered_map; + +// Parse the detector map from a JSON file. +// Return false if parsing failed. +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map); + +// Print the DetetectorMap +void printDetMap(const DetectorMap_t& map, const std::string& list = ""); +} // namespace o2::conf + +#endif // O2_DETECTORLISTS_H_ diff --git a/Common/SimConfig/include/SimConfig/DigiParams.h b/Common/SimConfig/include/SimConfig/DigiParams.h index ecbd79f3de12f..7692ceff057ea 100644 --- a/Common/SimConfig/include/SimConfig/DigiParams.h +++ b/Common/SimConfig/include/SimConfig/DigiParams.h @@ -33,6 +33,7 @@ struct DigiParams : public o2::conf::ConfigurableParamHelper { int maxOrbitsToDigitize = -1; // Digitizer can use this to truncate digits that fall beyond an orbit limit (relative to start of digization) given by this param; -1 means no limit imposed // This parameter should typically be set to coincide with a single timeframe length or multiples thereof. std::string passName = "unanchored"; // passName for anchored MC + int seed = 0; // rndSeed to be applied in digitization; convention is that 0 is time based O2ParamDef(DigiParams, "DigiParams"); }; diff --git a/Common/SimConfig/include/SimConfig/G4Params.h b/Common/SimConfig/include/SimConfig/G4Params.h index 3c6506bddf801..aa8aa05263c0a 100644 --- a/Common/SimConfig/include/SimConfig/G4Params.h +++ b/Common/SimConfig/include/SimConfig/G4Params.h @@ -22,15 +22,33 @@ namespace conf // enumerating the possible G4 physics settings enum class EG4Physics { - kFTFP_BERT_optical = 0, /* just ordinary */ - kFTFP_BERT_optical_biasing = 1, /* with biasing enabled */ - kFTFP_INCLXX_optical = 2, /* special INCL++ version */ - kFTFP_BERT_HP_optical = 3 /* enable low energy neutron transport */ + kFTFP_BERT_optical = 0, /* just ordinary */ + kFTFP_BERT_optical_biasing = 1, /* with biasing enabled */ + kFTFP_INCLXX_optical = 2, /* special INCL++ version */ + kFTFP_BERT_HP_optical = 3, /* enable low energy neutron transport */ + kFTFP_BERT_EMV_optical = 4, /* just ordinary with faster electromagnetic physics */ + kFTFP_BERT_EMV_optical_biasing = 5, /* with biasing enabled with faster electromagnetic physics */ + kFTFP_INCLXX_EMV_optical = 6, /* special INCL++ version */ + kFTFP_BERT_EMV_HP_optical = 7, /* enable low energy neutron transport */ + kUSER = 8 /* allows to give own string combination */ +}; + +// enumerating possible geometry navigation modes +// (understanding that geometry description is always done with TGeo) +enum class EG4Nav { + kTGeo = 0, /* navigate with TGeo */ + kG4 = 1 /* navigate with G4 native geometry */ }; // parameters to influence the G4 engine struct G4Params : public o2::conf::ConfigurableParamHelper { - EG4Physics physicsmode = EG4Physics::kFTFP_BERT_optical; // physics mode with which to configure G4 + EG4Physics physicsmode = EG4Physics::kFTFP_BERT_EMV_optical; // default physics mode with which to configure G4 + + std::string configMacroFile = ""; // a user provided g4Config.in file (otherwise standard one fill be taken) + std::string userPhysicsList = ""; // possibility to directly give physics list as string + + EG4Nav navmode = EG4Nav::kTGeo; // geometry navigation mode (default TGeo) + std::string const& getPhysicsConfigString() const; O2ParamDef(G4Params, "G4"); diff --git a/Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h b/Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h similarity index 97% rename from Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h rename to Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h index c8bacfa7c4e7c..c9811c727cb32 100644 --- a/Detectors/gconfig/include/SimSetup/GlobalProcessCutSimParam.h +++ b/Common/SimConfig/include/SimConfig/GlobalProcessCutSimParam.h @@ -43,7 +43,7 @@ struct GlobalProcessCutSimParam : public o2::conf::ConfigurableParamHelper 1 MeV double DCUTM = 1.0E-3; // GeV --> 1 MeV double PPCUTM = 1.0E-3; // GeV --> 1 MeV - double TOFMAX = 1.E10; // seconds + double TOFMAX = 0.1; // seconds // boilerplate stuff + make principal key "GlobalSimProcs" O2ParamDef(GlobalProcessCutSimParam, "GlobalSimProcs"); diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index 1cfed6eacdae9..be88d9fbd8c33 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -13,7 +13,15 @@ #define O2_SIM_CONFIGURATION #include +#ifndef __CLING__ #include +#else +namespace boost::program_options +{ +class variables_map; +class options_description; +} // namespace boost::program_options +#endif namespace o2 { @@ -29,7 +37,8 @@ enum class SimFieldMode { enum class VertexMode { kNoVertex = 0, // no vertexing should be applied in the generator kDiamondParam = 1, // Diamond param will influence vertexing - kCCDB = 2 // vertex should be taken from CCDB (Calib/MeanVertex object) + kCCDB = 2, // vertex should be taken from CCDB (Calib/MeanVertex object) + kCollCxt = 3 // vertex should be taken from collision context }; enum class TimeStampMode { @@ -40,43 +49,44 @@ enum class TimeStampMode { // configuration struct (which can be passed around) struct SimConfigData { - std::vector mActiveModules; // list of active modules - std::vector mReadoutDetectors; // list of readout detectors - std::string mMCEngine; // chosen VMC engine - std::string mGenerator; // chosen VMC generator - std::string mTrigger; // chosen VMC generator trigger - unsigned int mNEvents; // number of events to be simulated - std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) - std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding - unsigned int mStartEvent; // index of first event to be taken - float mBMax; // maximum for impact parameter sampling - bool mIsMT; // chosen MT mode (Geant4 only) - std::string mOutputPrefix; // prefix to be used for output files - std::string mLogSeverity; // severity for FairLogger - std::string mLogVerbosity; // loglevel for FairLogger - std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens - // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello - // (can be used to **loosely** change any configuration parameter from command-line) - std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). - // values within the config file will override values set in code by the param classes - // but will themselves be overridden by any values given in mKeyValueTokens. - int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job - int mInternalChunkSize; // - ULong_t mStartSeed; // base for random number seeds - int mSimWorkers = 1; // number of parallel sim workers (when it applies) - bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response - std::string mCCDBUrl; // the URL where to find CCDB - uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to + std::vector mActiveModules; // list of active modules + std::vector mReadoutDetectors; // list of readout detectors + std::string mMCEngine; // chosen VMC engine + std::string mGenerator; // chosen VMC generator + std::string mTrigger; // chosen VMC generator trigger + unsigned int mNEvents; // number of events to be simulated + std::string mExtKinFileName; // file name of external kinematics file (needed for ext kinematics generator) + std::string mEmbedIntoFileName; // filename containing the reference events to be used for the embedding + unsigned int mStartEvent; // index of first event to be taken + float mBMax; // maximum for impact parameter sampling + bool mIsMT; // chosen MT mode (Geant4 only) + std::string mOutputPrefix; // prefix to be used for output files + std::string mLogSeverity; // severity for FairLogger + std::string mLogVerbosity; // loglevel for FairLogger + std::string mKeyValueTokens; // a string holding arbitrary sequence of key-value tokens + // Foo.parameter1=x,Bar.parameter2=y,Baz.paramter3=hello + // (can be used to **loosely** change any configuration parameter from command-line) + std::string mConfigFile; // path to a JSON or INI config file (file extension is required to determine type). + // values within the config file will override values set in code by the param classes + // but will themselves be overridden by any values given in mKeyValueTokens. + unsigned int mPrimaryChunkSize; // defining max granularity for input primaries of a sim job + int mInternalChunkSize; // + ULong_t mStartSeed; // base for random number seeds + int mSimWorkers = 1; // number of parallel sim workers (when it applies) + bool mFilterNoHitEvents = false; // whether to filter out events not leaving any response + std::string mCCDBUrl; // the URL where to find CCDB + uint64_t mTimestamp; // timestamp in ms to anchor transport simulation to TimeStampMode mTimestampMode = TimeStampMode::kNow; // telling of timestamp was given as option or defaulted to now - int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible - int mField; // L3 field setting in kGauss: +-2,+-5 and 0 - SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field - bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) - bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) - bool mIsRun5 = false; // true if the simulation is for Run 5 - std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events - bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) - bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc + int mRunNumber = -1; // ALICE run number (if set != -1); the timestamp should be compatible + int mField; // L3 field setting in kGauss: +-2,+-5 and 0 + SimFieldMode mFieldMode = SimFieldMode::kDefault; // uniform magnetic field + bool mAsService = false; // if simulation should be run as service/deamon (does not exit after run) + bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) + bool mIsUpgrade = false; // true if the simulation is for Run 5 + std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events + // + bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) + bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc VertexMode mVertexMode = VertexMode::kDiamondParam; // by default we should use die InteractionDiamond parameter ClassDefNV(SimConfigData, 4); @@ -110,7 +120,7 @@ class SimConfig return SimConfig(); } - static void initOptions(boost::program_options::options_description&); + static void initOptions(boost::program_options::options_description&, bool isUpgrade = false); // initializes the configuration from command line arguments // returns true of correctly initialized and not --help called @@ -131,7 +141,8 @@ class SimConfig // static helper functions to determine list of active / readout modules // can also be used from outside - static void determineActiveModules(std::vector const& input, std::vector const& skipped, std::vector& active, bool isRun5 = false); + static void determineActiveModules(std::vector const& input, std::vector const& skipped, std::vector& active, bool isUpgrade = false); + static bool determineActiveModulesList(const std::string& version, std::vector const& input, std::vector const& skipped, std::vector& active); static void determineReadoutDetectors(std::vector const& active, std::vector const& enabledRO, std::vector const& skippedRO, std::vector& finalRO); // helper to parse field option @@ -163,16 +174,23 @@ class SimConfig uint64_t getTimestamp() const { return mConfigData.mTimestamp; } int getRunNumber() const { return mConfigData.mRunNumber; } bool isNoGeant() const { return mConfigData.mNoGeant; } - void setRun5(bool value = true) { mConfigData.mIsRun5 = value; } + void setRun5(bool value = true) { mConfigData.mIsUpgrade = value; } bool forwardKine() const { return mConfigData.mForwardKine; } bool writeToDisc() const { return mConfigData.mWriteToDisc; } VertexMode getVertexMode() const { return mConfigData.mVertexMode; } + // returns the pair of collision context filename as well as event prefix encoded + // in the mFromCollisionContext string. Returns empty string if information is not available or set. + std::pair getCollContextFilenameAndEventPrefix() const; + private: SimConfigData mConfigData; //! + // Filter out skipped elements in the list + static bool filterSkippedElements(std::vector& elements, std::vector const& skipped); + // adjust/overwrite some option settings when collision context is used - void adjustFromCollContext(); + void adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix); ClassDefNV(SimConfig, 1); }; @@ -198,9 +216,9 @@ struct SimReconfigData { std::string configFile; // path to a JSON or INI config file (file extension is required to determine type). // values within the config file will override values set in code by the param classes // but will themselves be overridden by any values given in mKeyValueTokens. - unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job - ULong_t startSeed; // base for random number seeds - bool stop; // to shut down the service + unsigned int primaryChunkSize; // defining max granularity for input primaries of a sim job + ULong_t startSeed; // base for random number seeds + bool stop; // to shut down the service std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events ClassDefNV(SimReconfigData, 1); diff --git a/Common/SimConfig/include/SimConfig/SimDLLoader.h b/Common/SimConfig/include/SimConfig/SimDLLoader.h new file mode 100644 index 0000000000000..e778023b28276 --- /dev/null +++ b/Common/SimConfig/include/SimConfig/SimDLLoader.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef SIMDLLOADER_H_ +#define SIMDLLOADER_H_ + +#include "CommonUtils/DLLoaderBase.h" + +namespace o2::conf +{ + +class SimDLLoader : public o2::utils::DLLoaderBase +{ + O2DLLoaderDef(SimDLLoader) +}; + +} // namespace o2::conf + +#endif // SIMDLLOADER_H_ diff --git a/Common/SimConfig/include/SimConfig/SimParams.h b/Common/SimConfig/include/SimConfig/SimParams.h index 2c103f43b2b04..b5f975d1b0c6e 100644 --- a/Common/SimConfig/include/SimConfig/SimParams.h +++ b/Common/SimConfig/include/SimConfig/SimParams.h @@ -36,7 +36,6 @@ struct SimCutParams : public o2::conf::ConfigurableParamHelper { float maxRTrackingZDC = 50; // R-cut applied in the tunnel leading to ZDC when z > beampipeZ (custom stepping function) float tunnelZ = 1900; // Z-value from where we apply maxRTrackingZDC (default value taken from standard "hall" dimensions) - float globalDensityFactor = 1.f; // global factor that scales all material densities for systematic studies bool lowneut = false; O2ParamDef(SimCutParams, "SimCutParams"); }; @@ -44,8 +43,12 @@ struct SimCutParams : public o2::conf::ConfigurableParamHelper { // parameter influencing material manager struct SimMaterialParams : public o2::conf::ConfigurableParamHelper { // Local density value takes precedence over global density value, i.e. local values overwrite the global value. - float globalDensityFactor = 1.f; - std::string localDensityFactor; // Expected format: "SimMaterialParams.localDensityFactor=:,:,..." + float globalDensityFactor = 1.f; // global factor that scales all material densities for systematic studies + // String to set densities on module or material level. Expected format: + // "SimMaterialParams.localDensityFactor=:,:,..." + // Example: "SimMaterialParams.localDensityFactor=TPC/Air:1.2,ITS:5." will scale the density of the Air in TPC + // with 1.2 and to 5.0 for all materials in ITS". + std::string localDensityFactor; O2ParamDef(SimMaterialParams, "SimMaterialParams"); }; diff --git a/Common/SimConfig/src/DetectorLists.cxx b/Common/SimConfig/src/DetectorLists.cxx new file mode 100644 index 0000000000000..c9132a3cb84a5 --- /dev/null +++ b/Common/SimConfig/src/DetectorLists.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/DetectorLists.h" +#include +#include +#include +#include +#include + +namespace o2::conf +{ + +bool parseDetectorMapfromJSON(const std::string& path, DetectorMap_t& map) +{ + // Parse JSON file to build map + std::ifstream fileStream(path, std::ios::in); + if (!fileStream.is_open()) { + LOGP(error, "Cannot open '{}'!", path); + return false; + } + rapidjson::IStreamWrapper isw(fileStream); + rapidjson::Document doc; + doc.ParseStream(isw); + if (doc.HasParseError()) { + LOGP(error, "Error parsing provided json file '{}':", path); + LOGP(error, " - Error -> {}", rapidjson::GetParseError_En(doc.GetParseError())); + LOGP(error, " - Offset -> {}", doc.GetErrorOffset()); + return false; + } + + // Clear and rebuild map + map.clear(); + try { + for (auto verItr = doc.MemberBegin(); verItr != doc.MemberEnd(); ++verItr) { + const auto& version = verItr->name.GetString(); + DetectorList_t list; + const auto& elements = doc[version]; + for (const auto& ele : elements.GetArray()) { + list.emplace_back(ele.GetString()); + } + map.emplace(version, list); + } + } catch (const std::exception& e) { + LOGP(error, "Failed to build detector map from file '{}' with '{}'", path, e.what()); + return false; + } + + return true; +} + +void printDetMap(const DetectorMap_t& map, const std::string& list) +{ + if (list.empty()) { + LOGP(error, "List of all available versions including their detectors:"); + for (int i{0}; const auto& [version, elements] : map) { + LOGP(error, " - {: >2d}. {}:", i++, version); + for (int j{0}; const auto& element : elements) { + LOGP(error, "\t\t* {: >2d}.\t{}", j++, element); + } + } + } else { + LOGP(error, "List of available modules for version {}:", list); + for (int j{0}; const auto& element : map.at(list)) { + LOGP(error, "\t* {: >2d}.\t{}", j++, element); + } + } +} + +} // namespace o2::conf diff --git a/Common/SimConfig/src/G4Params.cxx b/Common/SimConfig/src/G4Params.cxx index 48f7a1c240d7b..37625d914a85b 100644 --- a/Common/SimConfig/src/G4Params.cxx +++ b/Common/SimConfig/src/G4Params.cxx @@ -19,12 +19,21 @@ namespace conf namespace { -static const std::string confstrings[4] = {"FTFP_BERT_EMV+optical", "FTFP_BERT_EMV+optical+biasing", "FTFP_INCLXX_EMV+optical", +static const std::string confstrings[8] = {"FTFP_BERT+optical", + "FTFP_BERT+optical+biasing", + "FTFP_INCLXX+optical", + "FTFP_BERT_HP+optical", + "FTFP_BERT_EMV+optical", + "FTFP_BERT_EMV+optical+biasing", + "FTFP_INCLXX_EMV+optical", "FTFP_BERT_HP_EMV+optical"}; } std::string const& G4Params::getPhysicsConfigString() const { + if (physicsmode == o2::conf::EG4Physics::kUSER) { + return userPhysicsList; + } return confstrings[(int)physicsmode]; } diff --git a/Detectors/gconfig/src/GlobalProcessCutSimParam.cxx b/Common/SimConfig/src/GlobalProcessCutSimParam.cxx similarity index 92% rename from Detectors/gconfig/src/GlobalProcessCutSimParam.cxx rename to Common/SimConfig/src/GlobalProcessCutSimParam.cxx index 4f30020133996..5ae72164bb81d 100644 --- a/Detectors/gconfig/src/GlobalProcessCutSimParam.cxx +++ b/Common/SimConfig/src/GlobalProcessCutSimParam.cxx @@ -9,5 +9,5 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "SimSetup/GlobalProcessCutSimParam.h" +#include "SimConfig/GlobalProcessCutSimParam.h" O2ParamImpl(o2::GlobalProcessCutSimParam); diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index a35019648abb9..15879687872d5 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include +#include #include #include #include @@ -23,8 +24,15 @@ using namespace o2::conf; namespace bpo = boost::program_options; -void SimConfig::initOptions(boost::program_options::options_description& options) +void SimConfig::initOptions(boost::program_options::options_description& options, bool isUpgrade) { + // some default args might depend on whether Run3 or Run5 + // can be updated here: + std::string defaultGeomList{"ALICE2"}; + if (isUpgrade == true) { + defaultGeomList = "ALICE3"; + } + int nsimworkersdefault = std::max(1u, std::thread::hardware_concurrency() / 2); options.add_options()( "mcEngine,e", bpo::value()->default_value("TGeant4"), "VMC backend to be used.")( @@ -34,6 +42,14 @@ void SimConfig::initOptions(boost::program_options::options_description& options "skipModules", bpo::value>()->multitoken()->default_value(std::vector({""}), ""), "list of modules excluded in geometry (precendence over -m")( "readoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors creating hits, all if not given; added to to active modules")( "skipReadoutDetectors", bpo::value>()->multitoken()->default_value(std::vector(), ""), "list of detectors to skip hit creation (precendence over --readoutDetectors")( + "detectorList", bpo::value()->default_value(defaultGeomList), + "Use a specific version of ALICE, e.g., a predefined list." + "There is an 'official' list provided with:" + "\nALICE2 : The default configuration for Run 3" + "\nALICE2.1: The future configuration for Run 4" + "\nALICE3 : The far-future configuration for Run 5-6" + "\nAdditionally one can provide their own custom list of modules which should be included in the geometry." + "\nBy specifiying LIST:JSONFILE where LIST is a list present in JSONFILE.")( "nEvents,n", bpo::value()->default_value(0), "number of events")( "startEvent", bpo::value()->default_value(0), "index of first event to be used (when applicable)")( "extKinFile", bpo::value()->default_value("Kinematics.root"), @@ -60,10 +76,10 @@ void SimConfig::initOptions(boost::program_options::options_description& options "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); - options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\"."); + options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); } -void SimConfig::determineActiveModules(std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules, bool mIsRun5) +void SimConfig::determineActiveModules(std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules, bool isUpgrade) { using o2::detectors::DetID; @@ -72,16 +88,33 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules = inputargs; #ifdef ENABLE_UPGRADES if (activeModules[0] != "all") { - if (mIsRun5) { + if (isUpgrade) { for (int i = 0; i < activeModules.size(); ++i) { - if (activeModules[i] != "IT3" && activeModules[i] != "TRK" && activeModules[i] != "FT3" && activeModules[i] != "FCT" && activeModules[i] != "A3IP") { - LOGP(fatal, "List of active modules contains {}, which is not a run 5 module", activeModules[i]); + if (activeModules[i] != "A3IP" && + activeModules[i] != "IT3" && + activeModules[i] != "TRK" && + activeModules[i] != "FT3" && + activeModules[i] != "FCT" && + activeModules[i] != "TF3" && + activeModules[i] != "RCH" && + activeModules[i] != "MI3" && + activeModules[i] != "ECL" && + activeModules[i] != "FD3") { + LOGP(fatal, "List of active modules contains {}, which is not a module from the upgrades.", activeModules[i]); } } } - if (!mIsRun5) { + if (!isUpgrade) { for (int i = 0; i < activeModules.size(); ++i) { - if (activeModules[i] == "TRK" || activeModules[i] == "FT3" || activeModules[i] == "FCT" || activeModules[i] == "A3IP") { + if (activeModules[i] == "A3IP" || + activeModules[i] == "TRK" || + activeModules[i] == "FT3" || + activeModules[i] == "FCT" || + activeModules[i] == "TF3" || + activeModules[i] == "RCH" || + activeModules[i] == "MI3" || + activeModules[i] == "ECL" || + activeModules[i] == "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a run 3 module", activeModules[i]); } } @@ -91,13 +124,22 @@ void SimConfig::determineActiveModules(std::vector const& inputargs if (activeModules.size() == 1 && activeModules[0] == "all") { activeModules.clear(); #ifdef ENABLE_UPGRADES - if (mIsRun5) { + if (isUpgrade) { for (int d = DetID::First; d <= DetID::Last; ++d) { - if (d == DetID::IT3 || d == DetID::TRK || d == DetID::FT3 || d == DetID::FCT) { + if (d == DetID::TRK || + d == DetID::FT3 || + d == DetID::FCT || + d == DetID::TF3 || + d == DetID::RCH || + d == DetID::ECL || + d == DetID::FD3 || + d == DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } } activeModules.emplace_back("A3IP"); + activeModules.emplace_back("A3ABSO"); + activeModules.emplace_back("A3MAG"); } else { #endif // add passive components manually (make a PassiveDetID for them!) @@ -110,7 +152,7 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules.emplace_back("SHIL"); for (int d = DetID::First; d <= DetID::Last; ++d) { #ifdef ENABLE_UPGRADES - if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT) { + if (d != DetID::IT3 && d != DetID::TRK && d != DetID::FT3 && d != DetID::FCT && d != DetID::TF3 && d != DetID::RCH && d != DetID::ECL && d != DetID::FD3 && d != DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } } @@ -119,14 +161,72 @@ void SimConfig::determineActiveModules(std::vector const& inputargs #endif } } - // now we take out detectors listed as skipped - for (auto& s : skippedModules) { - auto iter = std::find(activeModules.begin(), activeModules.end(), s); - if (iter != activeModules.end()) { - // take it out - activeModules.erase(iter); + filterSkippedElements(activeModules, skippedModules); +} + +bool SimConfig::determineActiveModulesList(const std::string& version, std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules) +{ + DetectorList_t modules; + DetectorMap_t map; + if (auto pos = version.find(':'); pos != std::string::npos) { + auto pversion = version.substr(0, pos); + auto ppath = version.substr(pos + 1); + if (!parseDetectorMapfromJSON(ppath, map)) { + LOGP(error, "Could not parse {}; check errors above!", ppath); + return false; + } + if (map.find(pversion) == map.end()) { + LOGP(error, "List {} is not defined in custom JSON file!", pversion); + printDetMap(map); + return false; + } + modules = map[pversion]; + LOGP(info, "Running with version {} from custom detector list '{}'", pversion, ppath); + } else { + // Otherwise check 'official' versions which provided in config + auto o2env = std::getenv("O2_ROOT"); + if (!o2env) { + LOGP(error, "O2_ROOT environment not defined"); + return false; + } + const std::string rootpath(fmt::format("{}/share/config/o2simdefaultdetectorlist.json", o2env)); + if (!parseDetectorMapfromJSON(rootpath, map)) { + LOGP(error, "Could not parse {} -> check errors above!", rootpath); + return false; + } + if (map.find(version) == map.end()) { + LOGP(error, "List {} is not defined in 'official' JSON file!", version); + printDetMap(map); + return false; + } + modules = map[version]; + static std::string last_version{}; // prevent multiple printouts of same message + if (last_version != version) { + LOGP(info, "Running with official detector version '{}'", version); + last_version = version; + } + } + // check if specified modules are in list + if (inputargs.size() != 1 || inputargs[0] != "all") { + std::vector diff; + for (const auto& in : inputargs) { + if (std::find(modules.begin(), modules.end(), in) == std::end(modules)) { + diff.emplace_back(in); + } + } + if (!diff.empty()) { + LOGP(error, "Modules specified that are not present in detector list {}", version); + for (int j{0}; const auto& m : diff) { + LOGP(info, " - {: <2}. {}", j++, m); + } + printDetMap(map, version); + return false; } } + // Insert into active modules if module is built buy -m or insert all if default for -m is used ("all") + std::copy_if(modules.begin(), modules.end(), std::back_inserter(activeModules), + [&inputargs](const auto& e) { return (inputargs.size() == 1 && inputargs[0] == "all") || (std::find(inputargs.begin(), inputargs.end(), e) != inputargs.end()); }); + return filterSkippedElements(activeModules, skippedModules); } void SimConfig::determineReadoutDetectors(std::vector const& activeModules, std::vector const& enableReadout, std::vector const& disableReadout, std::vector& readoutDetectors) @@ -177,14 +277,36 @@ void SimConfig::determineReadoutDetectors(std::vector const& active } } +std::pair SimConfig::getCollContextFilenameAndEventPrefix() const +{ + // we decompose the argument to fetch + // (a) collision contextfilename + // (b) sim prefix to use from the context + auto pos = mConfigData.mFromCollisionContext.find(':'); + std::string collcontextfile{mConfigData.mFromCollisionContext}; + std::string simprefix{mConfigData.mOutputPrefix}; + if (pos != std::string::npos) { + collcontextfile = mConfigData.mFromCollisionContext.substr(0, pos); + simprefix = mConfigData.mFromCollisionContext.substr(pos + 1); + } + return std::make_pair(collcontextfile, simprefix); +} + bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& vm) { using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); - // get final set of active Modules - determineActiveModules(vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules, mConfigData.mIsRun5); + // Reset modules and detectors as they are anyway re-parsed + mConfigData.mReadoutDetectors.clear(); + mConfigData.mActiveModules.clear(); + + // Get final set of active Modules + if (!determineActiveModulesList(vm["detectorList"].as(), vm["modules"].as>(), vm["skipModules"].as>(), mConfigData.mActiveModules)) { + return false; + } + if (mConfigData.mNoGeant) { // CAVE is all that's needed (and that will be built either way), so clear all modules and set the O2TrivialMCEngine mConfigData.mActiveModules.clear(); @@ -233,7 +355,8 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& mConfigData.mFilterNoHitEvents = true; } mConfigData.mFromCollisionContext = vm["fromCollContext"].as(); - adjustFromCollContext(); + auto collcontext_simprefix = getCollContextFilenameAndEventPrefix(); + adjustFromCollContext(collcontext_simprefix.first, collcontext_simprefix.second); // analyse vertex options if (!parseVertexModeString(vm["vertexMode"].as(), mConfigData.mVertexMode)) { @@ -243,9 +366,9 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& // analyse field options // either: "ccdb" or +-2[U],+-5[U] and 0[U]; +-U auto& fieldstring = vm["field"].as(); - std::regex re("(ccdb)|([+-]?[250]U?)"); + std::regex re("(ccdb)|([+-]?(0|[2-9]|[12][0-9]|20)U?)"); if (!std::regex_match(fieldstring, re)) { - LOG(error) << "Invalid field option"; + LOG(error) << "Invalid field option " << fieldstring; return false; } if (fieldstring == "ccdb") { @@ -275,8 +398,11 @@ bool SimConfig::parseVertexModeString(std::string const& vertexstring, VertexMod } else if (vertexstring == "kCCDB") { mode = VertexMode::kCCDB; return true; + } else if (vertexstring == "kCollContext") { + mode = VertexMode::kCollCxt; + return true; } - LOG(error) << "Vertex mode must be one of kNoVertex, kDiamondParam, kCCDB"; + LOG(error) << "Vertex mode must be one of kNoVertex, kDiamondParam, kCCDB, kCollContext"; return false; } @@ -284,9 +410,9 @@ bool SimConfig::parseFieldString(std::string const& fieldstring, int& fieldvalue { // analyse field options // either: "ccdb" or +-2[U],+-5[U] and 0[U]; +-U - std::regex re("(ccdb)|([+-]?[250]U?)"); + std::regex re("(ccdb)|([+-]?(0|[2-9]|[12][0-9]|20)U?)"); if (!std::regex_match(fieldstring, re)) { - LOG(error) << "Invalid field option"; + LOG(error) << "Invalid field option " << fieldstring; return false; } if (fieldstring == "ccdb") { @@ -300,28 +426,60 @@ bool SimConfig::parseFieldString(std::string const& fieldstring, int& fieldvalue return true; } -void SimConfig::adjustFromCollContext() +bool SimConfig::filterSkippedElements(std::vector& elements, std::vector const& skipped) +{ + for (auto& s : skipped) { + if (s.empty()) { // nothing to skip here + continue; + } + auto iter = std::find(elements.begin(), elements.end(), s); + if (iter != elements.end()) { + // take it out + elements.erase(iter); + } else { + LOGP(error, "Skipped modules specified that are not present in built modules!"); + LOGP(error, "Built modules:"); + for (int j{0}; const auto& m : elements) { + LOGP(error, " + {: <2}. {}", j++, m); + } + std::vector diff; + for (const auto& skip : skipped) { + if (std::find(elements.begin(), elements.end(), skip) == std::end(elements)) { + diff.emplace_back(skip); + } + } + LOGP(error, "Specified skipped modules not present in built modules:"); + for (int j{0}; const auto& m : diff) { + LOGP(error, " - {: <2}. {}", j++, m); + } + return false; + } + } + return true; +} + +void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::string const& prefix) { // When we use pregenerated collision contexts, some options // need to be auto-adjusted. Do so and inform about this in the logs. - if (mConfigData.mFromCollisionContext == "") { + if (collcontextfile == "") { return; } - auto context = o2::steer::DigitizationContext::loadFromFile(mConfigData.mFromCollisionContext); + auto context = o2::steer::DigitizationContext::loadFromFile(collcontextfile); if (context) { // find the events belonging to a source that corresponds to a sim prefix - LOG(info) << "Looking up simprefixes " << mConfigData.mOutputPrefix; - int sourceid = context->findSimPrefix(mConfigData.mOutputPrefix); + LOG(info) << "Looking up simprefixes " << prefix; + int sourceid = context->findSimPrefix(prefix); if (sourceid == -1) { - LOG(error) << "Could not find collisions with sim prefix " << mConfigData.mOutputPrefix << " in the collision context. The collision contet specifies the following prefixes:"; - for (auto& prefix : context->getSimPrefixes()) { - LOG(info) << prefix; + LOG(error) << "Could not find collisions with sim prefix " << prefix << " in the collision context. The collision context specifies the following prefixes:"; + for (auto& sp : context->getSimPrefixes()) { + LOG(info) << sp; } LOG(fatal) << "Aborting due to prefix error"; } else { auto collisionmap = context->getCollisionIndicesForSource(sourceid); - LOG(info) << "Found " << collisionmap.size() << " events in the collisioncontext for prefix " << mConfigData.mOutputPrefix; + LOG(info) << "Found " << collisionmap.size() << " events in the collisioncontext for prefix " << prefix; // check if collisionmap is dense (otherwise it will get screwed up with order/indexing in ROOT output) bool good = true; @@ -339,25 +497,23 @@ void SimConfig::adjustFromCollContext() // we take what is specified in the context mConfigData.mNEvents = collisionmap.size(); } else { - LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. Taking the min of the 2"; - mConfigData.mNEvents = std::min((size_t)mConfigData.mNEvents, collisionmap.size()); + LOG(warning) << "The number of events on the command line " << mConfigData.mNEvents << " and in the collision context differ. We take the one from collision context " << collisionmap.size(); + mConfigData.mNEvents = collisionmap.size(); } LOG(info) << "Setting number of events to simulate to " << mConfigData.mNEvents; } } else { - LOG(fatal) << "Could not open collision context file " << mConfigData.mFromCollisionContext; + LOG(fatal) << "Could not open collision context file " << collcontextfile; } } bool SimConfig::resetFromArguments(int argc, char* argv[]) { - namespace bpo = boost::program_options; - // Arguments parsing bpo::variables_map vm; bpo::options_description desc("Allowed options"); desc.add_options()("help,h", "Produce help message."); - initOptions(desc); + initOptions(desc, mConfigData.mIsUpgrade); try { bpo::store(parse_command_line(argc, argv, desc), vm); diff --git a/Common/SimConfig/src/SimConfigLinkDef.h b/Common/SimConfig/src/SimConfigLinkDef.h index 844ea8db3ed8c..a1315e24ffedd 100644 --- a/Common/SimConfig/src/SimConfigLinkDef.h +++ b/Common/SimConfig/src/SimConfigLinkDef.h @@ -29,6 +29,7 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::DigiParams> + ; #pragma link C++ enum o2::conf::EG4Physics; +#pragma link C++ enum o2::conf::EG4Nav; #pragma link C++ enum o2::conf::SimFieldMode; #pragma link C++ struct o2::conf::G4Params + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::G4Params> + ; @@ -39,4 +40,7 @@ #pragma link C++ class o2::eventgen::InteractionDiamondParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::InteractionDiamondParam> + ; +#pragma link C++ class o2::GlobalProcessCutSimParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::GlobalProcessCutSimParam> + ; + #endif diff --git a/Common/SimConfig/src/SimDLLoader.cxx b/Common/SimConfig/src/SimDLLoader.cxx new file mode 100644 index 0000000000000..ed15a8b0cef6a --- /dev/null +++ b/Common/SimConfig/src/SimDLLoader.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "SimConfig/SimDLLoader.h" +O2DLLoaderImpl(o2::conf::SimDLLoader) diff --git a/Common/SimConfig/test/testSimCutParam.cxx b/Common/SimConfig/test/testSimCutParam.cxx index 7a17ae9d1f2cf..468d76429cbba 100644 --- a/Common/SimConfig/test/testSimCutParam.cxx +++ b/Common/SimConfig/test/testSimCutParam.cxx @@ -15,6 +15,7 @@ #include #include "SimConfig/SimParams.h" #include "CommonUtils/ConfigurableParam.h" +#include using namespace o2::conf; diff --git a/Common/Topologies/o2prototype_topology.xml b/Common/Topologies/o2prototype_topology.xml index 240b8d87d469a..8d53c9eb0127a 100644 --- a/Common/Topologies/o2prototype_topology.xml +++ b/Common/Topologies/o2prototype_topology.xml @@ -74,7 +74,7 @@ The following parameters need adjustment when extending the FLP-EPN configuratio - $ALICEO2_INSTALL_DIR/bin/aliceHLTWrapper Tracker_%collectionIndex%_%taskIndex% 1 --dds --poll-period 100 --input type=pull,size=5000,method=connect,property=EPNReceiverOutputAddress,count=1 --output type=push,size=500,method=bind,property=TrackingOutputAddress,min-port=48000 --library libAliHLTTPC.so --component TPCCATracker --run 167808 --parameter '-GlobalTracking -allowGPU -GPUHelperThreads 4 -loglevel=0x7c' + $ALICEO2_INSTALL_DIR/bin/aliceHLTWrapper Tracker_%collectionIndex%_%taskIndex% 1 --dds --poll-period 100 --input type=pull,size=5000,method=connect,property=EPNReceiverOutputAddress,count=1 --output type=push,size=500,method=bind,property=TrackingOutputAddress,min-port=48000 --library libAliHLTTPC.so --component TPCCATracker --run 167808 --parameter '-GlobalTracking -allowGPU -loglevel=0x7c' EPNReceiverOutputAddress diff --git a/Common/Utils/CMakeLists.txt b/Common/Utils/CMakeLists.txt index 4e43116b279a1..849a3d70f62e1 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -14,7 +14,7 @@ o2_add_library(CommonUtils src/RootChain.cxx src/CompStream.cxx src/ShmManager.cxx src/ValueMonitor.cxx src/StringUtils.cxx - src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx src/RootSerializableKeyValueStore.cxx + src/ConfigurableParamReaders.cxx src/ConfigurableParamHelper.cxx src/ConfigurableParam.cxx src/RootSerializableKeyValueStore.cxx src/KeyValParam.cxx src/FileSystemUtils.cxx src/FIFO.cxx @@ -24,15 +24,15 @@ o2_add_library(CommonUtils src/NameConf.cxx src/IRFrameSelector.cxx src/DebugStreamer.cxx + src/DLLoaderBase.cxx PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::Tree Boost::iostreams O2::CommonDataFormat O2::Headers - FairLogger::FairLogger O2::MathUtils TBB::tbb) + FairLogger::FairLogger O2::MathUtils TBB::tbb O2::GPUCommon) o2_target_root_dictionary(CommonUtils HEADERS include/CommonUtils/TreeStream.h include/CommonUtils/TreeStreamRedirector.h include/CommonUtils/RootChain.h include/CommonUtils/BoostHistogramUtils.h - include/CommonUtils/BoostSerializer.h include/CommonUtils/ShmManager.h include/CommonUtils/RngHelper.h include/CommonUtils/StringUtils.h @@ -40,27 +40,32 @@ o2_target_root_dictionary(CommonUtils include/CommonUtils/MemFileHelper.h include/CommonUtils/ConfigurableParam.h include/CommonUtils/ConfigurableParamHelper.h + include/CommonUtils/ConfigurableParamReaders.h include/CommonUtils/ConfigurationMacroHelper.h include/CommonUtils/RootSerializableKeyValueStore.h include/CommonUtils/KeyValParam.h include/CommonUtils/VerbosityConfig.h include/CommonUtils/FileFetcher.h + include/CommonUtils/DLLoaderBase.h include/CommonUtils/NameConf.h include/CommonUtils/IRFrameSelector.h include/CommonUtils/DebugStreamer.h) +# Extra dictionaries only needed if tests are built +if(BUILD_TESTING) + o2_add_library(CommonUtilsTest + SOURCES src/ConfigurableParamTest.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + o2_target_root_dictionary(CommonUtilsTest + HEADERS include/CommonUtils/ConfigurableParamTest.h) +endif() + o2_add_test(TreeStream COMPONENT_NAME CommonUtils LABELS utils SOURCES test/testTreeStream.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils O2::ReconstructionDataFormats) -o2_add_test(BoostSerializer - COMPONENT_NAME CommonUtils - LABELS utils - SOURCES test/testBoostSerializer.cxx - PUBLIC_LINK_LIBRARIES O2::CommonUtils Boost::serialization) - o2_add_test(CompStream COMPONENT_NAME CommonUtils LABELS utils @@ -85,7 +90,22 @@ o2_add_test(MemFileHelper SOURCES test/testMemFileHelper.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils) +o2_add_test(EnumFlags + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testEnumFlags.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils) + +o2_add_test(ConfigurableParam + COMPONENT_NAME CommonUtils + LABELS utils + SOURCES test/testConfigurableParam.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtilsTest) + o2_add_executable(treemergertool COMPONENT_NAME CommonUtils SOURCES src/TreeMergerTool.cxx PUBLIC_LINK_LIBRARIES O2::CommonUtils Boost::program_options ROOT::Core) + +add_library(fpu_support OBJECT src/fpu.cxx) +add_library(O2::fpu_support ALIAS fpu_support) diff --git a/Common/Utils/include/CommonUtils/BoostHistogramUtils.h b/Common/Utils/include/CommonUtils/BoostHistogramUtils.h index 5986ba01da7e0..0d20e97adf7a7 100644 --- a/Common/Utils/include/CommonUtils/BoostHistogramUtils.h +++ b/Common/Utils/include/CommonUtils/BoostHistogramUtils.h @@ -45,10 +45,10 @@ #include using boostHisto2d = boost::histogram::histogram, boost::histogram::axis::regular>, boost::histogram::unlimited_storage>>; -using boostHisto1d = boost::histogram::histogram>, boost::histogram::unlimited_storage>>; +using boostHisto1d = boost::histogram::histogram>>; using boostHisto2d_VarAxis = boost::histogram::histogram>, boost::histogram::axis::variable>>>; -using boostHisto1d_VarAxis = boost::histogram::histogram>>>; +using boostHisto1d_VarAxis = boost::histogram::histogram>>; namespace o2 { @@ -313,7 +313,7 @@ std::vector fitGaus(Iterator first, Iterator last, BinCenterView axisfir if (nbins > 1) { // dont take bins with 0 entries or bins with nan into account // if y-value (*iter) is 1, log(*iter) will be 0. Exclude these cases - if (isnan(*axisiter) || isinf(*axisiter) || *iter <= 0 || *iter == 1) { + if (std::isnan(*axisiter) || std::isinf(*axisiter) || *iter <= 0 || *iter == 1) { continue; } double x = *axisiter; @@ -401,10 +401,63 @@ std::vector fitBoostHistoWithGaus(boost::histogram::histogram& } /// \brief Convert a 1D root histogram to a Boost histogram -boostHisto1d_VarAxis boosthistoFromRoot_1D(TH1D* inHist1D); +template +Hist boosthistoFromRoot_1D(TH1D* inHist1D) +{ + // first setup the proper boost histogram + int nBins = inHist1D->GetNbinsX(); + std::vector binEdges; + for (int i = 0; i < nBins + 1; i++) { + binEdges.push_back(inHist1D->GetBinLowEdge(i + 1)); + } + Hist mHisto; + + if constexpr (std::is_same::value) { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdges)); + } else { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::regular<>(nBins, binEdges[0], binEdges.back())); + } + + // trasfer the acutal values + for (Int_t x = 1; x < nBins + 1; x++) { + mHisto.at(x - 1) = inHist1D->GetBinContent(x); + } + return mHisto; +} /// \brief Convert a 2D root histogram to a Boost histogram -boostHisto2d_VarAxis boostHistoFromRoot_2D(TH2D* inHist2D); +template +Hist boostHistoFromRoot_2D(TH2D* inHist2D) +{ + // Get Xaxis binning + const int nBinsX = inHist2D->GetNbinsX(); + std::vector binEdgesX; + for (int i = 0; i < nBinsX + 1; i++) { + binEdgesX.push_back(inHist2D->GetXaxis()->GetBinLowEdge(i + 1)); + } + // Get Yaxis binning + const int nBinsY = inHist2D->GetNbinsY(); + std::vector binEdgesY; + for (int i = 0; i < nBinsY + 1; i++) { + binEdgesY.push_back(inHist2D->GetYaxis()->GetBinLowEdge(i + 1)); + } + + Hist mHisto; + + if constexpr (std::is_same::value) { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesX), boost::histogram::axis::variable<>(binEdgesY)); + } else { + mHisto = boost::histogram::make_histogram(boost::histogram::axis::regular<>(nBinsX, binEdgesX[0], binEdgesX.back()), boost::histogram::axis::regular<>(nBinsY, binEdgesY[0], binEdgesY.back())); + } + + // trasfer the acutal values + for (Int_t x = 1; x < nBinsX + 1; x++) { + for (Int_t y = 1; y < nBinsY + 1; y++) { + mHisto.at(x - 1, y - 1) = inHist2D->GetBinContent(x, y); + } + } + return mHisto; +} /// \brief Get the mean of a 1D boost histogram /// \param inHist1D input boost histogram @@ -565,7 +618,7 @@ auto ProjectBoostHistoXFast(const boost::histogram::histogram& hist2d, /// \return result /// 1d boost histogram from projection of the input 2d boost histogram template -auto ReduceBoostHistoFastSlice(boost::histogram::histogram& hist2d, int binXLow, int binXHigh, int binYLow, int binYHigh, bool includeOverflowUnderflow) +auto ReduceBoostHistoFastSlice(const boost::histogram::histogram& hist2d, int binXLow, int binXHigh, int binYLow, int binYHigh, bool includeOverflowUnderflow) { int nXbins = binXHigh - binXLow + 1; int nYbins = binYHigh - binYLow + 1; @@ -661,6 +714,29 @@ auto ReduceBoostHistoFastSliceByValue(boost::histogram::histogram& hist return ReduceBoostHistoFastSlice(hist2d, binXLow, binXHigh, binYLow, binYHigh, includeOverflowUnderflow); } +/// \brief Function to integrate 1d boost histogram in specified range +/// \param hist 1d boost histogram +/// \param min lower integration range +/// \param max upper integration range +/// \return sum of bin contents in specified range +template +double getIntegralBoostHist(boost::histogram::histogram& hist, double min, double max) +{ + // find bins for min and max values + std::array axisLimitsIndex = {hist.axis(0).index(min), hist.axis(0).index(max)}; + // over/underflow bin have to be protected + for (auto& bin : axisLimitsIndex) { + if (bin < 0) { + bin = 0; + } else if (bin >= hist.axis(0).size()) { + bin = hist.axis(0).size() - 1; + } + } + // Reduce histogram to desired range + auto slicedHist = ReduceBoostHistoFastSlice1D(hist, axisLimitsIndex[0], axisLimitsIndex[1], false); + return boost::histogram::algorithm::sum(slicedHist); +} + } // namespace utils } // end namespace o2 diff --git a/Common/Utils/include/CommonUtils/BoostSerializer.h b/Common/Utils/include/CommonUtils/BoostSerializer.h deleted file mode 100644 index 4c388a3daa8cc..0000000000000 --- a/Common/Utils/include/CommonUtils/BoostSerializer.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file BoostSerializer.h -/// \brief Templatised boost serializer/deserializer for containers and base types -/// \author Gabriele G. Fronzé -/// \date 17 July 2018 - -#ifndef ALICEO2_BOOSTSERIALIZER_H -#define ALICEO2_BOOSTSERIALIZER_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace utils -{ -template -std::ostringstream BoostSerialize(const ContT& dataSet) -{ - /// Serialises a container (vector, array or list) using boost serialisation routines. - /// Requires the contained type to be either trivial or provided with an overried of boost::serialise method. - std::ostringstream buffer; - boost::archive::binary_oarchive outputArchive(buffer); - outputArchive << dataSet; - return buffer; -} - -template -ContT BoostDeserialize(std::string& msgStr) -{ - /// Deserialises a msg contained in a string in a container type (vector, array or list) of the provided type. - ContT output; - std::istringstream buffer(msgStr); - boost::archive::binary_iarchive inputArchive(buffer); - inputArchive >> output; - return std::move(output); -} -} // namespace utils -} // namespace o2 - -#endif //ALICEO2_BOOSTSERIALIZER_H diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 8b075aebd9a9e..39b24bbbbd57c 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -15,11 +15,13 @@ #define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ #include +#include #include #include -#include +#include #include #include +#include class TFile; class TRootIOCtor; @@ -160,7 +162,7 @@ class ConfigurableParam virtual std::string getName() const = 0; // print the current keys and values to screen (optionally with provenance information) - virtual void printKeyValues(bool showprov = true, bool useLogger = false) const = 0; + virtual void printKeyValues(bool showprov = true, bool useLogger = false, bool withPadding = false, bool showHash = false) const = 0; // get a single size_t hash_value of this parameter (can be used as a checksum to see // if object changed or different) @@ -174,15 +176,10 @@ class ConfigurableParam static void printAllRegisteredParamNames(); static void printAllKeyValuePairs(bool useLogger = false); - static const std::string& getInputDir() { return sInputDir; } static const std::string& getOutputDir() { return sOutputDir; } - static void setInputDir(const std::string& d) { sInputDir = d; } static void setOutputDir(const std::string& d) { sOutputDir = d; } - static boost::property_tree::ptree readINI(std::string const& filepath); - static boost::property_tree::ptree readJSON(std::string const& filepath); - static boost::property_tree::ptree readConfigFile(std::string const& filepath); static bool configFileExists(std::string const& filepath); // writes a human readable JSON file of all parameters @@ -194,10 +191,12 @@ class ConfigurableParam template static T getValueAs(std::string key) { - if (!sIsFullyInitialized) { - initialize(); - } - return sPtree->get(key); + return [](auto* tree, const std::string& key) -> T { + if (!sIsFullyInitialized) { + initialize(); + } + return tree->template get(key); + }(sPtree, key); } template @@ -206,42 +205,43 @@ class ConfigurableParam if (!sIsFullyInitialized) { initialize(); } - assert(sPtree); - try { - auto key = mainkey + "." + subkey; - if (sPtree->get_optional(key).is_initialized()) { - sPtree->put(key, x); - auto changed = updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x); - if (changed != EParamUpdateStatus::Failed) { - sValueProvenanceMap->find(key)->second = kRT; // set to runtime + return [&subkey, &x, &mainkey](auto* tree) -> void { + assert(tree); + try { + auto key = mainkey + "." + subkey; + if (tree->template get_optional(key).is_initialized()) { + tree->put(key, x); + auto changed = updateThroughStorageMap(mainkey, subkey, typeid(T), (void*)&x); + if (changed != EParamUpdateStatus::Failed) { + sValueProvenanceMap->find(key)->second = kRT; // set to runtime + } } + } catch (std::exception const& e) { + std::cerr << "Error in setValue (T) " << e.what() << "\n"; } - } catch (std::exception const& e) { - std::cerr << "Error in setValue (T) " << e.what() << "\n"; - } + }(sPtree); } - // specialized for std::string - // which means that the type will be converted internally - static void setValue(std::string const& key, std::string const& valuestring) + static void setProvenance(std::string const& mainkey, std::string const& subkey, EParamProvenance p) { if (!sIsFullyInitialized) { - initialize(); + std::cerr << "setProvenance was called on non-initialized ConfigurableParam\n"; + return; } - assert(sPtree); try { - if (sPtree->get_optional(key).is_initialized()) { - sPtree->put(key, valuestring); - auto changed = updateThroughStorageMapWithConversion(key, valuestring); - if (changed != EParamUpdateStatus::Failed) { - sValueProvenanceMap->find(key)->second = kRT; // set to runtime - } + auto key = mainkey + "." + subkey; + auto keyProv = sValueProvenanceMap->find(key); + if (keyProv != sValueProvenanceMap->end()) { + keyProv->second = p; } } catch (std::exception const& e) { - std::cerr << "Error in setValue (string) " << e.what() << "\n"; + std::cerr << "Error in setProvenance (T) " << e.what() << "\n"; } } + // specialized for std::string + // which means that the type will be converted internally + static void setValue(std::string const& key, std::string const& valuestring); static void setEnumValue(const std::string&, const std::string&); static void setArrayValue(const std::string&, const std::string&); @@ -302,7 +302,6 @@ class ConfigurableParam // (stored as a vector of pairs ) static EnumRegistry* sEnumRegistry; - static std::string sInputDir; static std::string sOutputDir; void setRegisterMode(bool b) { sRegisterMode = b; } @@ -322,17 +321,19 @@ class ConfigurableParam } // end namespace o2 // a helper macro for boilerplate code in parameter classes -#define O2ParamDef(classname, key) \ - public: \ - classname(TRootIOCtor*) {} \ - classname(classname const&) = delete; \ - \ - private: \ - static constexpr char const* const sKey = key; \ - static classname sInstance; \ - classname() = default; \ - template \ - friend class o2::conf::ConfigurableParamHelper; +#define O2ParamDef(classname, key) \ + public: \ + classname(TRootIOCtor*) {} \ + classname(classname const&) = delete; \ + \ + private: \ + static constexpr char const* const sKey = key; \ + static classname sInstance; \ + classname() = default; \ + template \ + friend class o2::conf::ConfigurableParamHelper; \ + template \ + friend class o2::conf::ConfigurableParamPromoter; // a helper macro to implement necessary symbols in source #define O2ParamImpl(classname) classname classname::sInstance; diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h index 1dc5d5c4c38f8..6e69fae03e6c3 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h @@ -34,7 +34,7 @@ struct ParamDataMember { std::string value; std::string provenance; - std::string toString(std::string const& prefix, bool showProv) const; + std::string toString(std::string const& prefix, bool showProv, size_t padding = 0) const; }; // ---------------------------------------------------------------- @@ -45,26 +45,29 @@ class _ParamHelper { private: static std::vector* getDataMembersImpl(std::string const& mainkey, TClass* cl, void*, - std::map const* provmap); + std::map const* provmap, size_t virtualoffset); static void fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void*, boost::property_tree::ptree*, std::map>*, - EnumRegistry*); + EnumRegistry*, size_t offset); static void printWarning(std::type_info const&); static void assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void syncCCDBandRegistry(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); - static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); - static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); + static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding = false, bool showHash = false); + static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash); static size_t getHashImpl(std::string const& mainkey, std::vector const* members); template friend class ConfigurableParamHelper; + + template + friend class ConfigurableParamPromoter; }; // ---------------------------------------------------------------- @@ -97,13 +100,13 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // ---------------------------------------------------------------- // one of the key methods, using introspection to print itself - void printKeyValues(bool showProv = true, bool useLogger = false) const final + void printKeyValues(bool showProv = true, bool useLogger = false, bool withPadding = true, bool showHash = true) const final { if (!isInitialized()) { initialize(); } auto members = getDataMembers(); - _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger); + _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger, withPadding, showHash); } // @@ -140,7 +143,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam return nullptr; } - return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap); + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 0); } // ---------------------------------------------------------------- @@ -153,7 +156,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam _ParamHelper::printWarning(typeid(P)); return; } - _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry); + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 0); } // ---------------------------------------------------------------- @@ -167,7 +170,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam file->GetObject(getName().c_str(), readback); if (readback != nullptr) { _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)readback, - sValueProvenanceMap); + sValueProvenanceMap, 0); delete readback; } setRegisterMode(true); @@ -185,7 +188,146 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // setRegisterMode(false); _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)externalobj, - sValueProvenanceMap); + sValueProvenanceMap, 0); + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void serializeTo(TFile* file) const final + { + file->WriteObjectAny((void*)this, TClass::GetClass(typeid(P)), getName().c_str()); + } +}; + +// Promotes a simple struct Base to a configurable parameter class +// Aka implements all interfaces for a ConfigurableParam P, which shares or +// takes the fields from a Base struct +template +class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam +{ + public: + using ConfigurableParam::ConfigurableParam; + + static const P& Instance() + { + return P::sInstance; + } + + // extracts a copy of the underlying data struct + Base detach() const + { + static_assert(std::copyable, "Base type must be copyable."); + return static_cast(*this); + } + + // ---------------------------------------------------------------- + std::string getName() const final + { + return P::sKey; + } + + // ---------------------------------------------------------------- + // get the provenace of the member with given key + EParamProvenance getMemberProvenance(const std::string& key) const final + { + return getProvenance(getName() + '.' + key); + } + + // ---------------------------------------------------------------- + + // one of the key methods, using introspection to print itself + void printKeyValues(bool showProv = true, bool useLogger = false, bool withPadding = true, bool showHash = true) const final + { + if (!isInitialized()) { + initialize(); + } + auto members = getDataMembers(); + _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger, withPadding, showHash); + } + + // + size_t getHash() const final + { + return _ParamHelper::getHashImpl(getName(), getDataMembers()); + } + + // ---------------------------------------------------------------- + + void output(std::ostream& out) const final + { + auto members = getDataMembers(); + _ParamHelper::outputMembersImpl(out, getName(), members, true, false); + } + + // ---------------------------------------------------------------- + + // Grab the list of ConfigurableParam data members + // Returns a nullptr if the TClass of the P template class cannot be created. + std::vector* getDataMembers() const + { + // just a helper line to make sure P::sInstance is looked-up + // and that compiler complains about missing static sInstance of type P + // volatile void* ptr = (void*)&P::sInstance; + // static assert on type of sInstance: + static_assert(std::is_same::value, + "static instance must of same type as class"); + + // obtain the TClass for the Base type and delegate further + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return nullptr; + } + + // we need to put an offset of 8 bytes since internally this is using data members of the Base class + // which doesn't account for the virtual table of P + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 8); + } + + // ---------------------------------------------------------------- + + // fills the data structures with the initial default values + void putKeyValues(boost::property_tree::ptree* tree) final + { + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return; + } + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 8); + } + + // ---------------------------------------------------------------- + + void initFrom(TFile* file) final + { + // switch off auto registering since the readback object is + // only a "temporary" singleton + setRegisterMode(false); + P* readback = nullptr; + file->GetObject(getName().c_str(), readback); + if (readback != nullptr) { + _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)readback, + sValueProvenanceMap, 8); + delete readback; + } + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void syncCCDBandRegistry(void* externalobj) final + { + // We may be getting an external copy from CCDB which is passed as externalobj. + // The task of this function is to + // a) update the internal registry with fields coming from CCDB + // but only if keys have not been modified via RT == command line / ini file + // b) update the external object with with fields having RT provenance + // + setRegisterMode(false); + _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)externalobj, + sValueProvenanceMap, 8); setRegisterMode(true); } diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h b/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h new file mode 100644 index 0000000000000..d5ecd6cb97f7a --- /dev/null +++ b/Common/Utils/include/CommonUtils/ConfigurableParamReaders.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_COMMON_UTILS_CONFIGURABLEPARAMREADERS_H_ +#define O2_COMMON_UTILS_CONFIGURABLEPARAMREADERS_H_ + +#include +#include + +namespace o2::conf +{ + +// Helpers to read ConfigurableParam from different file formats +class ConfigurableParamReaders +{ + public: + static void setInputDir(const std::string& d) { sInputDir = d; } + static const std::string& getInputDir() { return sInputDir; } + + static boost::property_tree::ptree readINI(std::string const& filepath); + static boost::property_tree::ptree readJSON(std::string const& filepath); + static boost::property_tree::ptree readConfigFile(std::string const& filepath); + + private: + static std::string sInputDir; +}; + +} // namespace o2::conf +#endif // O2_COMMON_UTILS_CONF_CONFIGURABLEPARAMREADERS_H_ diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamTest.h b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h new file mode 100644 index 0000000000000..547bbf9ba8c38 --- /dev/null +++ b/Common/Utils/include/CommonUtils/ConfigurableParamTest.h @@ -0,0 +1,45 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef COMMON_CONFIGURABLE_PARAM_TEST_H_ +#define COMMON_CONFIGURABLE_PARAM_TEST_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::conf::test +{ +struct TestParam : public o2::conf::ConfigurableParamHelper { + enum TestEnum : uint8_t { + A, + B, + C + }; + + int iValue{42}; + float fValue{3.14}; + double dValue{3.14}; + bool bValue{true}; + unsigned uValue{1}; + long lValue{1}; + unsigned long ulValue{1}; + long long llValue{1}; + unsigned long long ullValue{1}; + std::string sValue = "default"; + int iValueProvenanceTest{0}; + TestEnum eValue = TestEnum::C; + int caValue[3] = {0, 1, 2}; + + O2ParamDef(TestParam, "TestParam"); +}; +} // namespace o2::conf::test + +#endif diff --git a/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h index f16e501d96d40..be9bbcad25772 100644 --- a/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurationMacroHelper.h @@ -69,6 +69,21 @@ T GetFromMacro(const std::string& file, const std::string& funcname, const std:: return *ptr; } +// just-in-time interpret some C++ function using ROOT and make result available to runtime +// functiondecl: A string coding the function to call (example "bool foo(){ return true; }") +// funcname: The name of the function to call (example "foo()") +// type: Return type of function (example "bool") +// unique: Some unique string identifier under which the result will be stored in gROOT global variable space +template +T JITAndEvalFunction(const std::string& functiondecl, const std::string& funcname, const std::string& type, const std::string& unique) +{ + /** interpret and execute a function and retrieve pointer to the returned type **/ + auto line = Form("%s; %s __%s__ = %s;", functiondecl.c_str(), type.c_str(), unique.c_str(), funcname.c_str()); + gROOT->ProcessLine(line); + auto ptr = (T*)gROOT->GetGlobal(Form("__%s__", unique.c_str()))->GetAddress(); + return *ptr; +} + } // namespace conf } // namespace o2 diff --git a/Common/Utils/include/CommonUtils/DLLoaderBase.h b/Common/Utils/include/CommonUtils/DLLoaderBase.h new file mode 100644 index 0000000000000..61d349876c92e --- /dev/null +++ b/Common/Utils/include/CommonUtils/DLLoaderBase.h @@ -0,0 +1,266 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief A thin wrapper to manage dynamic library loading, based around boost::dll + +#ifndef DLLOADER_H_ +#define DLLOADER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "dlfcn.h" + +#if defined(__APPLE__) +#define DLLOADER_MAC_LINUX(mac, linux) mac +#else +#define DLLOADER_MAC_LINUX(mac, linux) linux +#endif + +#include "Framework/Logger.h" + +namespace o2::utils +{ + +// Manages dynamic loading and unloading (or rather ensuring they are not +// unloaded for the duration of the program) of libraries and symbol lookups. It +// ensures thread-safety through the use of a mutex and implements the Meyers +// Singleton pattern to provide a single instance of the manager. +template +class DLLoaderBase +{ + public: + struct filename_decorations { + static constexpr const char* prefix = "lib"; // same prefix on mac and linux + static constexpr const char* suffix = DLLOADER_MAC_LINUX(".dylib", ".so"); + }; + enum Options { + none = 0, + global = RTLD_GLOBAL, + local = RTLD_LOCAL, + no_delete = RTLD_NODELETE, + no_load = RTLD_NOLOAD, + lazy = RTLD_LAZY, + }; + using handle_t = void; + using handle_ptr_t = handle_t*; + struct HandleDeleter { + void operator()(handle_ptr_t p) + { + if (p != nullptr) { + dlclose(p); + } + } + }; + using library_t = std::unique_ptr; + + // Returns the singleton instance of the manager. Any function should only be + // accessed through an instance. This being a singleton serves two purposes: + // 1. Libraries are loaded only once. + // 2. Once loaded they are not again unloaded until the end of the program. + static DerivedType& Instance() + { + return DerivedType::sInstance; + } + + // Loads a dynamic library by its name and stores its handle. Returns true + // if the library is successfully loaded or already loaded. + bool addLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) != mLibraries.end()) { + return true; // Library already loaded + } + + if (mO2Path.empty()) { + if (const auto* path = std::getenv("O2_ROOT")) { + mO2Path = path; + } else { + LOGP(error, "$O2_ROOT not set!"); + return false; + } + } + + auto path = getO2Path(library); + if (!std::filesystem::exists(path)) { + LOGP(error, "Library under '{}' does not exist!", path); + return false; + } + + try { + auto lib = std::unique_ptr(dlopen(path.c_str(), mLoadPolicy)); + if (lib == nullptr) { + throw std::runtime_error("Library handle is nullptr!"); + } + mLibraries[library] = std::move(lib); + LOGP(info, "Loaded dynamic library '{}' from '{}'", library, path); + return true; + } catch (std::exception& e) { + LOGP(error, "Failed to load library (path='{}'), failed reason: '{}'", path, e.what()); + return false; + } catch (...) { + LOGP(error, "Failed to load library (path='{}') for unknown reason!", path.c_str()); + return false; + } + } + + // Unloads a given library returns true if this succeeded. + // + // Nota bene: Actually, we have very little control here when the unloading + // hapens since the linkder decides this based on if there is any reference + // left. And even if the reference counter goes to zero the linker is free to + // leave the library loaded and clean up whenever it wants. + bool unloadLibrary(const std::string& library) + { + const std::lock_guard lock(mLock); + + if (auto it = mLibraries.find(library); it != mLibraries.end()) { + mLibraries.erase(it); + return true; + } + + LOGP(error, "No '{}' library found, cannot unload it!", library); + return false; + } + + // Resets all loaded libraries and O2Path, this invalidates all outside kept + // references. + void reset() + { + mO2Path.clear(); + mLibraries.clear(); + } + + // Checks if a library contains a specific symbol. + bool hasSymbol(const std::string& library, const std::string& symbol) + { + const std::lock_guard lock(mLock); + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return false; + } + } + + dlerror(); // clear previous error + + // Checks if the symbol exists but does not load it. + handle_ptr_t ptr = dlsym(mLibraries[library].get(), symbol.c_str()); + if (const auto* err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", symbol, library, err); + } + + return ptr != nullptr; + } + + // Executes a function from a loaded library or return nullopt + template + std::optional executeFunction(const std::string& library, const std::string& fname, Args... args) + { + using Func_t = Ret (*)(Args...); + + const std::lock_guard lock(mLock); + + if (fname.empty()) { + LOGP(error, "Function name cannot be empty!"); + return std::nullopt; + } + + if (mLibraries.find(library) == mLibraries.end()) { + // Library not loaded, attempt to load it + if (!addLibrary(library)) { + return std::nullopt; + } + } + + const auto& lib = mLibraries[library].get(); + if (!hasSymbol(library, fname)) { + LOGP(error, "Library '{}' does not have a symbol '{}'", library, fname); + return std::nullopt; + } + + dlerror(); // Clear previous error + + auto func = (Func_t)dlsym(lib, fname.c_str()); + if (const auto* err = dlerror(); err != nullptr) { + LOGP(error, "Did not get {} from {}; error: {}", fname, library, err); + return std::nullopt; + } + + if (func == nullptr) { + LOGP(error, "Library '{}' does not have a symbol '{}' with {}", library, fname, getTypeName()); + return std::nullopt; + } + + // Execute function and return its return value + return func(args...); + } + + // Wrapper for function execution which fatals if execution fails + template + Ret executeFunctionAlias(const std::string& library, const std::string& fname, Args... args) + { + if (auto opt = executeFunction(library, fname, args...)) { + return *opt; + } + + LOGP(fatal, "Execution of '{}' from '{}' failed spectaculary!", fname, library); + __builtin_unreachable(); // is this safe, AFACIT only gcc and clang are supported anyway + } + + // Delete copy and move constructors to enforce singleton pattern. + DLLoaderBase(const DLLoaderBase&) = delete; + DLLoaderBase& operator=(const DLLoaderBase&) = delete; + DLLoaderBase(DLLoaderBase&&) = delete; + DLLoaderBase& operator=(DLLoaderBase&&) = delete; + + protected: + // Constructor and destructor are protected to enforce singleton pattern. + DLLoaderBase() = default; + ~DLLoaderBase() = default; + + private: + // Returns the full path to a O2 shared library.. + [[nodiscard]] std::string getO2Path(const std::string& library) const + { + return mO2Path + "/lib/" + filename_decorations::prefix + library + filename_decorations::suffix; + } + + // Returns the demangled type name of a prototype, e.g., for pretty printing. + template + [[nodiscard]] auto getTypeName() -> std::string + { + return typeid(ProtoType).name(); // TODO + } + + std::unordered_map mLibraries{}; // Pointers to loaded libraries, calls `unload()' for each library, e.g., correctly destroy this object + std::recursive_mutex mLock{}; // While a recursive mutex is more expansive it makes locking easier + std::string mO2Path{}; // Holds the path O2 dynamic library determined by $O2_ROOT + Options mLoadPolicy{Options::lazy}; // load resolution policy +}; + +} // namespace o2::utils + +#define O2DLLoaderDef(classname) \ + private: \ + static classname sInstance; \ + classname() = default; \ + friend class o2::utils::DLLoaderBase; + +#define O2DLLoaderImpl(classname) classname classname::sInstance; + +#endif // DLLOADER_H_ diff --git a/Common/Utils/include/CommonUtils/DebugStreamer.h b/Common/Utils/include/CommonUtils/DebugStreamer.h index 0c1ac9af6eef7..741afaabfdd60 100644 --- a/Common/Utils/include/CommonUtils/DebugStreamer.h +++ b/Common/Utils/include/CommonUtils/DebugStreamer.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #include "CommonUtils/ConfigurableParamHelper.h" +#include #if defined(DEBUG_STREAMER) #include "CommonUtils/TreeStreamRedirector.h" #include @@ -30,15 +31,17 @@ namespace o2::utils /// struct defining the flags which can be used to check if a certain debug streamer is used enum StreamFlags { - streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx - streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information - streamDigits = 1 << 2, ///< stream digit information - streamFastTransform = 1 << 3, ///< stream tpc fast transform - streamITCorr = 1 << 4, ///< stream ion tail correction information - streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) - streamUpdateTrack = 1 << 6, ///< stream update track informations - streamRejectCluster = 1 << 7, ///< stream cluster rejection informations - streamFlagsCount = 8 ///< total number of streamers + streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx + streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information + streamDigits = 1 << 2, ///< stream digit information + streamFastTransform = 1 << 3, ///< stream tpc fast transform + streamITCorr = 1 << 4, ///< stream ion tail correction information + streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) + streamUpdateTrack = 1 << 6, ///< stream update track informations + streamRejectCluster = 1 << 7, ///< stream cluster rejection informations + streamMergeBorderTracksBest = 1 << 8, ///< stream MergeBorderTracks best track + streamMergeBorderTracksAll = 1 << 9, ///< stream MergeBorderTracks all tracks + streamFlagsCount = 10 ///< total number of streamers }; enum SamplingTypes { @@ -46,6 +49,8 @@ enum SamplingTypes { sampleRandom = 1, ///< sample randomly every n points sampleID = 2, ///< sample every n IDs (per example track) sampleIDGlobal = 3, ///< in case different streamers have access to the same IDs use this gloabl ID + sampleWeights = 4, ///< perform sampling on weights, defined where the streamer is called + sampleTsallis = 5, ///< perform sampling on tsallis pdf }; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -121,6 +126,9 @@ class DebugStreamer /// \return returns sampling type and sampling frequency for given streamer static std::pair getSamplingTypeFrequency(const StreamFlags streamFlag); + /// \return returns sampling type and sampling frequency for given streamer + static float getSamplingFrequency(const StreamFlags streamFlag) { return getSamplingTypeFrequency(streamFlag).second; } + ///< return returns unique ID for each CPU thread to give each thread an own output file static size_t getCPUID(); @@ -134,7 +142,7 @@ class DebugStreamer std::string getUniqueTreeName(const char* tree, const size_t id = getCPUID()) const; /// set directly the debug level - static void setStreamFlags(const StreamFlags streamFlags) { o2::conf::ConfigurableParam::setValue("DebugStreamerParam", "streamLevel", static_cast(streamFlags)); } + static void setStreamFlags(const StreamFlags streamFlags) { o2::conf::ConfigurableParam::setValue("DebugStreamerParam", "streamLevel", static_cast(streamFlags)); } /// enable specific streamer flag static void enableStream(const StreamFlags streamFlag); @@ -144,7 +152,8 @@ class DebugStreamer /// check if streamer for specific flag is enabled /// \param samplingID optional index of the data which is streamed in to perform sampling on this index - static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1); + /// \param weight weight which can be used to perform some weightes sampling + static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1, const float weight = 1); /// merge trees with the same content structure, but different naming /// \param inpFile input file containing several trees with the same content @@ -155,10 +164,12 @@ class DebugStreamer /// \return returns integer index for given streamer flag static int getIndex(const StreamFlags streamFlag); + /// get random value between min and max + static float getRandom(float min = 0, float max = 1); + private: using StreamersPerFlag = tbb::concurrent_unordered_map>; StreamersPerFlag mTreeStreamer; ///< streamer which is used for the debugging - #else // empty implementation of the class for GPU or when the debug streamer is not build for CPU diff --git a/Common/Utils/include/CommonUtils/EnumFlags.h b/Common/Utils/include/CommonUtils/EnumFlags.h new file mode 100644 index 0000000000000..e7481c903e666 --- /dev/null +++ b/Common/Utils/include/CommonUtils/EnumFlags.h @@ -0,0 +1,796 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_FLAGS_H_ +#define O2_FRAMEWORK_FLAGS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CommonUtils/StringUtils.h" + +namespace o2::utils +{ + +namespace details::enum_flags +{ + +// Require that an enum with an underlying unsigned type. +template +concept EnumFlagHelper = requires { + requires std::is_enum_v; + requires std::is_unsigned_v>; + requires std::same_as>; +}; + +// Static constexpr only helper struct to implement modicum of enum reflection +// functions and also check via concepts expected properties of the enum. +// This is very much inspired by much more extensive libraries like magic_enum. +// Inspiration by its c++20 version (https://github.com/fix8mt/conjure_enum). +// NOTE: Cannot detect if bit values past the underlying type are defined. +template +struct FlagsHelper final { + using U = std::underlying_type_t; + using UMax = uint64_t; // max represetable type + static_assert(std::numeric_limits::digits <= std::numeric_limits::digits, "Underlying type has more digits than max supported digits"); + + static constexpr bool isScoped() noexcept + { + return std::is_enum_v && !std::is_convertible_v>; + } + + // Return line at given position. + template + static consteval const char* tpeek() noexcept + { + return std::source_location::current().function_name(); + } + // string_view value of function above + template + static constexpr std::string_view tpeek_v{tpeek()}; + + // Compiler Specifics + static constexpr auto CSpecifics{std::to_array< + std::tuple>({ +#if defined __clang__ + {"e = ", ']', "(anonymous namespace)", '('}, + {"T = ", ']', "(anonymous namespace)", '('}, +#else // assuming __GNUC__ + {"e = ", ';', "", '<'}, + {"T = ", ']', "{anonymous}", '{'}, +#endif + })}; + enum class SVal : uint8_t { Start, + End, + AnonStr, + AnonStart }; + enum class SType : uint8_t { Enum_t, + Type_t, + eT0, + eT1, + eT2, + eT3 }; + // Extract a compiler specification. + template + static constexpr auto getSpec() noexcept + { + return std::get(v)>(CSpecifics[static_cast(t)]); + } + + // Range that is scanned by the compiler + static constexpr size_t MinScan{0}; + static constexpr size_t MarginScan{1}; // Scan one past to check for overpopulation + static constexpr size_t MaxUnderScan{std::numeric_limits::digits}; // Maximum digits the underlying type has + static constexpr size_t MaxScan{MaxUnderScan + MarginScan}; + + // Checks if a given 'location' contains an enum. + template + static constexpr bool isValid() noexcept + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return false; + } +#if defined __clang__ + else if constexpr (tpeek_v[tp + getSpec().size()] == '(') { + if constexpr (tpeek_v[tp + getSpec().size() + 1] == '(') { + return false; + } + if constexpr (tpeek_v.find(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } + } else if constexpr (tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + // check if this is an anonymous enum + return true; + } +#elif __GNUC__ + else if constexpr (tpeek_v[tp + getSpec().size()] != '(' && tpeek_v.find_first_of(getSpec(), tp + getSpec().size()) != std::string_view::npos) { + return true; + } +#else +#error Unsupported compiler +#endif + return false; + } + + // Extract which values are present in the enum by checking all values in + // the min-max-range above. + template + static constexpr auto getValues(std::index_sequence /*unused*/) noexcept + { + constexpr std::array valid{isValid(MinScan + I)>()...}; + constexpr auto count{std::count_if(valid.cbegin(), valid.cend(), [](bool v) noexcept { return v; })}; + static_assert(count > 0, "EnumFlag requires at least one enum value. Check that your enum has consecutive values starting from 0."); + static_assert(count <= MaxUnderScan, "Too many enum values for underlying type. Consider using a larger underlying type or fewer enum values."); + std::array values{}; + for (size_t idx{}, n{}; n < count; ++idx) { + if (valid[idx]) { + values[n++] = static_cast(MinScan + idx); + } + } + return values; + } + static constexpr auto Values{getValues(std::make_index_sequence())}; // Enum Values + static constexpr auto count() noexcept { return Values.size(); } // Number of enum members + static constexpr auto Min_v{Values.front()}; // Enum first entry + static constexpr auto Max_v{Values.back()}; // Enum last entry + static constexpr auto Min_u_v{static_cast(Min_v)}; // Enum first entry as size_t + static constexpr auto Max_u_v{static_cast(Max_v)}; // Enum last entry as size_t + static_assert(Max_u_v < std::numeric_limits::digits, "Max Bit is beyond allow range deferred from underlying type"); + static constexpr bool isContinuous() noexcept { return (Max_u_v - Min_u_v + 1) == count(); } // Is the enum continuous + static constexpr UMax makeMaxRep(size_t min, size_t max) + { + const size_t width = max - min + 1; + if (width >= std::numeric_limits::digits) { + return std::numeric_limits::max(); + } + return ((UMax(1) << width) - 1) << min; + } + static constexpr auto MaxRep{makeMaxRep(Min_u_v, Max_u_v)}; // largest representable value + + template + static constexpr std::string_view getName() + { + constexpr auto tp{tpeek_v.rfind(getSpec())}; + if constexpr (tp == std::string_view::npos) { + return {}; + } + if constexpr (tpeek_v[tp + getSpec().size()] == getSpec()) { +#if defined __clang__ + if constexpr (tpeek_v[tp + getSpec().size() + 1] == getSpec()) { + return {}; + } +#endif + if (constexpr auto lstr{tpeek_v.substr(tp + getSpec().size())}; lstr.find(getSpec()) != std::string_view::npos) { // is anon + if constexpr (constexpr auto lc{lstr.find_first_of(getSpec())}; lc != std::string_view::npos) { + return lstr.substr(getSpec().size() + 2, lc - (getSpec().size() + 2)); + } + } + } + constexpr std::string_view result{tpeek_v.substr(tp + getSpec().size())}; + if constexpr (constexpr auto lc{result.find_first_of(getSpec())}; lc != std::string_view::npos) { + return result.substr(0, lc); + } else { + return {}; + } + } + + static constexpr std::string_view removeScope(std::string_view s) + { + if (const auto lc{s.find_last_of(':')}; lc != std::string_view::npos) { + return s.substr(lc + 1); + } + return s; + } + + static constexpr std::string_view findScope(std::string_view s) + { + const auto pos1 = s.rfind("::"); + if (pos1 == std::string_view::npos) { + return s; + } + const auto pos2 = s.rfind("::", pos1 - 1); + if (pos2 == std::string_view::npos) { + return s.substr(0, pos1); + } + return s.substr(pos2 + 2, pos1 - pos2 - 2); + } + + template + static constexpr auto getNameValue{getName()}; + + template + static constexpr auto getNames(std::index_sequence /*unused*/) + { + if constexpr (with_scope) { + return std::array{getNameValue...}; + } else { + return std::array{removeScope(getNameValue)...}; + } + } + + static constexpr auto Names{getNames(std::make_index_sequence())}; // Enum names without scope + static constexpr auto NamesScoped{getNames(std::make_index_sequence())}; // Enum names with scope + static constexpr auto Scope{findScope(NamesScoped.front())}; // Enum scope + + static constexpr auto getLongestName() noexcept + { + size_t max{0}; + for (size_t i{0}; i < count(); ++i) { + max = std::max(max, Names[i].size()); + } + return max; + } + + static constexpr auto NamesLongest{getLongestName()}; // Size of longest name + + template + static constexpr std::string_view toString() noexcept + { + return getNameValue(); + } + + static constexpr std::optional fromString(std::string_view str) noexcept + { + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], str) || isIEqual(NamesScoped[i], str)) { + return Values[i]; + } + } + return std::nullopt; + } + + // Convert char to lower. + static constexpr unsigned char toLower(const unsigned char c) noexcept + { + return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + } + + // Are these chars equal (case-insensitive). + static constexpr bool isIEqual(const unsigned char a, const unsigned char b) noexcept + { + return toLower(a) == toLower(b); + } + + // Case-insensitive comparison for string_view. + static constexpr bool isIEqual(std::string_view s1, std::string_view s2) noexcept + { + if (s1.size() != s2.size()) { + return false; + } + for (size_t i{0}; i < s1.size(); ++i) { + if (!isIEqual(s1[i], s2[i])) { + return false; + } + } + return true; + } + + static constexpr std::string_view None{"none"}; + static constexpr bool hasNone() noexcept + { + // check that enum does not contain member named 'none' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], None)) { + return true; + } + } + return false; + } + + static constexpr std::string_view All{"all"}; + static constexpr bool hasAll() noexcept + { + // check that enum does not contain member named 'all' + for (size_t i{0}; i < count(); ++i) { + if (isIEqual(Names[i], All)) { + return true; + } + } + return false; + } +}; + +} // namespace details::enum_flags + +// Require an enum to fullfil what one would except from a bitset. +template +concept EnumFlag = requires { + // range checks + requires details::enum_flags::FlagsHelper::Min_u_v == 0; // the first bit should be at position 0 + requires details::enum_flags::FlagsHelper::Max_u_v < details::enum_flags::FlagsHelper::count(); // the maximum is less than the total + requires details::enum_flags::FlagsHelper::isContinuous(); // do not allow missing bits + + // type checks + requires !details::enum_flags::FlagsHelper::hasNone(); // added automatically + requires !details::enum_flags::FlagsHelper::hasAll(); // added automatically +}; + +/** + * \brief Class to aggregate and manage enum-based on-off flags. + * + * This class manages flags as bits in the underlying type of an enum (upto 64 bits), allowing + * manipulation via enum member names. It supports operations akin to std::bitset + * but is fully constexpr and is ideal for aggregating multiple on-off booleans, + * e.g., enabling/disabling algorithm features. + * + * Example: + * enum class AlgoOptions { + * Feature1, + * Feature2, + * Feature3, + * }; + * ... + * EnumFlags opts; + * opts.set("Feature1 | Feature3"); // Set Feature1 and Feature3. + * if (opts[AlgoOptions::Feature1]) { // Do some work. } // Check if Feature1 is set. + * + * Additional examples of how to use this class are in testEnumFlags.cxx. + */ +template +class EnumFlags +{ + static constexpr int DefaultBase{2}; + using H = details::enum_flags::FlagsHelper; + using U = std::underlying_type_t; + U mBits{0}; + + // Converts enum to its underlying type. + constexpr auto to_underlying(E e) const noexcept + { + return static_cast(e); + } + + // Returns the bit representation of a flag. + constexpr auto to_bit(E e) const noexcept + { + return U(1) << to_underlying(e); + } + + public: + // Default constructor. + constexpr explicit EnumFlags() = default; + // Constructor to initialize with a single flag. + constexpr explicit EnumFlags(E e) : mBits(to_bit(e)) {} + // Copy constructor. + constexpr EnumFlags(const EnumFlags&) = default; + // Move constructor. + constexpr EnumFlags(EnumFlags&&) = default; + // Constructor to initialize with the underlying type. + constexpr explicit EnumFlags(U u) : mBits(u) {} + // Initialize with a list of flags. + constexpr EnumFlags(std::initializer_list flags) noexcept + { + std::for_each(flags.begin(), flags.end(), [this](const E f) noexcept { mBits |= to_bit(f); }); + } + // Init from a string. + // + explicit EnumFlags(const std::string& str, int base = DefaultBase) + { + set(str, base); + } + // Destructor. + constexpr ~EnumFlags() = default; + + static constexpr U None{0}; // Represents no flags set. + static constexpr U All{H::MaxRep}; // Represents all flags set. + + // Return list of all enum values + static constexpr auto getValues() noexcept + { + return H::Values; + } + + // Return list of all enum Names + static constexpr auto getNames() noexcept + { + return H::Names; + } + + // Sets flags from a string representation. + // This can be either from a number representation (binary or digits) or + // a concatenation of the enums members name e.g., 'Enum1|Enum2|...' + void set(const std::string& s, int base = DefaultBase) + { + if (s.empty()) { // no-op + return; + } + // on throw restore previous state and rethrow + const U prev = mBits; + reset(); + try { + setImpl(s, base); + } catch (const std::exception& e) { + mBits = prev; + throw; + } + } + // Returns the raw bitset value. + [[nodiscard]] constexpr auto value() const noexcept + { + return mBits; + } + + // Resets all flags. + constexpr void reset() noexcept + { + mBits = U(0); + } + + // Resets a specific flag. + template T> + constexpr void reset(T t) + { + mBits &= ~to_bit(t); + } + + // Tests if a specific flag is set. + template T> + [[nodiscard]] constexpr bool test(T t) const noexcept + { + return (mBits & to_bit(t)) != None; + } + + // Tests if all specified flags are set. + template ... Ts> + [[nodiscard]] constexpr bool test(Ts... flags) const noexcept + { + return ((test(flags) && ...)); + } + + // Sets a specific flag. + template T> + constexpr void set(T t) noexcept + { + mBits |= to_bit(t); + } + + // Sets multiple specific flags. + template ... Ts> + constexpr void set(Ts... flags) noexcept + { + (set(flags), ...); + } + + // Toggles a specific flag. + template T> + constexpr void toggle(T t) noexcept + { + mBits ^= to_bit(t); + } + + // Checks if any flag is set. + [[nodiscard]] constexpr bool any() const noexcept + { + return mBits != None; + } + + // Checks if all flags are set. + [[nodiscard]] constexpr bool all() const noexcept + { + return mBits == All; + } + + // Returns the bitset as a binary string. + [[nodiscard]] std::string string() const + { + std::ostringstream oss; + oss << std::bitset(mBits); + return oss.str(); + } + + // Returns the bitset as a pretty multiline binary string. + [[nodiscard]] std::string pstring(bool withNewline = false) const + { + std::ostringstream oss; + if (withNewline) { + oss << '\n'; + } + oss << "0b"; + const std::bitset bits(mBits); + oss << bits; + if constexpr (H::isScoped()) { + oss << " " << H::Scope; + } + oss << '\n'; + for (size_t i = 0; i < H::count(); ++i) { + oss << " "; + for (size_t j = 0; j < H::count() - i - 1; ++j) { + oss << "┃"; + } + oss << "┗"; + for (size_t a{2 + i}; --a != 0U;) { + oss << "━"; + } + oss << " " << std::setw(H::NamesLongest) << std::left + << H::Names[i] << " " << (bits[i] ? "[Active]" : "[Inactive]"); + if (i != H::count() - 1) { + oss << "\n"; + } + } + return oss.str(); + } + + // Checks if any flag is set (Boolean context). + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return any(); + } + + // Check if given flag is set. + template T> + [[nodiscard]] constexpr bool operator[](const T t) const noexcept + { + return test(t); + } + + // Checks if two flag sets are equal. + [[nodiscard]] constexpr bool operator==(const EnumFlags& o) const noexcept + { + return mBits == o.mBits; + } + + // Checks if two flag sets are not equal. + [[nodiscard]] constexpr bool operator!=(const EnumFlags& o) const noexcept + { + return mBits != o.mBits; + } + + // Copy assignment operator + constexpr EnumFlags& operator=(const EnumFlags& o) = default; + + // Move assignment operator + constexpr EnumFlags& operator=(EnumFlags&& o) = default; + + // Performs a bitwise OR with a flag. + template T> + constexpr EnumFlags& operator|=(T t) noexcept + { + mBits |= to_bit(t); + return *this; + } + + // Performs a bitwise AND with a flag. + template T> + constexpr EnumFlags& operator&=(T t) noexcept + { + mBits &= to_bit(t); + return *this; + } + + // Returns a flag set with a bitwise AND. + template T> + constexpr EnumFlags operator&(T t) const noexcept + { + return EnumFlags(mBits & to_bit(t)); + } + + // Returns a flag set with all bits inverted. + constexpr EnumFlags operator~() const noexcept + { + return EnumFlags(~mBits); + } + + // Performs a bitwise OR with another flag set. + constexpr EnumFlags operator|(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Performs a bitwise OR assignment. + constexpr EnumFlags& operator|=(const EnumFlags& o) noexcept + { + mBits |= o.mBits; + return *this; + } + + // Performs a bitwise XOR with another flag set. + constexpr EnumFlags operator^(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits ^ o.mBits); + } + + // Performs a bitwise and with another flag set. + constexpr EnumFlags operator&(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); + } + + // Performs a bitwise XOR assignment. + constexpr EnumFlags& operator^=(const EnumFlags& o) noexcept + { + mBits ^= o.mBits; + return *this; + } + + // Checks if all specified flags are set. + template + [[nodiscard]] constexpr bool all_of(Ts... flags) const noexcept + { + return test(flags...); + } + + // Checks if none of the specified flags are set. + template + [[nodiscard]] constexpr bool none_of(Ts... flags) const noexcept + { + return (!(test(flags) || ...)); + } + + // Serializes the flag set to a string. + [[nodiscard]] std::string serialize() const + { + return std::to_string(mBits); + } + + // Deserializes a string into the flag set. + void deserialize(const std::string& data) + { + typename H::UMax v = std::stoul(data); + if (v > H::MaxRep) { + throw std::out_of_range("Values exceeds enum range."); + } + mBits = static_cast(v); + } + + // Counts the number of set bits (active flags). + [[nodiscard]] constexpr size_t count() const noexcept + { + return std::popcount(mBits); + } + + // Returns the union of two flag sets. + [[nodiscard]] constexpr EnumFlags union_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits | o.mBits); + } + + // Returns the intersection of two flag sets. + [[nodiscard]] constexpr EnumFlags intersection_with(const EnumFlags& o) const noexcept + { + return EnumFlags(mBits & o.mBits); + } + + // Checks if all flags in another Flags object are present in the current object. + [[nodiscard]] constexpr bool contains(const EnumFlags& other) const noexcept + { + return (mBits & other.mBits) == other.mBits; + } + + private: + // Set implementation, bits was zeroed before. + void setImpl(const std::string& s, int base = 2) + { + // Helper to check if character is valid for given base + auto isValidForBase = [](unsigned char c, int base) -> bool { + if (base == 2) { + return c == '0' || c == '1'; + } + if (base == 10) { + return std::isdigit(c); + } + if (base == 16) { + return std::isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + return false; + }; + + // hex + if (base == 16) { + std::string_view hex_str{s}; + // Strip optional 0x or 0X prefix + if (s.size() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + hex_str.remove_prefix(2); + } + if (hex_str.empty()) { + throw std::invalid_argument("Empty hexadecimal string."); + } + if (!std::all_of(hex_str.begin(), hex_str.end(), [&](unsigned char c) { return isValidForBase(c, 16); })) { + throw std::invalid_argument("Invalid hexadecimal string."); + } + typename H::UMax v = std::stoul(std::string(hex_str), nullptr, 16); + if (v > H::MaxRep) { + throw std::out_of_range("Value exceeds enum range."); + } + mBits = static_cast(v); + return; + } + + // decimal and binary + if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); })) { + if (base == 2) { + // Binary: check only 0 and 1 + if (!std::all_of(s.begin(), s.end(), [&](unsigned char c) { return isValidForBase(c, 2); })) { + throw std::invalid_argument("Invalid binary string."); + } + } + typename H::UMax v = std::stoul(std::string(s), nullptr, base); + if (v > H::MaxRep) { + throw std::out_of_range("Value exceeds enum range."); + } + mBits = static_cast(v); + } + // enum name strings + else if (std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalnum(c) != 0 || c == '|' || c == ' ' || c == ':' || c == ',' || c == ';'; })) { + std::string cs{s}; + std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); }); + + if (cs == H::All) { + mBits = All; + } else if (cs == H::None) { + mBits = None; + } else { + // Detect delimiter and ensure only one type is used + char token = ' '; + size_t pipePos = s.find('|'); + size_t commaPos = s.find(','); + size_t semiPos = s.find(';'); + + // Count how many different delimiters exist + int delimiterCount = (pipePos != std::string_view::npos ? 1 : 0) + + (commaPos != std::string_view::npos ? 1 : 0) + + (semiPos != std::string_view::npos ? 1 : 0); + + if (delimiterCount > 1) { + throw std::invalid_argument("Mixed delimiters not allowed!"); + } + + if (pipePos != std::string_view::npos) { + token = '|'; + } else if (commaPos != std::string_view::npos) { + token = ','; + } else if (semiPos != std::string_view::npos) { + token = ';'; + } + + for (const auto& tok : Str::tokenize(std::string(s), token)) { + if (auto e = H::fromString(tok)) { + mBits |= to_bit(*e); + } else { + throw std::invalid_argument(tok + " is not a valid enum value!"); + } + } + } + } else { + throw std::invalid_argument("Cannot parse string!"); + } + } +}; + +template +std::ostream& operator<<(std::ostream& os, const EnumFlags& f) +{ + os << f.pstring(true); + return os; +} + +} // namespace o2::utils + +#endif diff --git a/Common/Utils/include/CommonUtils/FIFO.h b/Common/Utils/include/CommonUtils/FIFO.h index 94942277daa0a..83afa0b2206c9 100644 --- a/Common/Utils/include/CommonUtils/FIFO.h +++ b/Common/Utils/include/CommonUtils/FIFO.h @@ -17,6 +17,7 @@ #include #include +#include namespace o2 { diff --git a/Common/Utils/include/CommonUtils/IRFrameSelector.h b/Common/Utils/include/CommonUtils/IRFrameSelector.h index 6312ae8314c3a..a4365030b6a12 100644 --- a/Common/Utils/include/CommonUtils/IRFrameSelector.h +++ b/Common/Utils/include/CommonUtils/IRFrameSelector.h @@ -46,6 +46,8 @@ class IRFrameSelector auto getIRFrames() const { return mFrames; } bool isSet() const { return mIsSet; } + void setOwnList(const std::vector& lst, bool toBeSorted); + private: gsl::span mFrames{}; // externally provided span of IRFrames, must be sorted in IRFrame.getMin() o2::dataformats::IRFrame mLastIRFrameChecked{}; // last frame which was checked diff --git a/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h index 80545997af159..25205ce05ab40 100644 --- a/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h +++ b/Common/Utils/include/CommonUtils/RootSerializableKeyValueStore.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace o2 { @@ -39,14 +40,19 @@ class RootSerializableKeyValueStore /// Structure encapsulating the stored information: raw buffers and attached type information (combination of type_index_hash and TClass information) struct SerializedInfo { SerializedInfo() = default; - SerializedInfo(void* o, int N, char* b, TClass* cl, std::string const& s) : objptr(o), N(N), bufferptr(b), cl(cl), typeinfo_name(s) {} + SerializedInfo(int N, + std::unique_ptr buffer, TClass const* cl, std::string const& s) : N(N), cl(cl), typeinfo_name(s) + { + bufferptr = buffer.get(); + buffer.release(); + } SerializedInfo(SerializedInfo const& other) { // we do a deep copy N = other.N; bufferptr = new char[N]; + std::memcpy(bufferptr, other.bufferptr, sizeof(char) * N); - objptr = nullptr; cl = other.cl; typeinfo_name = other.typeinfo_name; } @@ -56,13 +62,18 @@ class RootSerializableKeyValueStore std::swap(*this, temp); return *this; } + ~SerializedInfo() + { + // we are the owner of this ... so delete it + delete bufferptr; + } - void* objptr = nullptr; //! pointer to existing object in memory + void* objptr = nullptr; //! pointer for "caching" Int_t N = 0; char* bufferptr = nullptr; //[N] pointer to serialized buffer // we use the TClass and/or the type_index_hash for type idendification - TClass* cl = nullptr; + TClass const* cl = nullptr; std::string typeinfo_name; // typeinfo name that can be used to store type if TClass not available (for PODs!) ClassDefNV(SerializedInfo, 1); }; @@ -141,8 +152,15 @@ class RootSerializableKeyValueStore mStore.clear(); } - /// print list of keys and type information - void print() const; + /// print list of keys, values (and optionally type information) + void print(bool includetypeinfo = false) const; + + /// resets store to the store of another object + void copyFrom(RootSerializableKeyValueStore const& other) + { + mStore.clear(); + mStore = other.mStore; + } private: std::map mStore; @@ -153,21 +171,21 @@ class RootSerializableKeyValueStore { // make sure we have a TClass for this // if there is a TClass, we'll use ROOT serialization to encode into the buffer - auto ptr = new T(value); + auto ptr = std::make_unique(T{value}); auto cl = TClass::GetClass(typeid(value)); if (!cl) { state = GetState::kNOTCLASS; return; } - char* bufferptr = nullptr; + std::unique_ptr bufferptr(nullptr); TBufferFile buff(TBuffer::kWrite); - buff.WriteObjectAny(ptr, cl); + buff.WriteObjectAny(ptr.get(), cl); int N = buff.Length(); - bufferptr = new char[N]; - memcpy(bufferptr, buff.Buffer(), sizeof(char) * N); + bufferptr.reset(new char[N]); + memcpy(bufferptr.get(), buff.Buffer(), sizeof(char) * N); auto name = std::type_index(typeid(value)).name(); - mStore.insert(std::pair(key, SerializedInfo((void*)ptr, N, (char*)bufferptr, cl, name))); + mStore.insert(std::pair(key, SerializedInfo(N, std::move(bufferptr), cl, name))); } // implementation for put for trivial types @@ -177,13 +195,13 @@ class RootSerializableKeyValueStore // we forbid pointers static_assert(!std::is_pointer::value); // serialization of trivial types is easy (not based on ROOT) - auto ptr = new T(value); + auto ptr = std::make_unique(T{value}); int N = sizeof(T); - auto bufferptr = new char[N]; - memcpy(bufferptr, (char*)ptr, sizeof(char) * N); + std::unique_ptr bufferptr(new char[N]); + memcpy(bufferptr.get(), (char*)ptr.get(), sizeof(char) * N); auto name = std::type_index(typeid(value)).name(); - mStore.insert(std::pair(key, SerializedInfo((void*)ptr, N, (char*)bufferptr, nullptr, name))); + mStore.insert(std::pair(key, SerializedInfo(N, std::move(bufferptr), nullptr, name))); } // generic implementation for get relying on TClass diff --git a/Common/Utils/include/CommonUtils/ShmManager.h b/Common/Utils/include/CommonUtils/ShmManager.h index 06dba283fec82..e7174b909b82c 100644 --- a/Common/Utils/include/CommonUtils/ShmManager.h +++ b/Common/Utils/include/CommonUtils/ShmManager.h @@ -23,10 +23,15 @@ #include #include +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) #include #include +#endif +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) && !defined(__APPLE__) +// this shared mem mode is meant for compiled stuff in o2-sim; not for ROOT sessions #define USESHM 1 +#endif namespace o2 { @@ -113,8 +118,10 @@ class ShmManager void* tryAttach(bool& success); size_t getPointerOffset(void* ptr) const { return (size_t)((char*)ptr - (char*)mBufferPtr); } +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) boost::interprocess::wmanaged_external_buffer* boostmanagedbuffer; boost::interprocess::allocator* boostallocator; +#endif }; } // namespace utils diff --git a/Common/Utils/include/CommonUtils/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index 2402f3faa975c..710632fc7dbfe 100644 --- a/Common/Utils/include/CommonUtils/StringUtils.h +++ b/Common/Utils/include/CommonUtils/StringUtils.h @@ -20,8 +20,7 @@ #include #include #include -#include -#include +#include "GPUCommonRtypes.h" namespace o2 { @@ -41,6 +40,13 @@ struct Str { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); } + static inline void ltrim(std::string& s, const std::string& start) + { + if (beginsWith(s, start)) { + s.erase(0, start.size()); + } + } + /** Trim from end (in place) * * @param s @@ -50,6 +56,13 @@ struct Str { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } + static inline void rtrim(std::string& s, const std::string& ending) + { + if (endsWith(s, ending)) { + s.erase(s.size() - ending.size(), ending.size()); + } + } + /** * Trim from both ends (in place) * @param s @@ -72,6 +85,13 @@ struct Str { return ss; } + static inline std::string ltrim_copy(const std::string& s, const std::string& start) + { + std::string ss = s; + ltrim(ss, start); + return ss; + } + /** * Trim from end (copying) * @param s @@ -84,6 +104,13 @@ struct Str { return ss; } + static inline std::string rtrim_copy(const std::string& s, const std::string& ending) + { + std::string ss = s; + rtrim(ss, ending); + return ss; + } + /** * Trim from both sides (copying) * @param s @@ -118,6 +145,9 @@ struct Str { return s.str(); } + // replace all occurencies of from by to, return count + static int replaceAll(std::string& s, const std::string& from, const std::string& to); + // generate random string of given length, suitable for file names static std::string getRandomString(int length); diff --git a/Common/Utils/include/CommonUtils/TreeStream.h b/Common/Utils/include/CommonUtils/TreeStream.h index 2c55f48c98d3a..d1d4527ffc99d 100644 --- a/Common/Utils/include/CommonUtils/TreeStream.h +++ b/Common/Utils/include/CommonUtils/TreeStream.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "GPUCommonDef.h" class TBranch; @@ -39,10 +41,79 @@ namespace utils /// /// See testTreeStream.cxx for functional example /// +namespace details +{ +template +struct IsTrivialRootType { + static constexpr bool value = + std::is_same_v || // Float_t + std::is_same_v || // Double_t + std::is_same_v || std::is_same_v || // ULong64_t or ULong_t + std::is_same_v || std::is_same_v || // Long64_t or Long_t + std::is_same_v || // UInt_t + std::is_same_v || // Int_t + std::is_same_v || // UShort_t + std::is_same_v || // Short_t + std::is_same_v || // UChar_t + std::is_same_v || std::is_same_v || std::is_same_v; // Char_t, int8_t, or Bool_t +}; + +template +struct IsTrivialRootType { + static constexpr bool value = IsTrivialRootType::value; +}; + +template +struct IsTrivialRootType { + static constexpr bool value = IsTrivialRootType::value; +}; + +template +concept TrivialRootType = IsTrivialRootType::value; + +template +concept ComplexRootType = !IsTrivialRootType::value; + +template +static constexpr char getRootTypeCode() +{ + if constexpr (std::is_array_v) { + return getRootTypeCode>(); + } else if constexpr (std::is_same_v) { + return 'F'; + } else if constexpr (std::is_same_v) { + return 'D'; + } else if constexpr (std::is_same_v || + std::is_same_v) { + return 'l'; + } else if constexpr (std::is_same_v || + std::is_same_v) { + return 'L'; + } else if constexpr (std::is_same_v) { + return 'i'; + } else if constexpr (std::is_same_v) { + return 'I'; + } else if constexpr (std::is_same_v) { + return 's'; + } else if constexpr (std::is_same_v) { + return 'S'; + } else if constexpr (std::is_same_v) { + return 'b'; + } else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v) { + return 'B'; + } else { + static_assert(false, "unsupported type!"); + } +} +} // namespace details + class TreeStream { public: struct TreeDataElement { + int arsize = 1; ///< size of array char type = 0; ///< type of data element const TClass* cls = nullptr; ///< data type pointer const void* ptr = nullptr; ///< pointer to element @@ -63,81 +134,11 @@ class TreeStream const char* getName() const { return mTree.GetName(); } void setID(int id) { mID = id; } int getID() const { return mID; } - TreeStream& operator<<(const Bool_t& b) - { - CheckIn('B', &b); - return *this; - } - - TreeStream& operator<<(const Char_t& c) - { - CheckIn('B', &c); - return *this; - } - - TreeStream& operator<<(const UChar_t& c) - { - CheckIn('b', &c); - return *this; - } - - TreeStream& operator<<(const Short_t& h) - { - CheckIn('S', &h); - return *this; - } - - TreeStream& operator<<(const UShort_t& h) - { - CheckIn('s', &h); - return *this; - } - - TreeStream& operator<<(const Int_t& i) - { - CheckIn('I', &i); - return *this; - } - - TreeStream& operator<<(const UInt_t& i) - { - CheckIn('i', &i); - return *this; - } - - TreeStream& operator<<(const Long_t& l) - { - CheckIn('L', &l); - return *this; - } - - TreeStream& operator<<(const ULong_t& l) - { - CheckIn('l', &l); - return *this; - } - - TreeStream& operator<<(const Long64_t& l) - { - CheckIn('L', &l); - return *this; - } - - TreeStream& operator<<(const ULong64_t& l) - { - CheckIn('l', &l); - return *this; - } - - TreeStream& operator<<(const Float_t& f) - { - CheckIn('F', &f); - return *this; - } - TreeStream& operator<<(const Double_t& d) + template + TreeStream& operator<<(const T& t) { - CheckIn('D', &d); + CheckIn(details::getRootTypeCode(), &t); return *this; } @@ -150,7 +151,7 @@ class TreeStream return *this; } - template ::value, bool>::type* = nullptr> + template ::value, bool>::type* = nullptr> TreeStream& operator<<(const T& obj) { CheckIn(&obj); @@ -168,6 +169,7 @@ class TreeStream int mCurrentIndex = 0; ///< index of current element int mID = -1; ///< identifier of layout int mNextNameCounter = 0; ///< next name counter + int mNextArraySize = 0; ///< next array size int mStatus = 0; ///< status of the layout TString mNextName; ///< name for next entry @@ -184,8 +186,7 @@ Int_t TreeStream::CheckIn(const T* obj) } if (mCurrentIndex >= static_cast(mElements.size())) { - mElements.emplace_back(); - auto& element = mElements.back(); + auto& element = mElements.emplace_back(); element.cls = pClass; TString name = mNextName; if (name.Length()) { @@ -197,6 +198,8 @@ Int_t TreeStream::CheckIn(const T* obj) } element.name = name.Data(); element.ptr = obj; + element.arsize = mNextArraySize; + mNextArraySize = 1; // reset } else { auto& element = mElements[mCurrentIndex]; if (!element.cls) { diff --git a/Common/Utils/include/CommonUtils/TreeStreamRedirector.h b/Common/Utils/include/CommonUtils/TreeStreamRedirector.h index 8199009df400d..80858fecea87b 100644 --- a/Common/Utils/include/CommonUtils/TreeStreamRedirector.h +++ b/Common/Utils/include/CommonUtils/TreeStreamRedirector.h @@ -41,7 +41,7 @@ namespace utils class TreeStreamRedirector { public: - TreeStreamRedirector(const char* fname = "", const char* option = "update"); + TreeStreamRedirector(const char* fname = "", const char* option = "recreate"); virtual ~TreeStreamRedirector(); void Close(); TFile* GetFile() { return mDirectory->GetFile(); } diff --git a/Common/Utils/src/BoostHistogramUtils.cxx b/Common/Utils/src/BoostHistogramUtils.cxx index c5c9d97742976..279a709b97586 100644 --- a/Common/Utils/src/BoostHistogramUtils.cxx +++ b/Common/Utils/src/BoostHistogramUtils.cxx @@ -40,48 +40,6 @@ std::string createErrorMessageFitGaus(o2::utils::FitGausError_t errorcode) return "Gaus fit failed! Unknown error code"; } -boostHisto1d_VarAxis boosthistoFromRoot_1D(TH1D* inHist1D) -{ - // first setup the proper boost histogram - int nBins = inHist1D->GetNbinsX(); - std::vector binEdges; - for (int i = 0; i < nBins + 1; i++) { - binEdges.push_back(inHist1D->GetBinLowEdge(i + 1)); - } - boostHisto1d_VarAxis mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdges)); - - // trasfer the acutal values - for (Int_t x = 1; x < nBins + 1; x++) { - mHisto.at(x - 1) = inHist1D->GetBinContent(x); - } - return mHisto; -} - -boostHisto2d_VarAxis boostHistoFromRoot_2D(TH2D* inHist2D) -{ - // Get Xaxis binning - const int nBinsX = inHist2D->GetNbinsX(); - std::vector binEdgesX; - for (int i = 0; i < nBinsX + 1; i++) { - binEdgesX.push_back(inHist2D->GetXaxis()->GetBinLowEdge(i + 1)); - } - // Get Yaxis binning - const int nBinsY = inHist2D->GetNbinsY(); - std::vector binEdgesY; - for (int i = 0; i < nBinsY + 1; i++) { - binEdgesY.push_back(inHist2D->GetYaxis()->GetBinLowEdge(i + 1)); - } - - boostHisto2d_VarAxis mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesX), boost::histogram::axis::variable<>(binEdgesY)); - - // trasfer the acutal values - for (Int_t x = 1; x < nBinsX + 1; x++) { - for (Int_t y = 1; y < nBinsY + 1; y++) { - mHisto.at(x - 1, y - 1) = inHist2D->GetBinContent(x, y); - } - } - return mHisto; -} /// \brief Printing an error message when then fit returns an invalid result /// \param errorcode Error of the type FitGausError_t, thrown when fit result is invalid. std::string createErrorMessage(o2::utils::FitGausError_t errorcode) diff --git a/Common/Utils/src/CommonUtilsTestLinkDef.h b/Common/Utils/src/CommonUtilsTestLinkDef.h new file mode 100644 index 0000000000000..9ee67f62fd7d0 --- /dev/null +++ b/Common/Utils/src/CommonUtilsTestLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::conf::test::TestParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::test::TestParam> + ; + +#endif diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index e581970fc81fb..8497a485fca39 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -9,11 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//first version 8/2018, Sandro Wenzel +// first version 8/2018, Sandro Wenzel #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/StringUtils.h" #include "CommonUtils/KeyValParam.h" +#include "CommonUtils/ConfigurableParamReaders.h" #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include #include @@ -47,7 +48,6 @@ std::vector* ConfigurableParam::sRegisteredParamClasses = nu boost::property_tree::ptree* ConfigurableParam::sPtree = nullptr; std::map>* ConfigurableParam::sKeyToStorageMap = nullptr; std::map* ConfigurableParam::sValueProvenanceMap = nullptr; -std::string ConfigurableParam::sInputDir = ""; std::string ConfigurableParam::sOutputDir = ""; EnumRegistry* ConfigurableParam::sEnumRegistry = nullptr; @@ -77,6 +77,30 @@ bool keyInTree(boost::property_tree::ptree* pt, const std::string& key) return reply; } +// Convert a type info to the appropiate literal suffix +std::string getLiteralSuffixFromType(const std::type_info& type) +{ + if (type == typeid(float)) { + return "f"; + } + if (type == typeid(long double)) { + return "l"; + } + if (type == typeid(unsigned int)) { + return "u"; + } + if (type == typeid(unsigned long)) { + return "ul"; + } + if (type == typeid(long long)) { + return "ll"; + } + if (type == typeid(unsigned long long)) { + return "ull"; + } + return ""; +} + // ------------------------------------------------------------------ void EnumRegistry::add(const std::string& key, const TDataMember* dm) @@ -122,7 +146,6 @@ std::string EnumRegistry::toString() const out.append("\n"); } - LOG(info) << out; return out; } @@ -194,60 +217,58 @@ void ConfigurableParam::writeINI(std::string const& filename, std::string const& bool ConfigurableParam::configFileExists(std::string const& filepath) { - return std::filesystem::exists(o2::utils::Str::concat_string(sInputDir, filepath)); -} - -// ------------------------------------------------------------------ - -boost::property_tree::ptree ConfigurableParam::readConfigFile(std::string const& filepath) -{ - auto inpfilename = o2::utils::Str::concat_string(sInputDir, filepath); - if (!std::filesystem::exists(inpfilename)) { - LOG(fatal) << inpfilename << " : config file does not exist!"; - } - - boost::property_tree::ptree pt; - - if (boost::iends_with(inpfilename, ".ini")) { - pt = readINI(inpfilename); - } else if (boost::iends_with(inpfilename, ".json")) { - pt = readJSON(inpfilename); - } else { - LOG(fatal) << "Configuration file must have either .ini or .json extension"; - } - - return pt; + return std::filesystem::exists(o2::utils::Str::concat_string(ConfigurableParamReaders::getInputDir(), filepath)); } // ------------------------------------------------------------------ -boost::property_tree::ptree ConfigurableParam::readINI(std::string const& filepath) +void ConfigurableParam::setValue(std::string const& key, std::string const& valuestring) { - boost::property_tree::ptree pt; - try { - boost::property_tree::read_ini(filepath, pt); - } catch (const boost::property_tree::ptree_error& e) { - LOG(fatal) << "Failed to read INI config file " << filepath << " (" << e.what() << ")"; - } catch (...) { - LOG(fatal) << "Unknown error when reading INI config file "; + if (!sIsFullyInitialized) { + initialize(); } - - return pt; -} - -// ------------------------------------------------------------------ - -boost::property_tree::ptree ConfigurableParam::readJSON(std::string const& filepath) -{ - boost::property_tree::ptree pt; - + assert(sPtree); + auto setValueImpl = [&](std::string const& value) { + sPtree->put(key, value); + auto changed = updateThroughStorageMapWithConversion(key, value); + if (changed != EParamUpdateStatus::Failed) { + sValueProvenanceMap->find(key)->second = kRT; // set to runtime + } + }; try { - boost::property_tree::read_json(filepath, pt); - } catch (const boost::property_tree::ptree_error& e) { - LOG(fatal) << "Failed to read JSON config file " << filepath << " (" << e.what() << ")"; + if (sPtree->get_optional(key).is_initialized()) { + try { + // try first setting value without stripping a literal suffix + setValueImpl(valuestring); + } catch (...) { + // try second stripping the expected literal suffix value for fundamental types + auto iter = sKeyToStorageMap->find(key); + if (iter == sKeyToStorageMap->end()) { + std::cerr << "Error in setValue (string) key is not known\n"; + return; + } + const auto expectedSuffix = getLiteralSuffixFromType(iter->second.first); + if (!expectedSuffix.empty()) { + auto valuestringLower = valuestring; + std::transform(valuestring.cbegin(), valuestring.cend(), valuestringLower.begin(), tolower); + if (valuestringLower.ends_with(expectedSuffix)) { + std::string strippedValue = valuestringLower.substr(0, valuestringLower.length() - expectedSuffix.length()); + setValueImpl(strippedValue); + } else { + // check if it has a different suffix and throw + for (const auto& suffix : {"f", "l", "u", "ul", "ll", "ull"}) { + if (valuestringLower.ends_with(suffix) && suffix != expectedSuffix) { + throw std::invalid_argument("Wrong type suffix: expected " + expectedSuffix + " but got " + suffix); + } + } + throw; // just rethrow the original exception + } + } + } + } + } catch (std::exception const& e) { + std::cerr << "Error in setValue (string) " << e.what() << "\n"; } - - return pt; } // ------------------------------------------------------------------ @@ -258,7 +279,7 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const LOG(info) << "ignoring writing of json file " << filename; return; } - initPropertyTree(); // update the boost tree before writing + initPropertyTree(); // update the boost tree before writing auto outfilename = o2::utils::Str::concat_string(sOutputDir, filename); if (!keyOnly.empty()) { // write ini for selected key only try { @@ -409,7 +430,7 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin return; } - boost::property_tree::ptree pt = readConfigFile(cfgfile); + boost::property_tree::ptree pt = ConfigurableParamReaders::readConfigFile(cfgfile); std::vector> keyValPairs; auto request = o2::utils::Str::tokenize(paramsList, ',', true); @@ -480,14 +501,12 @@ void ConfigurableParam::updateFromString(std::string const& configString) std::vector> pairs; for (auto& token : tokens) { - auto keyval = o2::utils::Str::tokenize(token, '='); - if (keyval.size() != 2) { + auto s = token.find('='); + if (s == 0 || s == std::string::npos || s == token.size() - 1) { LOG(fatal) << "Illegal command-line key/value string: " << token; continue; } - - std::pair pair = std::make_pair(keyval[0], o2::utils::Str::trim_copy(keyval[1])); - pairs.push_back(pair); + pairs.emplace_back(token.substr(0, s), token.substr(s + 1, token.size())); } return pairs; @@ -522,7 +541,7 @@ void ConfigurableParam::updateFromString(std::string const& configString) const auto& kv = o2::conf::KeyValParam::Instance(); if (getProvenance("keyval.input_dir") != kCODE) { - sInputDir = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(kv.input_dir)); + ConfigurableParamReaders::setInputDir(o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(kv.input_dir))); } if (getProvenance("keyval.output_dir") != kCODE) { if (kv.output_dir == "/dev/null") { @@ -541,6 +560,8 @@ void ConfigurableParam::setValues(std::vector 0 && (el.at(0) == '[') && (el.at(el.size() - 1) == ']'); }; + bool nonFatal = getenv("ALICEO2_CONFIGURABLEPARAM_WRONGKEYISNONFATAL") != nullptr; + // Take a vector of param key/value pairs // and update the storage map for each of them by calling setValue. // 1. For string/scalar types this is simple. @@ -553,6 +574,10 @@ void ConfigurableParam::setValues(std::vector #include #include +#include #ifdef NDEBUG #undef NDEBUG #endif @@ -35,16 +36,25 @@ using namespace o2::conf; // ---------------------------------------------------------------------- -std::string ParamDataMember::toString(std::string const& prefix, bool showProv) const +std::string ParamDataMember::toString(std::string const& prefix, bool showProv, size_t padding) const { - std::string nil = ""; - + const std::string label = prefix + "." + name + " : " + value; std::ostringstream out; - out << prefix << "." << name << " : " << value; + out << label; if (showProv) { - std::string prov = (provenance.compare("") == 0 ? nil : provenance); - out << "\t\t[ " + prov + " ]"; + std::string prov = (provenance.compare("") == 0 ? "" : provenance); + if (padding) { + size_t len = label.size() - prefix.size() - 5; // 4 four the extra chars + 1 for the maxpad + if (len < padding) { + out << std::string(padding - len, ' '); + } else { + out << ' '; + } + out << "[ " + prov + " ]"; + } else { + out << "\t\t[ " + prov + " ]"; + } } return out.str(); } @@ -182,19 +192,19 @@ std::string asString(TDataMember const& dm, char* pointer) // potentially other cases to be added here LOG(error) << "COULD NOT REPRESENT AS STRING"; - return nullptr; + return std::string(); } // ---------------------------------------------------------------------- std::vector* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj, - std::map const* provmap) + std::map const* provmap, size_t globaloffset) { std::vector* members = new std::vector; - auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) { + auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; const std::string name = getName(dm, index, size); auto value = asString(*dm, pointer); @@ -280,14 +290,14 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt) void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree, std::map>* keytostoragemap, - EnumRegistry* enumRegistry) + EnumRegistry* enumRegistry, size_t globaloffset) { boost::property_tree::ptree localtree; - auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) { + auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; localtree.put(name, asString(*dm, pointer)); auto key = mainkey + "." + name; @@ -308,23 +318,40 @@ void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, voi // ---------------------------------------------------------------------- -void _ParamHelper::printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger) +void _ParamHelper::printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash) { - _ParamHelper::outputMembersImpl(std::cout, mainkey, members, showProv, useLogger); + _ParamHelper::outputMembersImpl(std::cout, mainkey, members, showProv, useLogger, withPadding, showHash); } -void _ParamHelper::outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger) +void _ParamHelper::outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger, bool withPadding, bool showHash) { if (members == nullptr) { return; } + size_t maxpad{0}; + if (withPadding) { + for (auto& member : *members) { + maxpad = std::max(maxpad, member.name.size() + member.value.size()); + } + } + + if (showHash) { + std::string shash = std::format("{:07x}", getHashImpl(mainkey, members)); + shash = shash.substr(0, 7); + if (useLogger) { + LOG(info) << mainkey << " [Hash#" << shash << "]"; + } else { + out << mainkey << " [Hash#" << shash << "]\n"; + } + } + for (auto& member : *members) { if (useLogger) { - LOG(info) << member.toString(mainkey, showProv); + LOG(info) << member.toString(mainkey, showProv, maxpad); } else { - out << member.toString(mainkey, showProv) << "\n"; + out << member.toString(mainkey, showProv, maxpad) << "\n"; } } } @@ -355,14 +382,14 @@ bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes // ---------------------------------------------------------------------- void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // lambda to update the provenance auto updateProv = [&mainkey, name, provmap]() { @@ -402,14 +429,14 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* // ---------------------------------------------------------------------- void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto sync = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // check current provenance auto key = mainkey + "." + name; diff --git a/Common/Utils/src/ConfigurableParamReaders.cxx b/Common/Utils/src/ConfigurableParamReaders.cxx new file mode 100644 index 0000000000000..2f36f8b27f5fa --- /dev/null +++ b/Common/Utils/src/ConfigurableParamReaders.cxx @@ -0,0 +1,76 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamReaders.h" +#include "CommonUtils/StringUtils.h" +#include +#include + +#include +#include +#include + +namespace o2::conf +{ +// ------------------------------------------------------------------ + +boost::property_tree::ptree ConfigurableParamReaders::readINI(std::string const& filepath) +{ + boost::property_tree::ptree pt; + try { + boost::property_tree::read_ini(filepath, pt); + } catch (const boost::property_tree::ptree_error& e) { + LOG(fatal) << "Failed to read INI config file " << filepath << " (" << e.what() << ")"; + } catch (...) { + LOG(fatal) << "Unknown error when reading INI config file "; + } + + return pt; +} + +// ------------------------------------------------------------------ + +boost::property_tree::ptree ConfigurableParamReaders::readJSON(std::string const& filepath) +{ + boost::property_tree::ptree pt; + + try { + boost::property_tree::read_json(filepath, pt); + } catch (const boost::property_tree::ptree_error& e) { + LOG(fatal) << "Failed to read JSON config file " << filepath << " (" << e.what() << ")"; + } + + return pt; +} + +boost::property_tree::ptree ConfigurableParamReaders::readConfigFile(std::string const& filepath) +{ + auto inpfilename = o2::utils::Str::concat_string(sInputDir, filepath); + if (!std::filesystem::exists(inpfilename)) { + LOG(fatal) << inpfilename << " : config file does not exist!"; + } + + boost::property_tree::ptree pt; + + if (boost::iends_with(inpfilename, ".ini")) { + pt = ConfigurableParamReaders::readINI(inpfilename); + } else if (boost::iends_with(inpfilename, ".json")) { + pt = ConfigurableParamReaders::readJSON(inpfilename); + } else { + LOG(fatal) << "Configuration file must have either .ini or .json extension"; + } + + return pt; +} + +std::string ConfigurableParamReaders::sInputDir = ""; + +} // namespace o2::conf diff --git a/Common/Utils/src/ConfigurableParamTest.cxx b/Common/Utils/src/ConfigurableParamTest.cxx new file mode 100644 index 0000000000000..5115a8dfe889d --- /dev/null +++ b/Common/Utils/src/ConfigurableParamTest.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParamTest.h" +O2ParamImpl(o2::conf::test::TestParam); diff --git a/Common/Utils/src/DLLoaderBase.cxx b/Common/Utils/src/DLLoaderBase.cxx new file mode 100644 index 0000000000000..1e4ab97604794 --- /dev/null +++ b/Common/Utils/src/DLLoaderBase.cxx @@ -0,0 +1,12 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/DLLoaderBase.h" diff --git a/Common/Utils/src/DebugStreamer.cxx b/Common/Utils/src/DebugStreamer.cxx index 502c9797924c4..46ff9be83d415 100644 --- a/Common/Utils/src/DebugStreamer.cxx +++ b/Common/Utils/src/DebugStreamer.cxx @@ -55,7 +55,7 @@ void o2::utils::DebugStreamer::flush() } } -bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID) +bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID, const float weight) { const bool isStreamerSet = ((getStreamFlags() & streamFlag) == streamFlag); if (!isStreamerSet) { @@ -65,27 +65,22 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s // check sampling frequency const auto sampling = getSamplingTypeFrequency(streamFlag); if (sampling.first != SamplingTypes::sampleAll) { - // init random number generator for each thread - static thread_local std::mt19937 generator(std::random_device{}()); - std::uniform_real_distribution<> distr(0, 1); - auto sampleTrack = [&]() { if (samplingID == -1) { - LOGP(fatal, "Sampling type sampleID not supported for stream flag {}", streamFlag); + LOGP(fatal, "Sampling type sampleID not supported for stream flag {}", (int)streamFlag); } - std::uniform_real_distribution<> distr(0, 1); // sample on samplingID (e.g. track level) static thread_local std::unordered_map> idMap; // in case of first call samplingID in idMap is 0 and always false and first ID rejected if (idMap[streamFlag].first != samplingID) { - idMap[streamFlag] = std::pair{samplingID, (distr(generator) < sampling.second)}; + idMap[streamFlag] = std::pair{samplingID, (getRandom() < sampling.second)}; } return idMap[streamFlag].second; }; if (sampling.first == SamplingTypes::sampleRandom) { // just sample randomly - return (distr(generator) < sampling.second); + return (getRandom() < sampling.second); } else if (sampling.first == SamplingTypes::sampleID) { return sampleTrack(); } else if (sampling.first == SamplingTypes::sampleIDGlobal) { @@ -104,11 +99,23 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s refIDs[index][samplingID] = storeTrk; return storeTrk; } + } else if (sampling.first == SamplingTypes::sampleWeights) { + // sample with weight + return (weight * getRandom() < sampling.second); } } return true; } +float o2::utils::DebugStreamer::getRandom(float min, float max) +{ + // init random number generator for each thread + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_real_distribution<> distr(min, max); + const float rnd = distr(generator); + return rnd; +} + int o2::utils::DebugStreamer::getIndex(const StreamFlags streamFlag) { // see: https://stackoverflow.com/a/71539401 @@ -145,19 +152,21 @@ int o2::utils::DebugStreamer::getNTrees(const size_t id) const { return isStream void o2::utils::DebugStreamer::mergeTrees(const char* inpFile, const char* outFile, const char* option) { TFile fInp(inpFile, "READ"); - std::unordered_map lists; + std::unordered_map lists; for (TObject* keyAsObj : *fInp.GetListOfKeys()) { const auto key = dynamic_cast(keyAsObj); TTree* tree = (TTree*)fInp.Get(key->GetName()); // perform simple check on the number of entries to merge only TTree with same content (ToDo: Do check on name of branches) const int entries = tree->GetListOfBranches()->GetEntries(); - lists[entries].Add(tree); + const std::string brName = key->GetName(); + const std::string nameBr = brName.substr(0, brName.find_last_of("_")); + lists[nameBr].Add(tree); } TFile fOut(outFile, "RECREATE"); for (auto& list : lists) { auto tree = TTree::MergeTrees(&list.second, option); - fOut.WriteObject(tree, tree->GetName()); + fOut.WriteObject(tree, list.first.data()); } } diff --git a/Common/Utils/src/FileFetcher.cxx b/Common/Utils/src/FileFetcher.cxx index 6492383d836f7..32c51ac704d4b 100644 --- a/Common/Utils/src/FileFetcher.cxx +++ b/Common/Utils/src/FileFetcher.cxx @@ -84,10 +84,17 @@ void FileFetcher::processInput(const std::vector& input) { for (auto inp : input) { o2::utils::Str::trim(inp); - if (fs::is_directory(inp)) { processDirectory(inp); } else if (mSelRegex && !std::regex_match(inp, *mSelRegex.get())) { // provided selector does not match, treat as a txt file with list + // Avoid reading a multigiB data file as a list of inputs + // bringing down the system. + std::filesystem::path p(inp); + if (std::filesystem::file_size(p) > 10000000) { + LOGP(error, "file list {} larger than 10MB. Is this a data file?", inp); + continue; + } + std::ifstream listFile(inp); if (!listFile.good()) { LOGP(error, "file {} pretends to be a list of inputs but does not exist", inp); @@ -97,7 +104,7 @@ void FileFetcher::processInput(const std::vector& input) std::vector newInput; while (getline(listFile, line)) { o2::utils::Str::trim(line); - if (line[0] == '#') { // ignore commented file + if (line[0] == '#' || line.empty()) { // ignore commented file or empty line continue; } newInput.push_back(line); @@ -322,6 +329,7 @@ bool FileFetcher::copyFile(size_t id) bool aliencpMode = false; std::string uuid{}; std::vector logsToClean; + std::string dbgset{}; if (mCopyCmd.find("alien") != std::string::npos) { if (!gGrid && !TGrid::Connect("alien://")) { LOG(error) << "Copy command refers to alien but connection to Grid failed"; @@ -332,15 +340,18 @@ bool FileFetcher::copyFile(size_t id) c = '_'; } } - gSystem->Setenv("ALIENPY_DEBUG", "1"); - logsToClean.push_back(fmt::format("log_alienpy_{}.txt", uuid)); - gSystem->Setenv("ALIENPY_DEBUG_FILE", logsToClean.back().c_str()); - gSystem->Setenv("XRD_LOGLEVEL", "Dump"); - logsToClean.push_back(fmt::format("log_xrd_{}.txt", uuid)); - gSystem->Setenv("XRD_LOGFILE", logsToClean.back().c_str()); + if (!(getenv("ALIENPY_DEBUG") && std::stoi(getenv("ALIENPY_DEBUG")) == 1)) { + logsToClean.push_back(fmt::format("log_alienpy_{}.txt", uuid)); + dbgset += fmt::format("ALIENPY_DEBUG=1 ALIENPY_DEBUG_FILE={} ", logsToClean.back()); + } + if (!(getenv("XRD_LOGLEVEL") && strcmp(getenv("XRD_LOGLEVEL"), "Dump") == 0)) { + logsToClean.push_back(fmt::format("log_xrd_{}.txt", uuid)); + dbgset += fmt::format("XRD_LOGLEVEL=Dump XRD_LOGFILE={} ", logsToClean.back()); + } + LOGP(debug, "debug setting for for {}: {}", mInputFiles[id].getOrigName(), dbgset); } auto realCmd = std::regex_replace(std::regex_replace(mCopyCmd, std::regex(R"(\?src)"), mInputFiles[id].getOrigName()), std::regex(R"(\?dst)"), mInputFiles[id].getLocalName()); - auto fullCmd = fmt::format(R"(sh -c "{}" >> {} 2>&1)", realCmd, mCopyCmdLogFile); + auto fullCmd = fmt::format(R"(sh -c "{}{}" >> {} 2>&1)", dbgset, realCmd, mCopyCmdLogFile); LOG(info) << "Executing " << fullCmd; const auto sysRet = gSystem->Exec(fullCmd.c_str()); if (sysRet != 0) { diff --git a/Common/Utils/src/FileSystemUtils.cxx b/Common/Utils/src/FileSystemUtils.cxx index 09541050325eb..44c0b06b4b03a 100644 --- a/Common/Utils/src/FileSystemUtils.cxx +++ b/Common/Utils/src/FileSystemUtils.cxx @@ -74,6 +74,12 @@ std::string expandShellVarsInFileName(std::string const& input) std::sregex_iterator iter; auto words_end = std::sregex_iterator(); // the end iterator (default) auto words_begin = std::sregex_iterator(input.begin(), input.end(), e); + + // check first of all if there is shell variable inside + if (words_end == words_begin) { + return input; + } + std::string tail; for (auto i = words_begin; i != words_end; ++i) { std::smatch match = *i; @@ -85,8 +91,11 @@ std::string expandShellVarsInFileName(std::string const& input) auto envlookup = getenv(m[0].str().c_str()); if (envlookup) { finalstr += match.prefix().str() + std::string(envlookup); - tail = match.suffix().str(); + } else { + // in case of non existance we keep the env part unreplaced + finalstr += match.prefix().str() + "${" + m[0].str().c_str() + "}"; } + tail = match.suffix().str(); } } finalstr += tail; diff --git a/Common/Utils/src/IRFrameSelector.cxx b/Common/Utils/src/IRFrameSelector.cxx index d5afd45ea96d0..abc0ee1ee6ce3 100644 --- a/Common/Utils/src/IRFrameSelector.cxx +++ b/Common/Utils/src/IRFrameSelector.cxx @@ -167,6 +167,16 @@ size_t IRFrameSelector::loadIRFrames(const std::string& fname) return mOwnList.size(); } +void IRFrameSelector::setOwnList(const std::vector& lst, bool toBeSorted) +{ + clear(); + mOwnList.insert(mOwnList.end(), lst.begin(), lst.end()); + if (toBeSorted) { + std::sort(mOwnList.begin(), mOwnList.end(), [](const auto& a, const auto& b) { return a.getMin() < b.getMin(); }); + } + setSelectedIRFrames(mOwnList, 0, 0, 0, false); +} + void IRFrameSelector::print(bool lst) const { LOGP(info, "Last query stopped at entry {} for IRFrame {}:{}", mLastBoundID, @@ -183,6 +193,8 @@ void IRFrameSelector::clear() { mIsSet = false; mOwnList.clear(); + mLastIRFrameChecked.getMin().clear(); // invalidate + mLastBoundID = -1; mFrames = {}; } @@ -208,7 +220,7 @@ void IRFrameSelector::applyMargins(size_t bwd, size_t fwd, long shift, bool remo if (shiftFwd > 0) { irmax = (o2::InteractionRecord::MaxGlobalBCs - fr.getMax().toLong()) > shiftFwd ? fr.getMax() + shiftFwd : o2::InteractionRecord::getIRMaxBC(); } else { - irmax = fr.getMax().toLong() > -shiftBwd ? fr.getMax() + shiftBwd : o2::InteractionRecord{0, 0}; + irmax = fr.getMax().toLong() > -shiftFwd ? fr.getMax() + shiftFwd : o2::InteractionRecord{0, 0}; } LOGP(debug, "before removerlap: {}:{} -> {}:{}", fr.getMin().toLong(), fr.getMax().toLong(), irmin.toLong(), irmax.toLong()); if (removeOverlaps && lst.size() && lst.back().getMax() >= irmin) { diff --git a/Common/Utils/src/RootSerializableKeyValueStore.cxx b/Common/Utils/src/RootSerializableKeyValueStore.cxx index 7bc424300674e..0e0902f6cc4f2 100644 --- a/Common/Utils/src/RootSerializableKeyValueStore.cxx +++ b/Common/Utils/src/RootSerializableKeyValueStore.cxx @@ -14,11 +14,57 @@ using namespace o2::utils; -void RootSerializableKeyValueStore::print() const +namespace +{ +template +std::string stringFromType(char* buffer) +{ + T value; + std::memcpy(&value, buffer, sizeof(T)); + return std::to_string(value); +} +} // namespace + +void RootSerializableKeyValueStore::print(bool includetypeinfo) const { for (auto& p : mStore) { const auto& key = p.first; const auto info = p.second; - std::cout << "key: " << key << " of-type: " << info.typeinfo_name << "\n"; + auto tinfo = info.typeinfo_name; + + std::string value("unknown-value"); + // let's try to decode the value as a string if we can + if (tinfo == typeid(int).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(unsigned int).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(short).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(unsigned short).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(double).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(float).name()) { + value = stringFromType(info.bufferptr); + } + // let's try to decode the value as a string if we can + else if (tinfo == typeid(std::string).name()) { + value = *(get(key)); + } + std::cout << "key: " << key << " value: " << value; + if (includetypeinfo) { + std::cout << " type: " << info.typeinfo_name; + } + std::cout << "\n"; } } diff --git a/Common/Utils/src/ShmManager.cxx b/Common/Utils/src/ShmManager.cxx index cb3af5dedf380..26b30be062220 100644 --- a/Common/Utils/src/ShmManager.cxx +++ b/Common/Utils/src/ShmManager.cxx @@ -119,8 +119,8 @@ bool ShmManager::createGlobalSegment(int nsegments) return false; } - LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; #ifdef USESHM + LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; // LOG(info) << "SIZEOF ShmMetaInfo " << sizeof(ShmMetaInfo); const auto totalsize = sizeof(ShmMetaInfo) + SHMPOOLSIZE * nsegments; if ((mShmID = shmget(IPC_PRIVATE, totalsize, IPC_CREAT | 0666)) == -1) { diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 8466df31dd57c..687225d069ed2 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -12,7 +12,11 @@ #include "CommonUtils/StringUtils.h" #include #include +#ifndef GPUCA_STANDALONE #include +#include +#endif +#include using namespace o2::utils; @@ -33,13 +37,28 @@ std::vector Str::tokenize(const std::string& src, char delim, bool return tokens; } +// replace all occurencies of from by to, return count +int Str::replaceAll(std::string& s, const std::string& from, const std::string& to) +{ + int count = 0; + size_t pos = 0; + while ((pos = s.find(from, pos)) != std::string::npos) { + s.replace(pos, from.length(), to); + pos += to.length(); // Handles case where 'to' is a substring of 'from' + count++; + } + return count; +} + // generate random string of given lenght, suitable for file names std::string Str::getRandomString(int lenght) { - auto nextAllowed = []() { + int pid = (int)getpid(); + auto nextAllowed = [pid]() { constexpr char chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; constexpr size_t L = sizeof(chars) - 1; - return chars[std::rand() % L]; + int rn = std::rand() | pid; + return chars[rn % L]; }; std::string str(lenght, 0); std::generate_n(str.begin(), lenght, nextAllowed); @@ -61,6 +80,7 @@ std::string Str::getFullPath(const std::string_view p) return std::filesystem::canonical(std::string{p}).string(); } +#ifndef GPUCA_STANDALONE std::string Str::rectifyDirectory(const std::string_view p) { std::string dir(p); @@ -88,6 +108,7 @@ std::string Str::rectifyDirectory(const std::string_view p) } return dir; } +#endif // Create unique non-existing path name starting with prefix. Loose equivalent of boost::filesystem::unique_path() // The prefix can be either existing directory or just a string to add in front of the random part diff --git a/Common/Utils/src/TreeStream.cxx b/Common/Utils/src/TreeStream.cxx index da25f25ad2eb1..cd0641a11d043 100644 --- a/Common/Utils/src/TreeStream.cxx +++ b/Common/Utils/src/TreeStream.cxx @@ -29,8 +29,7 @@ int TreeStream::CheckIn(Char_t type, const void* pointer) // Insert object if (mCurrentIndex >= static_cast(mElements.size())) { - mElements.emplace_back(); - auto& element = mElements.back(); + auto& element = mElements.emplace_back(); element.type = type; TString name = mNextName; if (name.Length()) { @@ -42,6 +41,8 @@ int TreeStream::CheckIn(Char_t type, const void* pointer) } element.name = name.Data(); element.ptr = pointer; + element.arsize = mNextArraySize; + mNextArraySize = 1; // reset } else { auto& element = mElements[mCurrentIndex]; if (element.type != type) { @@ -89,7 +90,13 @@ void TreeStream::BuildTree() } if (element.type > 0) { - TString nameC = TString::Format("%s/%c", name.Data(), element.type); + TString nameC; + if (element.arsize > 1) { + nameC = TString::Format("%s[%d]/%c", name.Data(), element.arsize, + element.type); + } else { + nameC = TString::Format("%s/%c", name.Data(), element.type); + } br = mTree.Branch(name.Data(), const_cast(element.ptr), nameC.Data()); if (entriesFilled) { br->SetAddress(nullptr); @@ -148,28 +155,43 @@ TreeStream& TreeStream::Endl() TreeStream& TreeStream::operator<<(const Char_t* name) { // Stream the branch name - // if (name[0] == '\n') { return Endl(); } - // + // if tree was already defined ignore if (mTree.GetEntries() > 0) { return *this; } + + int arsize = 1; + // check branch name if tree was not - // Int_t last = 0; for (last = 0;; last++) { if (name[last] == 0) { break; } } - if (last > 0 && name[last - 1] == '=') { mNextName = name; - mNextName[last - 1] = 0; + mNextName[last - 1] = 0; // remove '=' from string mNextNameCounter = 0; + + TString inName{name}; + auto brkStaPos = inName.Index('['); + + if (brkStaPos != kNPOS) { + auto brkEndPos = inName.Index(']'); + if (brkEndPos != kNPOS && brkEndPos > brkStaPos + 1) { + TString size = inName(brkStaPos + 1, brkEndPos - brkStaPos - 1); + arsize = size.Atoi(); + mNextName = inName(0, brkStaPos); // use parsed name + } + } } + + mNextArraySize = arsize; + return *this; } diff --git a/Common/Utils/src/TreeStreamRedirector.cxx b/Common/Utils/src/TreeStreamRedirector.cxx index 86e0eb5a37552..06fb3d65678c4 100644 --- a/Common/Utils/src/TreeStreamRedirector.cxx +++ b/Common/Utils/src/TreeStreamRedirector.cxx @@ -111,11 +111,13 @@ TreeStream& TreeStreamRedirector::operator<<(const char* name) void TreeStreamRedirector::Close() { // flush and close - + if (!mDirectory) { + return; + } TDirectory* backup = gDirectory; mDirectory->cd(); for (auto& layout : mDataLayouts) { - layout->getTree().Write(layout->getName()); + layout->getTree().Write(layout->getName(), TObject::kOverwrite); } mDataLayouts.clear(); if (backup) { diff --git a/Common/Utils/src/fpu.cxx b/Common/Utils/src/fpu.cxx new file mode 100644 index 0000000000000..505935e15c36e --- /dev/null +++ b/Common/Utils/src/fpu.cxx @@ -0,0 +1,44 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// To enable check for problematic LIBXML version uncomment this +// #define _PROTECT_LIBXML_ + +#ifdef __linux +#ifdef _PROTECT_LIBXML_ +#include +#if LIBXML_VERSION > 20912 +#define _DUMMY_FEE_TRAP_ +#endif // LIBXML_VERSION > 20912 +#endif // _PROTECT_LIBXML_ +#else // __linux +#define _DUMMY_FEE_TRAP_ +#endif // __linux + +#ifdef _DUMMY_FEE_TRAP_ +void trapfpe() {} +#else // _DUMMY_FEE_TRAP_ +#define _GNU_SOURCE 1 +#include +#include +static void __attribute__((constructor)) trapfpe() +{ + // allows to disable set of particular FE's by setting corresponding bit of the O2_DISABLE_FPE_TRAP, + // i.e. to enable only FE_DIVBYZERO use O2_DISABLE_FPE_TRAP=9 + // const char* ev = std::getenv("O2_DISABLE_FPE_TRAP"); + // int enabledFE = (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) & ~(ev ? atoi(ev) : 0); + const char* ev = std::getenv("O2_ENABLE_FPE_TRAP"); + int enabledFE = (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW) & (ev ? atoi(ev) : 0); + if (enabledFE) { + feenableexcept(enabledFE); + } +} +#endif // _DUMMY_FEE_TRAP_ diff --git a/Common/Utils/test/testBoostSerializer.cxx b/Common/Utils/test/testBoostSerializer.cxx deleted file mode 100644 index 520bc7dda8b08..0000000000000 --- a/Common/Utils/test/testBoostSerializer.cxx +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @author Gabriele Gaetano Fronzé - -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN - -#include -#include -#include -#include -#include "CommonUtils/BoostSerializer.h" -#include - -using namespace o2::utils; - -struct TestCluster { - uint8_t deId; ///< Detection element ID - float xCoor; ///< Local x coordinate - float yCoor; ///< Local y coordinate - float sigmaX2; ///< Square of dispersion along x - float sigmaY2; ///< Square of dispersion along y - - friend class boost::serialization::access; - - /// Serializes the struct - template - void serialize(Archive& ar, const unsigned int version) - { - ar& deId& xCoor& yCoor& sigmaX2& sigmaY2; - } -}; - -BOOST_AUTO_TEST_SUITE(testDPLSerializer) - -// BOOST_AUTO_TEST_CASE(testTrivialTypeVect) -// { -// using contType = std::vector; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// BOOST_TEST(test == inputV2[i]); -// i++; -// } -// } - -// BOOST_AUTO_TEST_CASE(testTrivialTypeArray) -// { -// using contType = std::array; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// BOOST_TEST(test == inputV2[i]); -// i++; -// } -// } - -// BOOST_AUTO_TEST_CASE(testTrivialTypeList) -// { -// using contType = std::list; - -// contType inputV{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - -// auto msgStr = BoostSerialize(inputV).str(); -// auto inputV2 = BoostDeserialize(msgStr); - -// BOOST_TEST(inputV.size() == inputV2.size()); - -// size_t i = 0; -// for (auto const& test : inputV) { -// auto value = std::next(std::begin(inputV2), i).operator*(); -// BOOST_TEST(test == value); -// i++; -// } -// } - -BOOST_AUTO_TEST_CASE(testBoostSerialisedType) -{ - using contType = std::vector; - - contType inputV; - - for (size_t i = 0; i < 17; i++) { - float iFloat = (float)i; - inputV.emplace_back(TestCluster{(uint8_t)i, 0.3f * iFloat, 0.5f * iFloat, 0.7f / iFloat, 0.9f / iFloat}); - } - - auto msgStr = BoostSerialize(inputV).str(); - auto inputV2 = BoostDeserialize(msgStr); - - BOOST_TEST(inputV.size() == inputV2.size()); - - size_t i = 0; - for (auto const& test : inputV) { - BOOST_TEST(test.deId == inputV2[i].deId); - BOOST_TEST(test.xCoor == inputV2[i].xCoor); - BOOST_TEST(test.yCoor == inputV2[i].yCoor); - BOOST_TEST(test.sigmaX2 == inputV2[i].sigmaX2); - BOOST_TEST(test.sigmaY2 == inputV2[i].sigmaY2); - i++; - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/Common/Utils/test/testConfigurableParam.cxx b/Common/Utils/test/testConfigurableParam.cxx new file mode 100644 index 0000000000000..3ef177aaca3fe --- /dev/null +++ b/Common/Utils/test/testConfigurableParam.cxx @@ -0,0 +1,145 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test ConfigurableParams +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include + +#include "CommonUtils/ConfigurableParamTest.h" + +using namespace o2::conf; +using namespace o2::conf::test; + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Basic) +{ + // Tests the default parameters and also getter helpers. + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(param.iValue, 42); + BOOST_CHECK_EQUAL(param.dValue, 3.14); + BOOST_CHECK_EQUAL(param.bValue, true); + BOOST_CHECK_EQUAL(param.sValue, "default"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 2); + + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.iValue"), 42); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.dValue"), 3.14); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.bValue"), true); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.sValue"), "default"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_Fundamental) +{ + // tests runtime setting and getting for fundamental types + ConfigurableParam::setValue("TestParam.iValue", "100"); + ConfigurableParam::setValue("TestParam.dValue", "2.718"); + ConfigurableParam::setValue("TestParam.bValue", "0"); + ConfigurableParam::setValue("TestParam.sValue", "modified"); + ConfigurableParam::setValue("TestParam.eValue", "0"); + + auto& param = TestParam::Instance(); + param.printKeyValues(); + BOOST_CHECK_EQUAL(param.iValue, 100); + BOOST_CHECK_EQUAL(param.dValue, 2.718); + BOOST_CHECK_EQUAL(param.bValue, false); + BOOST_CHECK_EQUAL(param.sValue, "modified"); + BOOST_CHECK_EQUAL(static_cast(param.eValue), 0); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_CArray) +{ + // tests setting and getting for a c-style array type + auto& param = TestParam::Instance(); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[0]"), 0); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 1); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[2]"), 2); + + ConfigurableParam::setValue("TestParam.caValue[1]", "99"); + BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs("TestParam.caValue[1]"), 99); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Provenance) +{ + // tests correct setting of provenance + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kCODE); + ConfigurableParam::setValue("TestParam.iValueProvenanceTest", "123"); + BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kRT); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Ini) +{ + // test for ini file serialization + const std::string testFileName = "test_config.ini"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::writeINI(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json) +{ + // test for json file serialization + const std::string testFileName = "test_config.json"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + ConfigurableParam::writeJSON(testFileName); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::updateFromFile(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT) +{ + // test for root file serialization + const std::string testFileName = "test_config.root"; + auto iValueBefore = TestParam::Instance().iValue; + auto sValueBefore = TestParam::Instance().sValue; + TFile* testFile = TFile::Open(testFileName.c_str(), "RECREATE"); + TestParam::Instance().serializeTo(testFile); + testFile->Close(); + ConfigurableParam::setValue("TestParam.iValue", "999"); + ConfigurableParam::setValue("TestParam.sValue", testFileName); + ConfigurableParam::fromCCDB(testFileName); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore); + std::remove(testFileName.c_str()); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_Cli) +{ + // test setting values from as a cli arg string + ConfigurableParam::updateFromString("TestParam.iValue=55;TestParam.sValue=cli"); + BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 55); + BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "cli"); +} + +BOOST_AUTO_TEST_CASE(ConfigurableParam_LiteralSuffix) +{ + // test setting values with the correct literal suffix + ConfigurableParam::updateFromString("TestParam.fValue=42.f"); + BOOST_CHECK_EQUAL(TestParam::Instance().fValue, 42.f); + + ConfigurableParam::setValue("TestParam.ullValue", "999ull"); + BOOST_CHECK_EQUAL(TestParam::Instance().ullValue, 999ULL); + // check using wrong literal suffix fails, prints error to std + ConfigurableParam::setValue("TestParam.ullValue", "888u"); + BOOST_CHECK_NE(TestParam::Instance().ullValue, 888); +} diff --git a/Common/Utils/test/testEnumFlags.cxx b/Common/Utils/test/testEnumFlags.cxx new file mode 100644 index 0000000000000..9101ffb97fdfe --- /dev/null +++ b/Common/Utils/test/testEnumFlags.cxx @@ -0,0 +1,653 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test Flags +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include + +#include +#include + +#include "CommonUtils/EnumFlags.h" + +// Example enum to use with EnumFlags +enum class TestEnum : uint8_t { + Bit1 = 0, + Bit2, + Bit3, + Bit4, + Bit5VeryLongName, +}; + +// Very long enum +// to test that it works beyond 32 bits upto 64 bits +#define ENUM_BIT_NAME(n) Bit##n +#define ENUM_BIT_NAME_EXPAND(n) ENUM_BIT_NAME(n) +#define ENUM_BIT(z, n, _) ENUM_BIT_NAME_EXPAND(BOOST_PP_INC(n)) = (n), +enum class TestEnumLong : uint64_t { + BOOST_PP_REPEAT(64, ENUM_BIT, _) +}; +#undef ENUM_BIT +#undef ENUM_BIT_NAME +#undef ENUM_BIT_NAME_EXPAND + +BOOST_AUTO_TEST_CASE(Flags_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test default initialization + EFlags flags; + BOOST_TEST(flags.None == 0); + BOOST_TEST(flags.All == 31); + BOOST_TEST(flags.value() == 0); + BOOST_TEST(!flags.any()); + + // Test initialization with a single flag + EFlags flag1(TestEnum::Bit1); + BOOST_TEST(flag1.test(TestEnum::Bit1)); + BOOST_TEST(!flag1.test(TestEnum::Bit2)); + BOOST_TEST(flag1.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test initialization with initializer list + EFlags multipleFlags({TestEnum::Bit1, TestEnum::Bit3}); + BOOST_TEST(multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit2)); + BOOST_TEST(multipleFlags.any()); + + // Test reset + multipleFlags.reset(TestEnum::Bit1); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit3)); + multipleFlags.reset(); + BOOST_TEST(!multipleFlags.any()); + + // Test multiset + multipleFlags.reset(); + multipleFlags.set(TestEnum::Bit2, TestEnum::Bit4); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit1)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit2)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit3)); + BOOST_TEST(multipleFlags.test(TestEnum::Bit4)); + BOOST_TEST(!multipleFlags.test(TestEnum::Bit5VeryLongName)); + + // Test operator| + EFlags combinedFlags = flag1 | EFlags(TestEnum::Bit2); + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + combinedFlags |= TestEnum::Bit5VeryLongName; + BOOST_TEST(combinedFlags.test(TestEnum::Bit5VeryLongName)); + + // Test operator[] + BOOST_TEST(combinedFlags[TestEnum::Bit1]); + BOOST_TEST(combinedFlags[TestEnum::Bit2]); + BOOST_TEST(!combinedFlags[TestEnum::Bit3]); + + // Test operator|= + combinedFlags |= TestEnum::Bit3; + BOOST_TEST(combinedFlags.test(TestEnum::Bit3)); + + // Test operator& + EFlags intersection = combinedFlags & TestEnum::Bit1; + BOOST_TEST(intersection.test(TestEnum::Bit1)); + BOOST_TEST(!intersection.test(TestEnum::Bit2)); + BOOST_TEST(intersection.value() == (1 << static_cast(TestEnum::Bit1))); + + // Test operator&= + combinedFlags &= TestEnum::Bit1; + BOOST_TEST(combinedFlags.test(TestEnum::Bit1)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit2)); + BOOST_TEST(!combinedFlags.test(TestEnum::Bit3)); + + // Test operator~ (complement) + EFlags complement = ~EFlags(TestEnum::Bit1); + BOOST_TEST(!complement.test(TestEnum::Bit1)); + BOOST_TEST(complement.test(TestEnum::Bit2)); + BOOST_TEST(complement.test(TestEnum::Bit3)); + + // Test string() method + { + std::string flagString = flag1.string(); + BOOST_TEST(flagString.back() == '1'); // Ensure the least significant bit is set for flag1 + } + + // Test set with binary string + { + std::string binaryStr = "101"; + flags.set(binaryStr, 2); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(!flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.test(TestEnum::Bit3)); + } + + // Test invalid binary string in set + BOOST_CHECK_THROW(flags.set(std::string("invalid"), 2), std::invalid_argument); + + // Test range validation in set + BOOST_CHECK_THROW(flags.set(std::string("100000000"), 2), std::out_of_range); + + { // Test that return lists are sensible + const auto n = flags.getNames(); + const auto v = flags.getValues(); + BOOST_CHECK(n.size() == v.size()); + } + + { // print test + std::cout << flags; + } + + // Test flag tokenization and parsing + { + { // only one scoped flag + std::string str = "TestEnum::Bit2"; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(flags.none_of(TestEnum::Bit1, TestEnum::Bit3, TestEnum::Bit4)); + } + + { // test with ws-triming and scope mixing + std::string str = "Bit4|TestEnum::Bit2 | Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // test with , delimiter + std::string str = "Bit4,TestEnum::Bit2 , Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // test with ; delimiter + std::string str = "Bit4;TestEnum::Bit2 ; Bit1 "; + flags.set(str); + BOOST_TEST(flags.test(TestEnum::Bit1)); + BOOST_TEST(flags.test(TestEnum::Bit2)); + BOOST_TEST(!flags.test(TestEnum::Bit3)); + BOOST_TEST(flags.test(TestEnum::Bit4)); + } + + { // throw test with mixed delimiter + std::string str = "Bit4|TestEnum::Bit2 , Bit1 "; + BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); + } + + { // test throw + std::string str = "Invalid"; + BOOST_CHECK_THROW(flags.set(str), std::invalid_argument); + } + } + + // Test all_of and none_of + { + EFlags allFlags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}); + BOOST_TEST(allFlags.all_of(TestEnum::Bit1, TestEnum::Bit2)); + BOOST_TEST(!allFlags.all_of(TestEnum::Bit4)); + BOOST_TEST(allFlags.none_of(TestEnum::Bit4)); + } + + // Test toggle + { + EFlags toggleFlags; + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(toggleFlags.test(TestEnum::Bit4)); + toggleFlags.toggle(TestEnum::Bit4); + BOOST_TEST(!toggleFlags.test(TestEnum::Bit4)); + } + + // Create a flag set and serialize it + { + EFlags serializedFlags{TestEnum::Bit1, TestEnum::Bit3}; + std::string serialized = serializedFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "5"); // 5 in binary is 0101, meaning Bit1 and Bit3 are set. + + // Deserialize back into a flag set + EFlags deserializedFlags; + deserializedFlags.deserialize(serialized); + BOOST_CHECK(deserializedFlags == serializedFlags); // Ensure the deserialized flags match the original + } + + // Test with an empty flag set + { + EFlags emptyFlags; + std::string serialized = emptyFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, "0"); + + EFlags deserialized; + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == emptyFlags); + + // Test with all flags set + EFlags allFlags(EFlags::All); + serialized = allFlags.serialize(); + BOOST_CHECK_EQUAL(serialized, std::to_string(EFlags::All)); + + deserialized.deserialize(serialized); + BOOST_CHECK(deserialized == allFlags); + } + + // check throw deserializng out of range + { + EFlags flag; + std::string str = "999999"; + BOOST_CHECK_THROW(flag.deserialize(str), std::out_of_range); + } + + // Create two flag sets + { + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2}; + EFlags flags2{TestEnum::Bit3, TestEnum::Bit4}; + + // Perform a union operation + EFlags unionFlags = flags1.union_with(flags2); + BOOST_CHECK(unionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(unionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(unionFlags.value(), 15); // 1111 in binary + } + + // Create two overlapping flag sets + { + EFlags flags3{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags4{TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}; + + // test xor + auto flagsXOR = flags3 ^ flags4; + BOOST_CHECK(flagsXOR.test(TestEnum::Bit1, TestEnum::Bit4)); + + // test and + auto flagsAND = flags3 & flags4; + BOOST_CHECK(flagsAND.test(TestEnum::Bit2, TestEnum::Bit3)); + + // Perform an intersection operation + EFlags intersectionFlags = flags3.intersection_with(flags4); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit2)); + BOOST_CHECK(intersectionFlags.test(TestEnum::Bit3)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit1)); + BOOST_CHECK(!intersectionFlags.test(TestEnum::Bit4)); + BOOST_CHECK_EQUAL(intersectionFlags.value(), 6); // 0110 in binary + } + + { + // Check special flag names. + EFlags flag("all"); + BOOST_CHECK(flag.all()); + flag.set("none"); + BOOST_CHECK(!flag.any()); + } + + { + // Create two flag sets + EFlags flags1{TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}; + EFlags flags2{TestEnum::Bit2, TestEnum::Bit3}; + + // Check containment + BOOST_CHECK(flags1.contains(flags2)); // flags1 contains all flags in flags2 + BOOST_CHECK(!flags2.contains(flags1)); // flags2 does not contain all flags in flags1 + + // Test with disjoint sets + EFlags flags3{TestEnum::Bit4}; + BOOST_CHECK(!flags1.contains(flags3)); // flags1 does not contain flags3 + } + + { + // Test compilation using an enum with more than 32 bits + // Also tests space delimiter and construction from string. + o2::utils::EnumFlags test("Bit32 Bit34"); + BOOST_CHECK(test.test(TestEnumLong::Bit32, TestEnumLong::Bit34)); + BOOST_CHECK(!test.test(TestEnumLong::Bit1, TestEnumLong::Bit23)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_case_insensitive_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test case-insensitive flag names + { + EFlags flags("bit1"); // lowercase + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(!flags.test(TestEnum::Bit2)); + } + + { + EFlags flags("BIT2"); // uppercase + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(!flags.test(TestEnum::Bit1)); + } + + { + EFlags flags("BiT3"); // mixed case + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } + + { + EFlags flags("bit1|BIT2|BiT3"); // mixed case with delimiter + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } + + // Test special keywords case-insensitive + { + EFlags flags("ALL"); + BOOST_CHECK(flags.all()); + } + + { + EFlags flags("None"); + BOOST_CHECK(!flags.any()); + } +} + +BOOST_AUTO_TEST_CASE(Flags_error_recovery_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test that previous state is restored on exception + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + auto previousValue = flags.value(); + + // Try to set with invalid string + BOOST_CHECK_THROW(flags.set("InvalidFlag"), std::invalid_argument); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + { + EFlags flags({TestEnum::Bit3, TestEnum::Bit4}); + auto previousValue = flags.value(); + + // Try to set with out-of-range value + BOOST_CHECK_THROW(flags.set("999999", 10), std::out_of_range); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + } + + { + EFlags flags(TestEnum::Bit5VeryLongName); + auto previousValue = flags.value(); + + // Try to set with invalid binary string + BOOST_CHECK_THROW(flags.set("10102", 2), std::invalid_argument); + + // Verify state was restored + BOOST_CHECK_EQUAL(flags.value(), previousValue); + BOOST_CHECK(flags.test(TestEnum::Bit5VeryLongName)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_whitespace_handling_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test leading/trailing whitespace + { + EFlags flags(" Bit1 "); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + } + + { + EFlags flags(" Bit1 | Bit2 "); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + // Test excessive whitespace between flags + { + EFlags flags("Bit1 | Bit3"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(!flags.test(TestEnum::Bit2)); + } + + // Test tabs and other whitespace (should work with space delimiter) + { + EFlags flags("Bit1 Bit2 Bit3"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_count_bits_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test counting set bits + { + EFlags flags; + BOOST_CHECK_EQUAL(flags.count(), 0); + } + + { + EFlags flags(TestEnum::Bit1); + BOOST_CHECK_EQUAL(flags.count(), 1); + } + + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + BOOST_CHECK_EQUAL(flags.count(), 2); + } + + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3, TestEnum::Bit4}); + BOOST_CHECK_EQUAL(flags.count(), 4); + } + + { + EFlags flags(EFlags::All); + BOOST_CHECK_EQUAL(flags.count(), 5); // TestEnum has 5 members + } + + // Test count after operations + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2, TestEnum::Bit3}); + BOOST_CHECK_EQUAL(flags.count(), 3); + + flags.reset(TestEnum::Bit2); + BOOST_CHECK_EQUAL(flags.count(), 2); + + flags.set(TestEnum::Bit4); + BOOST_CHECK_EQUAL(flags.count(), 3); + + flags.toggle(TestEnum::Bit1); + BOOST_CHECK_EQUAL(flags.count(), 2); + } +} + +BOOST_AUTO_TEST_CASE(Flags_mixed_delimiter_validation_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test that mixed delimiters throw an error + { + BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1;Bit2|Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1,Bit2;Bit3"), std::invalid_argument); + } + + { + BOOST_CHECK_THROW(EFlags("Bit1|Bit2,Bit3;Bit4"), std::invalid_argument); + } + + // Test that single delimiter types work + { + EFlags flags1("Bit1|Bit2|Bit3"); + BOOST_CHECK_EQUAL(flags1.count(), 3); + } + + { + EFlags flags2("Bit1,Bit2,Bit3"); + BOOST_CHECK_EQUAL(flags2.count(), 3); + } + + { + EFlags flags3("Bit1;Bit2;Bit3"); + BOOST_CHECK_EQUAL(flags3.count(), 3); + } +} + +BOOST_AUTO_TEST_CASE(Flags_empty_and_edge_cases_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test empty string + { + EFlags flags({TestEnum::Bit1, TestEnum::Bit2}); + flags.set(""); // Should be no-op + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } + + // Test with only whitespace + { + EFlags flags({TestEnum::Bit1}); + flags.set(" "); // Should result in empty after tokenization + // Depending on implementation, this might clear or throw + // Adjust expectation based on actual behavior + } + + // Test duplicate flags (should work, setting same bit twice is idempotent) + { + EFlags flags("Bit1|Bit1|Bit1"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK_EQUAL(flags.count(), 1); + } + + // Test scoped and unscoped mixed + { + EFlags flags("Bit1|TestEnum::Bit2"); + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + } +} + +BOOST_AUTO_TEST_CASE(Flags_binary_decimal_parsing_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test binary parsing + { + EFlags flags("101", 2); + BOOST_CHECK(flags.test(TestEnum::Bit1)); // bit 0 + BOOST_CHECK(!flags.test(TestEnum::Bit2)); // bit 1 + BOOST_CHECK(flags.test(TestEnum::Bit3)); // bit 2 + } + + // Test decimal parsing + { + EFlags flags("7", 10); // 7 = 0b111 + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(!flags.test(TestEnum::Bit4)); + } + + // Test hexadecimal parsing + { + EFlags flags("F", 16); // 15 = 0b1111 + BOOST_CHECK(flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + BOOST_CHECK(!flags.test(TestEnum::Bit5VeryLongName)); + } + + // Test hexadecimal with 0x prefix + { + EFlags flags("0xA", 16); // 10 = 0b1010 + BOOST_CHECK(!flags.test(TestEnum::Bit1)); + BOOST_CHECK(flags.test(TestEnum::Bit2)); + BOOST_CHECK(!flags.test(TestEnum::Bit3)); + BOOST_CHECK(flags.test(TestEnum::Bit4)); + } + + // Test hexadecimal with 0X prefix (uppercase) + { + EFlags flags("0X1F", 16); // 31 = all 5 bits + BOOST_CHECK(flags.all()); + } + + // Test lowercase hex digits + { + EFlags flags("0xa", 16); + BOOST_CHECK_EQUAL(flags.value(), 10); + } + + // Test thros + { + BOOST_CHECK_THROW(EFlags("0xAbCd", 16), std::out_of_range); + } + + // Test invalid binary string (contains 2) + { + BOOST_CHECK_THROW(EFlags("1012", 2), std::invalid_argument); + } + + // Test out of range for base + { + BOOST_CHECK_THROW(EFlags("100000", 2), std::out_of_range); + } +} + +BOOST_AUTO_TEST_CASE(Flags_operator_bool_test) +{ + using EFlags = o2::utils::EnumFlags; + + // Test explicit bool conversion + { + EFlags empty; + BOOST_CHECK(!static_cast(empty)); + } + + { + EFlags withFlag(TestEnum::Bit1); + BOOST_CHECK(static_cast(withFlag)); + } + + // Test in conditional + { + EFlags flags; + if (flags) { + BOOST_FAIL("Empty flags should be false"); + } + + flags.set(TestEnum::Bit1); + if (!flags) { + BOOST_FAIL("Non-empty flags should be true"); + } + } +} diff --git a/Common/Utils/test/testMemFileHelper.cxx b/Common/Utils/test/testMemFileHelper.cxx index 2a55d67b9884a..79fa7e86f96e9 100644 --- a/Common/Utils/test/testMemFileHelper.cxx +++ b/Common/Utils/test/testMemFileHelper.cxx @@ -40,3 +40,35 @@ BOOST_AUTO_TEST_CASE(test_memfile_helper) BOOST_CHECK(rvec); BOOST_CHECK(*rvec == vec); } + +#include "CommonUtils/FileSystemUtils.h" +#include + +BOOST_AUTO_TEST_CASE(test_expandenv) +{ + { + std::string noenv("simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(noenv); + BOOST_CHECK(expandedFileName.size() > 0); + BOOST_CHECK_EQUAL(expandedFileName, noenv); + } + + { + std::string withenv("${PWD}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK(expandedFileName.size() > 0); + } + + { + setenv("FOO_123", "BAR", 0); + std::string withenv("/tmp/${FOO_123}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK_EQUAL(expandedFileName, "/tmp/BAR/simple_file.root"); + } + + { // what if the variable doesn't exist --> should return unmodified string + std::string withenv("/tmp/${FOO_DOESNOTEXIST}/simple_file.root"); + auto expandedFileName = o2::utils::expandShellVarsInFileName(withenv); + BOOST_CHECK_EQUAL(expandedFileName, withenv); + } +} \ No newline at end of file diff --git a/Common/Utils/test/testTreeStream.cxx b/Common/Utils/test/testTreeStream.cxx index 7ff6f165a1d37..2491fea7f6efd 100644 --- a/Common/Utils/test/testTreeStream.cxx +++ b/Common/Utils/test/testTreeStream.cxx @@ -53,12 +53,28 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) tstStream << "TrackTreeR" << "id=" << i << "x=" << x << "track=" << trc << "\n"; } + + // test for c-arrays + int iArray[6] = {1, 2, 3, 4, 5, 6}; + float fArray[6] = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f}; + for (int i{0}; i < nit; ++i) { + for (int j{0}; j < 6; ++j) { + iArray[j] += i; + fArray[j] += (float)i; + } + tstStream << "ArrayTree" + << "id=" << i + << "iArray[6]=" << iArray + << "fArray[6]=" << fArray + << "\n"; + } + // on destruction of tstTreem the trees will be stored, but we can also force it by tstStream.Close(); } // - LOG(info) << "Testing reading back tree maid by the TreeStream "; - // read back tracks + LOG(info) << "Testing reading back tree made by the TreeStream "; + // read back tracks and arrays { TFile inpf(outFName.data()); BOOST_CHECK(!inpf.IsZombie()); @@ -80,6 +96,27 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) trc->printParam(); BOOST_CHECK(std::abs(x - trc->getX()) < 1e-4); } + + // check arrays + tree = (TTree*)inpf.GetObjectChecked("ArrayTree", "TTree"); + BOOST_CHECK(tree); + nent = tree->GetEntries(); + BOOST_CHECK(nent == nit); + int iArray[6]; + float fArray[6]; + BOOST_CHECK(!tree->SetBranchAddress("id", &id)); + BOOST_CHECK(!tree->SetBranchAddress("iArray", iArray)); + BOOST_CHECK(!tree->SetBranchAddress("fArray", fArray)); + for (int i = 0; i < nit; i++) { + BOOST_CHECK(tree->GetEntry(i) > 0); + BOOST_CHECK(id == i); + for (int j = 0; j < 6; j++) { + BOOST_CHECK(iArray[j] == (1 + j + i * (i + 1) / 2)); + } + for (int j = 0; j < 6; j++) { + BOOST_CHECK_CLOSE(fArray[j], (1.f + j + i * (i + 1) / 2.f + 0.1 * (j + 1)), 1e-5); + } + } } LOG(info) << "Testing loading tree via RootChain"; @@ -104,7 +141,6 @@ BOOST_AUTO_TEST_CASE(TreeStream_test) nit = 1000; BOOST_CHECK(UnitTestSparse(0.5, nit)); BOOST_CHECK(UnitTestSparse(0.1, nit)); - // } //_________________________________________________ diff --git a/DEBUGGING.md b/DEBUGGING.md index cda0d2a94ed81..c782b99987ef9 100644 --- a/DEBUGGING.md +++ b/DEBUGGING.md @@ -1,3 +1,7 @@ + + # How do I run in debug mode? By default, O2 builds with optimizations (`-O2`) turned on, while leaving debug symbols available. diff --git a/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h index 975279b7dadfd..2f8a88072a43d 100644 --- a/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h +++ b/DataFormats/Detectors/CPV/include/DataFormatsCPV/RawFormats.h @@ -115,20 +115,21 @@ class CpvTrailer } } CpvTrailer(unsigned short wordCounter, uint16_t bunchCrossing, bool isAllDataSent) - { // writing - mBytes[0] = bunchCrossing & 0x00ff; // bits 11 - 0 bunch crossing - mBytes[1] = ((bunchCrossing & 0x0f00) >> 8) // bits 11 - 0 bunch crossing - + ((wordCounter & 0x0f) << 4); // bits 20 - 12 wordCounter - mBytes[2] = (wordCounter & 0b111110000) >> 4; // bits 20 - 12 wordCounter + { // writing + mBytes[0] = bunchCrossing & 0x00ff; // bits 11 - 0 bunch crossing + mBytes[1] = ((bunchCrossing & 0x0f00) >> 8) // bits 11 - 0 bunch crossing + + ((wordCounter & 0x0f) << 4); // bits 20 - 12 wordCounter + mBytes[2] = (wordCounter & 0b1111110000) >> 4; // bits 21 - 12 wordCounter + for (int i = 3; i < 8; i++) { - mBytes[i] = 0; // bits 70 - 21 reserved + mBytes[i] = 0; // bits 70 - 22 reserved } mBytes[8] = isAllDataSent * 0b10000000; // bit 71 all data is sent for current trigger mBytes[9] = char(0xf0); // word ID of cpv trailer } ~CpvTrailer() = default; bool isOK() const { return (mBytes[9] == 0xf0); } - uint16_t wordCounter() const { return (mBytes[1] >> 4) + ((mBytes[2] & 0b00011111) << 4); } + uint16_t wordCounter() const { return (mBytes[1] >> 4) + ((mBytes[2] & 0b00111111) << 4); } bool isAllDataSent() const { return (mBytes[8] & 0b10000000); } uint16_t bc() const { return mBytes[0] + ((mBytes[1] & 0x0f) << 8); } diff --git a/DataFormats/Detectors/CTP/CMakeLists.txt b/DataFormats/Detectors/CTP/CMakeLists.txt index c9e8a371e4eb1..37c5af7b07b6e 100644 --- a/DataFormats/Detectors/CTP/CMakeLists.txt +++ b/DataFormats/Detectors/CTP/CMakeLists.txt @@ -8,17 +8,19 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. - o2_add_library(DataFormatsCTP SOURCES src/Digits.cxx src/Configuration.cxx src/Scalers.cxx src/CTF.cxx src/TriggerOffsetsParam.cxx + src/LumiInfo.cxx + src/CTPRateFetcher.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers - O2::SimulationDataFormat O2::CommonUtils + O2::DetectorsCommonDataFormats + O2::DataFormatsParameters O2::CommonConstants) o2_target_root_dictionary(DataFormatsCTP HEADERS include/DataFormatsCTP/Digits.h @@ -26,5 +28,6 @@ o2_target_root_dictionary(DataFormatsCTP include/DataFormatsCTP/Configuration.h include/DataFormatsCTP/Scalers.h include/DataFormatsCTP/LumiInfo.h + include/DataFormatsCTP/CTPRateFetcher.h include/DataFormatsCTP/TriggerOffsetsParam.h) diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h index 8481db8f6d7e5..3635b8ede44db 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h @@ -29,13 +29,16 @@ namespace ctp struct CTFHeader : public o2::ctf::CTFDictHeader { uint64_t lumiCounts = 0; /// FT0 Luminosity counts moving average over lumiNHBFs orbits uint64_t lumiCountsFV0 = 0; /// FV0 Luminosity counts moving average over lumiNHBFs orbits - uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated - uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit - uint32_t nTriggers = 0; /// number of triggers - uint32_t firstOrbit = 0; /// orbit of 1st trigger - uint16_t firstBC = 0; /// bc of 1st trigger - - ClassDefNV(CTFHeader, 3); + uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated + uint32_t lumiNHBFsFV0 = 0; /// Number of FV0 HBFs over which lumi is integrated + uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit + uint32_t nTriggers = 0; /// number of triggers + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + uint16_t inp1 = 0; /// lumiCounts input ID + uint16_t inp2 = 0; /// lumiCountsFV0 input ID + + ClassDefNV(CTFHeader, 5); }; /// wrapper for the Entropy-encoded trigger inputs and classes of the TF diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h new file mode 100644 index 0000000000000..78c4245b16b20 --- /dev/null +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTPRateFetcher.h @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef COMMON_CCDB_CTPRATEFETCHER_H_ +#define COMMON_CCDB_CTPRATEFETCHER_H_ + +#include + +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "DataFormatsCTP/Configuration.h" +#include "DataFormatsCTP/Scalers.h" + +namespace o2::ccdb +{ +class BasicCCDBManager; +} + +namespace o2::ctp +{ + +class CTPRateFetcher +{ + public: + CTPRateFetcher() = default; + double fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string sourceName); + double fetchNoPuCorr(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string sourceName); + void setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, bool initScalers); + void updateScalers(ctp::CTPRunScalers& scalers); + int getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName); // rates at start,stop and middle of the run + double getLumi(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName, int puCorr = 0); // total lumi for a run + double getLumiNoPuCorr(const std::string& classname, int type = 1); + double getLumiWPuCorr(const std::string& classname, int type = 1); + void setOrbit(bool orb) { mOrbit = orb; } // use orbit instead of time + void setOutsideLimits(bool qc) { mOutsideLimits = qc; } // return first/last rate of time outside of run + + private: + double fetchCTPratesInputs(uint64_t timeStamp, int input); + double fetchCTPratesClasses(uint64_t timeStamp, const std::string& className, int inputType = 1); + double fetchCTPratesInputsNoPuCorr(uint64_t timeStamp, int input); + double fetchCTPratesClassesNoPuCorr(uint64_t timeStamp, const std::string& className, int inputType = 1); + double getLumi(const std::string& classname, int type = 1, int puCorr = 0); + double pileUpCorrection(double rate); + int mRunNumber = -1; + bool mOutsideLimits = 0; + bool mOrbit = 0; + o2::ctp::CTPConfiguration mConfig{}; + o2::ctp::CTPRunScalers mScalers{}; + o2::parameters::GRPLHCIFData mLHCIFdata{}; + ClassDefNV(CTPRateFetcher, 1); +}; +} // namespace o2::ctp + + +#endif // COMMON_CCDB_CTPRATEFETCHER_H_ diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h index 2708fc72f0cd4..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace o2 { namespace ctp @@ -39,6 +39,7 @@ struct BCMask { std::string name = ""; std::string mask = ""; std::bitset BCmask; + int setBCmask(std::vector& tokens); void printStream(std::ostream& stream) const; ClassDefNV(BCMask, 1); }; @@ -62,7 +63,7 @@ struct CTPInput { std::uint64_t inputMask = 0; o2::detectors::DetID::ID detID = 16; // CTP bool neg = 1; - int getIndex() const { return ((inputMask > 0) ? 1 + log2(inputMask) : 0xff); } + uint32_t getIndex() const { return ((inputMask > 0) ? 1 + log2(inputMask) : 0xff); } std::string getInputDetName() const { return o2::detectors::DetID::getName(detID); } void setRun3DetName(std::string& run2Name); void printStream(std::ostream& strem) const; @@ -107,7 +108,7 @@ struct CTPClass { std::uint64_t classMask = 0; CTPDescriptor const* descriptor = nullptr; CTPCluster const* cluster = nullptr; - int clusterIndex = 0; + int clusterIndex = 0xff; int descriptorIndex = 0xff; uint32_t downScale = 1; std::vector BCClassMask; @@ -118,11 +119,13 @@ struct CTPClass { struct CTPInputsConfiguration { CTPInputsConfiguration() = default; std::vector CTPInputs; + static const std::vector CTPInputsDefault; int createInputsConfigFromFile(std::string& filename); void printStream(std::ostream& strem) const; static CTPInputsConfiguration defaultInputConfig; static void initDefaultInputConfig(); - static std::string getInputNameFromIndex(int index); + static std::string getInputNameFromIndex100(uint32_t index); + static std::string getInputNameFromIndex(uint32_t index); static int getInputIndexFromName(std::string& name); ClassDefNV(CTPInputsConfiguration, 0); }; @@ -137,10 +140,12 @@ class CTPConfiguration static bool isNumber(const std::string& s); int addInput(std::string& inp, int clsindex, std::map>& descInputsIndex); enum ConfigPart { START, + VERSION, RUN, INPUTS, MASKS, GENS, + DESCRIPTORS, LTG, LTGitems, CLUSTER, @@ -148,35 +153,44 @@ class CTPConfiguration UNKNOWN }; int loadConfigurationRun3(const std::string& ctpconfiguartion); void printStream(std::ostream& stream) const; + void setRunNumber(uint32_t runnumber) { mRunNumber = runnumber; } std::vector& getCTPInputs() { return mInputs; } std::vector& getCTPClasses() { return mCTPClasses; } const std::vector& getCTPInputs() const { return mInputs; } // Read-only interface const std::vector& getCTPClasses() const { return mCTPClasses; } // Read-only interface uint64_t getInputMask(const std::string& name) const; int getInputIndex(const std::string& name) const; + std::string getClassNameFromIndex(int index); + std::string getClassNameFromHWIndex(int index); + const CTPClass* getCTPClassFromHWIndex(const int index) const; bool isMaskInInputs(const uint64_t& mask) const; bool isBCMaskInConfig(const std::string maskname) const; + const BCMask* isBCMaskInConfigP(const std::string bcmask) const; const CTPInput* isInputInConfig(const std::string inpname) const; - const CTPInput* isInputInConfig(const int index) const; + const CTPInput* isInputInConfig(const uint32_t index) const; + const CTPDescriptor* isDescriptorInConfig(const std::string descname, int& index) const; void createInputsInDecriptorsFromNames(); uint64_t getDecrtiptorInputsMask(const std::string& name) const; std::map> getDet2InputMap(); uint64_t getTriggerClassMask() const; + uint64_t getTriggerClassMaskWInputs() const; + uint64_t getTriggerClassMaskWInputsNoTrgDets() const; std::vector getTriggerClassList() const; uint32_t getRunNumber() { return mRunNumber; }; std::vector getDetectorList() const; o2::detectors::DetID::mask_t getDetectorMask() const; uint64_t getClassMaskForInputMask(uint64_t inputMask) const; - void printConfigString() { std::cout << mConfigString << std::endl; }; + void printConfigString(); std::string getConfigString() { return mConfigString; }; CTPDescriptor* getDescriptor(int index) { return &mDescriptors[index]; }; int assignDescriptors(); + int checkConfigConsistency() const; private: std::string mConfigString = ""; uint32_t mRunNumber = 0; std::string mName = ""; - std::string mVersion = "1"; + std::string mVersion = "0"; std::vector mBCMasks; std::vector mGenerators; std::vector mInputs; @@ -185,52 +199,24 @@ class CTPConfiguration std::vector mClusters; std::vector mCTPClasses; int processConfigurationLineRun3(std::string& line, int& level, std::map>& descInputsIndex); + int processConfigurationLineRun3v2(std::string& line, int& level, std::map>& descInputsIndex); ClassDefNV(CTPConfiguration, 6); }; -// Run Manager -struct CTPActiveRun { - CTPActiveRun() = default; - long timeStart; - long timeStop; - CTPConfiguration cfg; - CTPRunScalers scalers; -}; -class CTPRunManager -{ - public: - CTPRunManager() = default; - void init(); - int loadRun(const std::string& cfg); - int startRun(const std::string& cfg); - int stopRun(uint32_t irun, long timeStamp); - int addScalers(uint32_t irun, std::time_t time); - int processMessage(std::string& topic, const std::string& message); - void printActiveRuns() const; - int saveRunScalersToCCDB(int i); - int saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart); - static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); - CTPRunScalers getScalersFromCCDB(long timestamp, std::string, bool& ok); - int loadScalerNames(); - // void setCCDBPathConfig(std::string path) { mCCDBPathCTPConfig = path;}; - void setCCDBPathScalers(std::string path) { mCCDBPathCTPScalers = path; }; - static void setCCDBHost(std::string host) { mCCDBHost = host; }; - void printCounters(); - private: - /// Database constants - // std::string mCCDBHost = "http://ccdb-test.cern.ch:8080"; - static std::string mCCDBHost; - std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; - std::array mActiveRuns; - std::array mActiveRunNumbers; - std::array mCounters; - std::map mScalerName2Position; - std::map mRunsLoaded; - int mEOX = 0; // redundancy check - int mNew = 1; // 1 - no CCDB: used for QC - ClassDefNV(CTPRunManager, 5); -}; +std::ostream& operator<<(std::ostream& in, const CTPConfiguration& conf); +struct CtpCfg { + CtpCfg() = default; + std::string filename = "ctp.cfg"; + int readAndSave(std::string& path); + uint32_t TFOrbits = 0; + int ccdb = -1; // -1 means def constructor was called + uint32_t orbitShift = 0; + uint32_t irInputs_1_24 = 0; + uint32_t irInputs_25_48 = 0; + std::vector listOfUsedInputs(); + ClassDefNV(CtpCfg, 2) +}; } // namespace ctp } // namespace o2 #endif //_CTP_CONFIGURATION_H_ diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h index 6514f344b0ea3..f52e63c24f36e 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Digits.h @@ -51,10 +51,12 @@ struct CTPDigit { o2::InteractionRecord intRecord; std::bitset CTPInputMask; std::bitset CTPClassMask; - CTPDigit() = default; void printStream(std::ostream& stream) const; void setInputMask(gbtword80_t mask); void setClassMask(gbtword80_t mask); + bool isInputEmpty() const { return CTPInputMask.count() == 0; } + bool isClassEmpty() const { return CTPClassMask.count() == 0; } + bool isEmpty() const { return isInputEmpty() && isClassEmpty(); } bool operator==(const CTPDigit& d) const { return intRecord == d.intRecord && CTPInputMask == d.CTPInputMask && CTPClassMask == d.CTPClassMask; @@ -69,7 +71,6 @@ struct CTPInputDigit { o2::InteractionRecord intRecord; std::bitset inputsMask; o2::detectors::DetID::ID detector; - CTPInputDigit() = default; ClassDefNV(CTPInputDigit, 1) }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h index 27f67ba6e92a8..c75fcc32ddaf4 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h @@ -13,6 +13,7 @@ #define _ALICEO2_CTP_LUMIINFO_H_ #include "CommonConstants/LHCConstants.h" #include +#include /// \brief Luminosity information as a moving average over certain number of TFs @@ -21,17 +22,21 @@ namespace o2 namespace ctp { struct LumiInfo { - LumiInfo() = default; uint32_t orbit = 0; // orbit of TF when was updated uint32_t nHBFCounted = 0; // length of interval in HB uint32_t nHBFCountedFV0 = 0; uint64_t counts = 0; // counts in the interval for the nominal lumi detector (FT0) uint64_t countsFV0 = 0; // connts for FV0 (less reliable) + int inp1 = 3; // TVX + int inp2 = 6; // VBA float getLumi() const { return nHBFCounted > 0 ? float(counts / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0() const { return nHBFCountedFV0 > 0 ? float(countsFV0 / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } + float getLumiAlt() const { return getLumiFV0(); } float getLumiError() const { return nHBFCounted > 0 ? float(std::sqrt(counts) / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0Error() const { return nHBFCountedFV0 > 0 ? float(std::sqrt(countsFV0) / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } - ClassDefNV(LumiInfo, 2); + float getLumiAltError() const { return getLumiFV0Error(); } + void printInputs() const; + ClassDefNV(LumiInfo, 3); }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h index f71799edceac7..45d54b034f8d9 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h @@ -33,6 +33,7 @@ struct errorCounters { void printStream(std::ostream& stream) const; uint32_t lmB = 0, l0B = 0, l1B = 0, lmA = 0, l0A = 0, l1A = 0; // decreasing counters uint32_t lmBlmA = 0, lmAl0B = 0, l0Bl0A = 0, l0Al1B = 0, l1Bl1A = 0; // between levels countres + uint32_t lmBlmAd1 = 0, lmAl0Bd1 = 0, l0Bl0Ad1 = 0, l0Al1Bd1 = 0, l1Bl1Ad1 = 0; // between levels countres - diff =1 - just warning uint32_t MAXPRINT = 3; }; struct CTPScalerRaw { @@ -67,31 +68,44 @@ struct CTPScalerRecordRaw { o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; - ClassDefNV(CTPScalerRecordRaw, 3); + ClassDefNV(CTPScalerRecordRaw, 4); }; struct CTPScalerRecordO2 { CTPScalerRecordO2() = default; o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; void printFromZero(std::ostream& stream, CTPScalerRecordO2& record0) const; - ClassDefNV(CTPScalerRecordO2, 3); + ClassDefNV(CTPScalerRecordO2, 4); }; class CTPRunScalers { public: + // + // static constexpr uint32_t NCOUNTERS = 1052; + // v1 + // static constexpr uint32_t NCOUNTERS = 1070; + // v2 - orbitid added at the end + static constexpr uint32_t NCOUNTERSv2 = 1071; + static constexpr uint32_t NCOUNTERS = 1085; + static std::vector scalerNames; CTPRunScalers() = default; - void printStream(std::ostream& stream) const; void printO2(std::ostream& stream) const; void printFromZero(std::ostream& stream) const; void printClasses(std::ostream& stream) const; std::vector getClassIndexes() const; + uint32_t getRunNumber() { return mRunNumber; }; + int getScalerIndexForClass(uint32_t cls) const; std::vector& getScalerRecordO2() { return mScalerRecordO2; }; + std::vector& getScalerRecordRaw() { return mScalerRecordRaw; }; + void setEpochTime(std::time_t tt, int index) { mScalerRecordRaw[index].epochTime = tt; }; int readScalers(const std::string& rawscalers); int convertRawToO2(); int checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& scal1, errorCounters& eCnts) const; @@ -100,37 +114,59 @@ class CTPRunScalers void setDetectorMask(o2::detectors::DetID::mask_t mask) { mDetectorMask = mask; }; void setRunNumber(uint32_t rnumber) { mRunNumber = rnumber; }; void addScalerRacordRaw(CTPScalerRecordRaw& scalerrecordraw) { mScalerRecordRaw.push_back(scalerrecordraw); }; - uint32_t getRunNUmber() { return mRunNumber; }; int printRates(); int printIntegrals(); + int printInputRateAndIntegral(int inp); + int printClassBRateAndIntegralII(int icls); + int printClassBRateAndIntegral(int iclsinscalers); + // + int addOrbitOffset(uint32_t offset); // - // static constexpr uint32_t NCOUNTERS = 1052; - // v1 - // static constexpr uint32_t NCOUNTERS = 1070; - // v2 - orbitid added at the end - static constexpr uint32_t NCOUNTERS = 1071; - static std::vector scalerNames; - void printLMBRateVsT() const; // prints LMB interaction rate vs time for debugging - // returns the pair of global (levelled) interaction rate, as well as interpolated // rate in Hz at a certain orbit number within the run - std::pair getRate(uint32_t orbit, int classindex, int type) const; + std::pair getRate(uint32_t orbit, int classindex, int type, bool qc = 0) const; /// same with absolute timestamp (not orbit) as argument - std::pair getRateGivenT(double timestamp, int classindex, int type) const; + std::pair getRateGivenT(double timestamp, int classindex, int type, bool qc = 0) const; - /// retrieves time boundaries of this scaler object + /// retrieves integral for class + std::array getIntegralForClass(int i) const + { + return { + mScalerRecordO2[0].scalers[i].classIndex, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmBefore - mScalerRecordO2[0].scalers[i].lmBefore, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmAfter - mScalerRecordO2[0].scalers[i].lmAfter, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0Before - mScalerRecordO2[0].scalers[i].l0Before, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0After - mScalerRecordO2[0].scalers[i].l0After, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l1Before - mScalerRecordO2[0].scalers[i].l1Before, + mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l1After - mScalerRecordO2[0].scalers[i].l1After, + }; + } + /// retrieves integral - same interface as getRate, no pileup correction + uint64_t getLumiNoPuCorr(int classindex, int type) const; + /// retrieves vector of counters - same interface as getRate, needed for + std::vector> getRatesForIndex(int classindex, int type) const; + /// retrieves time boundaries of this scaler object from O2 scalers std::pair getTimeLimit() const { return std::make_pair((unsigned long)mScalerRecordO2[0].epochTime * 1000, (unsigned long)mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime * 1000); } - - /// retrieves orbit boundaries of this scaler object + /// retrieves time boundaries of this scaler object from Raw: should be same as from O2 and can be used without convertRawToO2 call + std::pair getTimeLimitFromRaw() const + { + return std::make_pair((unsigned long)mScalerRecordRaw[0].epochTime * 1000, (unsigned long)mScalerRecordRaw[mScalerRecordRaw.size() - 1].epochTime * 1000); + } + /// retrieves orbit boundaries of this scaler object from O2 std::pair getOrbitLimit() const { return std::make_pair((unsigned long)mScalerRecordO2[0].intRecord.orbit, (unsigned long)mScalerRecordO2[mScalerRecordO2.size() - 1].intRecord.orbit); } + /// retrieves orbit boundaries of this scaler object from Raw: should be same as from O2 and can be used without convertRawToO2 call + std::pair getOrbitLimitFromRaw() const + { + return std::make_pair((unsigned long)mScalerRecordRaw[0].intRecord.orbit, (unsigned long)mScalerRecordRaw[mScalerRecordRaw.size() - 1].intRecord.orbit); + } private: // map from class index to overflow @@ -143,11 +179,11 @@ class CTPRunScalers o2::detectors::DetID::mask_t mDetectorMask; std::vector mScalerRecordRaw; std::vector mScalerRecordO2; - int processScalerLine(const std::string& line, int& level, int& nclasses); - int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows); + int processScalerLine(const std::string& line, int& level, uint32_t& nclasses); + int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows); int updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, overflows_t& classesoverflows) const; int updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw& scal1, std::array& overflow) const; - + int updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const; ClassDefNV(CTPRunScalers, 2); }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h index ec9f003d8f94c..063336e5461ce 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h @@ -24,8 +24,10 @@ namespace ctp struct TriggerOffsetsParam : public o2::conf::ConfigurableParamHelper { static constexpr int MaxNDet = 32; // take with margin to account for possible changes / upgrades int64_t LM_L0 = 15; - int64_t L0_L1 = 280; + int64_t L0_L1 = 281; // trigger input latency + int64_t globalInputsShift = 0; // Global shift of inps; customOffset[CTP] is global shift of classes int64_t customOffset[MaxNDet] = {}; + int64_t L0_L1_classes = 280; // trigger input latency O2ParamDef(TriggerOffsetsParam, "TriggerOffsetsParam"); // boilerplate stuff + make principal key }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx new file mode 100644 index 0000000000000..5f31fe5741240 --- /dev/null +++ b/DataFormats/Detectors/CTP/src/CTPRateFetcher.cxx @@ -0,0 +1,274 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsCTP/CTPRateFetcher.h" +#include "CCDB/BasicCCDBManager.h" + +#include +#include +#include "CommonConstants/LHCConstants.h" + +using namespace o2::ctp; +double CTPRateFetcher::fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName) +{ + auto triggerRate = fetchNoPuCorr(ccdb, timeStamp, runNumber, sourceName); + if (triggerRate >= 0) { + return pileUpCorrection(triggerRate); + } + return -1; +} +double CTPRateFetcher::fetchNoPuCorr(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName) +{ + setupRun(runNumber, ccdb, timeStamp, 1); + if (sourceName.find("ZNC") != std::string::npos) { + if (runNumber < 544448) { + return fetchCTPratesInputsNoPuCorr(timeStamp, 25) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } else { + return fetchCTPratesClassesNoPuCorr(timeStamp, "C1ZNC-B-NOPF-CRU", 6) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } + } else if (sourceName == "T0CE") { + return fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVXTCE-B-NOPF"); + } else if (sourceName == "T0SC") { + return fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVXTSC-B-NOPF"); + } else if (sourceName == "T0VTX") { + if (runNumber < 534202) { + return fetchCTPratesClassesNoPuCorr(timeStamp, "minbias_TVX_L0", 3); // 2022 + } else { + double ret = fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVX-B-NOPF"); + if (ret == -2.) { + LOG(info) << "Trying different class"; + ret = fetchCTPratesClassesNoPuCorr(timeStamp, "CMTVX-NONE"); + if (ret < 0) { + LOG(error) << "None of the classes used for lumi found"; + return -1.; + } + } + return ret; + } + } + LOG(error) << "CTP rate for " << sourceName << " not available"; + return -1.; +} +void CTPRateFetcher::updateScalers(ctp::CTPRunScalers& scalers) +{ + mScalers = scalers; + mScalers.convertRawToO2(); +} +// +int CTPRateFetcher::getRates(std::array& rates, o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName) // rates at start,stop and middle of the run +{ + setupRun(runNumber, ccdb, 0, 1); + mOrbit = 1; + mOutsideLimits = 1; + auto orbitlimits = mScalers.getOrbitLimit(); + // std::cout << "1st orbit:" << orbitlimits.first << " last:" << orbitlimits.second << " Middle:" << (orbitlimits.first + orbitlimits.second)/2 << std::endl; + double rate0 = fetch(ccdb, orbitlimits.first, mRunNumber, sourceName); + double rateLast = fetch(ccdb, orbitlimits.second, mRunNumber, sourceName); + double rateM = fetch(ccdb, (orbitlimits.first + orbitlimits.second) / 2, mRunNumber, sourceName); + // std::cout << rate0 << " " << rateLast << " " << rateM << std::endl; + rates[0] = rate0; + rates[1] = rateLast; + rates[2] = rateM; + return 0; +} +double CTPRateFetcher::getLumiNoPuCorr(const std::string& classname, int type) +{ + if (classname == "zncinp") { + return mScalers.getLumiNoPuCorr(26, 7); + } + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(classname) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << classname << " not found in CTPConfiguration"; + return -1; + } + return mScalers.getLumiNoPuCorr(classIndex, type); +} +double CTPRateFetcher::getLumiWPuCorr(const std::string& classname, int type) +{ + std::vector> scals; + if (classname == "zncinp") { + scals = mScalers.getRatesForIndex(26, 7); + } else { + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(classname) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << classname << " not found in CTPConfiguration"; + return -1; + } + scals = mScalers.getRatesForIndex(classIndex, type); + } + double lumi = 0; + for (auto const& ss : scals) { + // std::cout << ss.first << " " << ss.second << " " << pileUpCorrection(ss.first/ss.second) << std::endl; + lumi += pileUpCorrection(ss.first / ss.second) * ss.second; + } + return lumi; +} +double CTPRateFetcher::getLumi(const std::string& classname, int type, int puCorr) +{ + if (puCorr) { + return getLumiWPuCorr(classname, type); + } else { + return getLumiNoPuCorr(classname, type); + } +} + +double CTPRateFetcher::getLumi(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, const std::string sourceName, int puCorr) +{ + // setupRun(runNumber, ccdb, timeStamp, 1); + if (sourceName.find("ZNC") != std::string::npos) { + if (runNumber < 544448) { + return getLumi("zncinp", 1, puCorr) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } else { + return getLumi("C1ZNC-B-NOPF-CRU", 6, puCorr) / (sourceName.find("hadronic") != std::string::npos ? 28. : 1.); + } + } else if (sourceName == "T0CE") { + return getLumi("CMTVXTCE-B-NOPF", 1, puCorr); + } else if (sourceName == "T0SC") { + return getLumi("CMTVXTSC-B-NOPF", 1, puCorr); + } else if (sourceName == "T0VTX") { + if (runNumber < 534202) { + return getLumi("minbias_TVX_L0", 3, puCorr); // 2022 + } else { + double ret = getLumi("CMTVX-B-NOPF", 1, puCorr); + if (ret == -1.) { + LOG(info) << "Trying different class"; + ret = getLumi("CMTVX-NONE", 1, puCorr); + if (ret < 0) { + LOG(fatal) << "None of the classes used for lumi found"; + } + } + return ret; + } + } + LOG(error) << "CTP Lumi for " << sourceName << " not available"; + return 0; +} +// +double CTPRateFetcher::fetchCTPratesClasses(uint64_t timeStamp, const std::string& className, int inputType) +{ + auto triggerRate = fetchCTPratesClassesNoPuCorr(timeStamp, className, inputType); + if (triggerRate >= 0) { + return pileUpCorrection(triggerRate); + } + return -1; +} +double CTPRateFetcher::fetchCTPratesClassesNoPuCorr(uint64_t timeStamp, const std::string& className, int inputType) +{ + std::vector& ctpcls = mConfig.getCTPClasses(); + std::vector clslist = mConfig.getTriggerClassList(); + int classIndex = -1; + for (size_t i = 0; i < clslist.size(); i++) { + if (ctpcls[i].name.find(className) != std::string::npos) { + classIndex = i; + break; + } + } + if (classIndex == -1) { + LOG(warn) << "Trigger class " << className << " not found in CTPConfiguration"; + return -2.; + } + if (mOrbit) { + auto rate{mScalers.getRate((uint32_t)timeStamp, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } else { + auto rate{mScalers.getRateGivenT(timeStamp * 1.e-3, classIndex, inputType, mOutsideLimits)}; + return rate.second; + } +} +double CTPRateFetcher::fetchCTPratesInputs(uint64_t timeStamp, int input) +{ + std::vector& recs = mScalers.getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + if (mOrbit) { + return pileUpCorrection(mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second); + } else { + return pileUpCorrection(mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second); + } + } else { + LOG(error) << "Inputs not available"; + return -1.; + } +} +double CTPRateFetcher::fetchCTPratesInputsNoPuCorr(uint64_t timeStamp, int input) +{ + std::vector& recs = mScalers.getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + if (mOrbit) { + return mScalers.getRate((uint32_t)timeStamp, input, 7, mOutsideLimits).second; + } else { + return mScalers.getRateGivenT(timeStamp * 1.e-3, input, 7, mOutsideLimits).second; // qc flag implemented only for time + } + } else { + LOG(error) << "Inputs not available"; + return -1.; + } +} + +double CTPRateFetcher::pileUpCorrection(double triggerRate) +{ + if (mLHCIFdata.getFillNumber() == 0) { + LOG(fatal) << "No filling" << std::endl; + } + auto bfilling = mLHCIFdata.getBunchFilling(); + std::vector bcs = bfilling.getFilledBCs(); + double nbc = bcs.size(); + double nTriggersPerFilledBC = triggerRate / nbc / constants::lhc::LHCRevFreq; + double mu = -std::log(1 - nTriggersPerFilledBC); + return mu * nbc * constants::lhc::LHCRevFreq; +} + +void CTPRateFetcher::setupRun(int runNumber, o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, bool initScalers) +{ + if (runNumber == mRunNumber) { + return; + } + mRunNumber = runNumber; + LOG(info) << "Setting up CTP scalers for run " << mRunNumber << " and timestamp : " << timeStamp; + auto ptrLHCIFdata = ccdb->getSpecific("GLO/Config/GRPLHCIF", timeStamp); + if (ptrLHCIFdata == nullptr) { + LOG(error) << "GRPLHCIFData not in database, timestamp:" << timeStamp; + return; + } + mLHCIFdata = *ptrLHCIFdata; + std::map metadata; + metadata["runNumber"] = std::to_string(mRunNumber); + auto ptrConfig = ccdb->getSpecific("CTP/Config/Config", timeStamp, metadata); + if (ptrConfig == nullptr) { + LOG(error) << "CTPRunConfig not in database, timestamp:" << timeStamp; + return; + } + mConfig = *ptrConfig; + if (initScalers) { + auto ptrScalers = ccdb->getSpecific("CTP/Calib/Scalers", timeStamp, metadata); + if (ptrScalers) { + mScalers = *ptrScalers; + mScalers.convertRawToO2(); + } else { + LOG(error) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + } +} diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index 258c84ee92eb6..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -19,10 +19,10 @@ #include #include "CommonUtils/StringUtils.h" #include +#include using namespace o2::ctp; // -std::string CTPRunManager::mCCDBHost = "http://o2-ccdb.internal"; const std::map CTPInput::run2DetToRun3Det = {{"T", "FT0"}, {"V", "FV0"}, {"U", "FDD"}, {"E", "EMC"}, {"D", "EMC"}, {"H", "TRD"}, {"O", "TOF"}, {"P", "PHS"}, {"Z", "ZDC"}}; const std::map CTPConfiguration::detName2LTG = {{"FV0", "1"}, {"FT0", "2"}, {"FDD", "3"}, {"ITS", "4"}, {"TOF", "5"}, {"MFT", "6"}, {"TPC", "7"}, {"MCH", "8"}, {"MID", "9"}, {"TST", "10"}, {"TRD", "13"}, {"HMP", "14"}, {"ZDC", "15"}, {"PHS", "16"}, {"EMC", "17"}, {"CPV", "18"}}; // @@ -48,6 +48,67 @@ bool CTPConfiguration::isNumber(const std::string& s) s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); } // +int BCMask::setBCmask(std::vector& tokens) +{ + BCmask.reset(); + name = tokens[1]; + bool coded = tokens[2].find("L") != std::string::npos; + coded |= tokens[2].find("H") != std::string::npos; + std::cout << "coded:" << coded << std::endl; + if (coded) { + // jusko notation + std::string bcmaskstr = tokens[2]; + size_t pos = 0; + size_t posnext = 0; + int bccur = 0; + while (bccur < 3564) { + // std::cout << "pos:" << pos << std::endl; + size_t posH = bcmaskstr.find('H', pos); + size_t posL = bcmaskstr.find('L', pos); + // std::cout << "H:" << posH << " L:" << posL << std::endl; + bool b = 1; + posnext = posH; + if (posL < posH) { + posnext = posL; + b = 0; + } + std::string bcsub = bcmaskstr.substr(pos, posnext - pos); + // std::cout << "bcsub:" << bcsub << " b:" << b << std::endl; + int bcrange = 0; + try { + bcrange = std::stoull(bcsub); + } catch (...) { + LOG(warning) << "problem in bcmask decoding H:" << posH << " posL:" << posL << " bcsub:" << bcsub; + return 1; + } + if (b) { + for (int bc = bccur; bc < bccur + bcrange; bc++) { + try { + BCmask.set(bc, 1); + } catch (...) { + LOG(warning) << "BC mask decoding to big bc:" << bc; + } + } + } + bccur += bcrange; + pos = posnext + 1; + // std::cout << "bccur:" << bccur << std::endl; + } + } else { + // list of integers + for (uint32_t i = 2; i < tokens.size(); i++) { + uint32_t bc; + try { + bc = std::stoull(tokens[i]); + } catch (...) { + LOG(info) << "mask syntax:" << i << ":" << tokens[i]; + continue; + } + BCmask.set(bc, 1); + } + } + return 0; +} void BCMask::printStream(std::ostream& stream) const { stream << "CTP BC mask:" << name << ":" << mask; /// << std::endl; @@ -149,7 +210,7 @@ int CTPConfiguration::addInput(std::string& inp, int clsindex, std::map>& descInputsIndex) { LOG(info) << "Processing line"; LOG(info) << "line:" << line << " lev:" << level; - if (line.size() == 0) { - return 0; - } - if (line.at(0) == '#') { - return 0; - } // std::vector tokens = o2::utils::Str::tokenize(line, ' '); size_t ntokens = tokens.size(); @@ -283,7 +366,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level // jusko notation } else { // list of integers - for (int i = 2; i < ntokens; i++) { + for (uint32_t i = 2; i < ntokens; i++) { uint32_t bc; try { bc = std::stoull(tokens[i]); @@ -338,7 +421,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level LOG(info) << "Cluster:" << line; cluster.name = tokens[2]; o2::detectors::DetID::mask_t mask; - for (int item = 3; item < ntokens; item++) { + for (uint32_t item = 3; item < ntokens; item++) { std::string detname = tokens[item]; capitaliseString(detname); // LOG(info) << "Detector:" << detname; @@ -373,7 +456,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level CTPDescriptor desc; desc.name = "d" + cls.name; // LOG(info) << "point:" << cls.cluster << " " << &mClusters.front(); - for (int i = 2; i < tokens.size(); i++) { + for (uint32_t i = 2; i < tokens.size(); i++) { std::string token = tokens[i]; bool isGenerator = 0; for (auto const& gen : CTPGenerator::Generators) { @@ -397,7 +480,7 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level cls.downScale = std::stoul(token, nullptr, 16); } else if (token.find("bcm") != std::string::npos) { // bcmask // std::cout << "Mask" << std::endl; - int i = 0; + uint32_t i = 0; for (auto const& bcm : mBCMasks) { if (bcm.name == token) { cls.BCClassMask.push_back(&bcm); @@ -425,19 +508,243 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level } return 0; } +int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& level, std::map>& descInputsIndex) +{ + LOG(debug) << "Processing line"; + LOG(debug) << "line:" << line << " lev:" << level; + // + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + size_t ntokens = tokens.size(); + if (ntokens == 0) { + LOG(warning) << "# of tokens zero in line:" << line; + return 0; + } + size_t first; + if (((first = line.find("ver")) != std::string::npos) && (level == START)) { + mVersion = line; + // std::cout << "debug:" << mVersion << std::endl; + level = VERSION; + return 0; + } else if (((first = line.find("run")) != std::string::npos) && (level == VERSION)) { + level = RUN; + } else if ((line.find("INPUTS") != std::string::npos) && (level == RUN)) { + level = INPUTS; + return 0; + } else if ((line.find("inp") != std::string::npos) && (level == INPUTS)) { + level = INPUTS; + } else if ((line.find("BCMASKS") != std::string::npos) && ((level == INPUTS) || (level == RUN))) { + level = MASKS; + return 0; + } else if (((first = line.find("bcm")) != std::string::npos) && (level == MASKS)) { + level = MASKS; + } else if (line.find("GENS") != std::string::npos) { + level = GENS; + return 0; + } else if (CTPGenerator::Generators.count(tokens[0]) && (level == GENS)) { + level = GENS; + } else if (line.find("DESCRIPTORS") != std::string::npos) { + level = DESCRIPTORS; + return 0; + } else if ((tokens[0][0] == 'D') && (level == DESCRIPTORS)) { + level = DESCRIPTORS; + } else if ((first = line.find("LTG")) != std::string::npos) { + level = LTG; + } else if ((first = line.find("cluster")) != std::string::npos) { + level = CLUSTER; + } else { + bool knownlevels = ((level == LTGitems) || (level == CLASS)); + if (knownlevels == false) { + level = UNKNOWN; + } + } + LOG(debug) << "Level:" << level; + switch (level) { + case VERSION: { + break; + } + case RUN: { + try { + mRunNumber = std::stoul(tokens[1]); + } catch (...) { + LOG(error) << "RUN line:" << line; + } + level = RUN; + break; + } + case INPUTS: { + level = INPUTS; + if (tokens.size() != 3) { + LOG(error) << "Wrong input line:" << line; + return 1; + } + CTPInput ctpinp; + ctpinp.name = tokens[1]; + ctpinp.level = tokens[1][0]; + std::string run2Name{tokens[1][1]}; + ctpinp.setRun3DetName(run2Name); + uint32_t index = std::stoul(tokens[2]); + ctpinp.inputMask = (1ull << (index - 1)); + mInputs.push_back(ctpinp); + LOG(debug) << "Input:" << ctpinp.name << " index:" << index; + break; + } + case MASKS: { + BCMask bcmask; + if (tokens.size() < 3) { + LOG(error) << "Wrong bc mask:" << line; + break; + } + bcmask.setBCmask(tokens); + mBCMasks.push_back(bcmask); + LOG(debug) << "BC mask added:" << bcmask.name; + break; + } + case GENS: { + CTPGenerator gen; + gen.name = tokens[0]; + gen.frequency = tokens[1]; + mGenerators.push_back(gen); + LOG(debug) << "Gen added:" << line; + break; + } + case DESCRIPTORS: { + if ((tokens.size() < 2)) { + if (line.find("TRUE") != std::string::npos) { + CTPDescriptor desc; + desc.name = tokens[0]; + mDescriptors.push_back(desc); + break; + } else { + LOG(warning) << "Unexpected Descriptor:" << line; + break; + } + } + CTPDescriptor desc; + desc.name = tokens[0]; + for (uint32_t i = 1; i < tokens.size(); i++) { + const CTPInput* inp = isInputInConfig(tokens[i]); + if (inp != nullptr) { + desc.inputs.push_back(inp); + } + } + mDescriptors.push_back(desc); + break; + } + case LTG: { + CTPDetector ctpdet; + std::string detname = tokens[1]; + capitaliseString(detname); + o2::detectors::DetID det(detname.c_str()); + if (isDetector(det)) { + ctpdet.detID = det.getID(); + LOG(debug) << "Detector found:" << det.getID() << " " << detname; + } else { + LOG(error) << "Unknown detectors:" << line; + } + mDetectors.push_back(ctpdet); + level = LTGitems; + break; + } + case LTGitems: { + if (ntokens == 1) { + mDetectors.back().mode = tokens[0]; + } + LOG(debug) << "LTGitem:" << line; + break; + } + case CLUSTER: { + CTPCluster cluster; + try { + cluster.hwMask = std::stoull(tokens[0]); + } catch (...) { + LOG(error) << "Cluster syntax error:" << line; + return level; + } + LOG(debug) << "Cluster:" << line; + cluster.name = tokens[2]; + o2::detectors::DetID::mask_t mask; + for (uint32_t item = 3; item < ntokens; item++) { + std::string detname = tokens[item]; + capitaliseString(detname); + // LOG(info) << "Detector:" << detname; + o2::detectors::DetID det(detname.c_str()); + isDetector(det); + mask |= det.getMask(); + } + cluster.maskCluster = mask; + mClusters.push_back(cluster); + level = CLASS; + // LOG(info) << "Cluster done:" << cluster.name << std::endl; + break; + } + case CLASS: { + // add to the last cluster + if (tokens.size() < 6) { + LOG(error) << "CTPClass items < 6" << line; + break; + } + uint64_t index; + try { + index = std::stoull(tokens[1]); + } catch (...) { + LOG(error) << "Class syntax error:" << line; + return level; + } + LOG(debug) << "Class:" << line; + CTPClass cls; + cls.classMask = 1ull << index; + cls.name = tokens[0]; + // Descriptor + std::string descname = tokens[2]; + int dindex; + if (descname.find("DTRUE") != std::string::npos) { + descname = "DTRUE"; + } + const CTPDescriptor* desc = isDescriptorInConfig(descname, dindex); + if (desc != nullptr) { + cls.descriptor = desc; + cls.descriptorIndex = dindex; + } + cls.clusterIndex = mClusters.size() - 1; + // PF not member of class + std::string bcmask = tokens[5]; + bcmask = bcmask.substr(1, bcmask.size() - 2); + if (bcmask.size()) { + const BCMask* bcm = isBCMaskInConfigP(bcmask); + if (bcm != nullptr) { + cls.BCClassMask.push_back(bcm); + } + } + // Down scaling + if (tokens.size() > 6) { + cls.downScale = std::stoul(tokens[6], nullptr, 16); + } + mCTPClasses.push_back(cls); + break; + } + default: { + LOG(warning) << "unknown line:" << line; + } + } + return 0; +} void CTPConfiguration::printStream(std::ostream& stream) const { - stream << "Configuration:" << mName << "\n Version:" << mVersion << std::endl; - stream << "Run:" << mRunNumber << " cfg name:" << mName; + stream << "Configuration:" << mName << " Version:" << mVersion << std::endl; + stream << "Run:" << mRunNumber << " cfg name:" << mName << std::endl; stream << "CTP BC masks:" << std::endl; for (const auto& i : mBCMasks) { i.printStream(stream); } - stream << "CTP inputs:" << std::endl; + stream << "CTP inputs:" << mInputs.size() << std::endl; for (const auto& i : mInputs) { i.printStream(stream); } - stream << "CTP descriptors:" << std::endl; + stream << "CTP generators:" << std::endl; + for (const auto& i : mGenerators) { + i.printStream(stream); + } + stream << "CTP descriptors:" << mDescriptors.size() << std::endl; for (const auto& i : mDescriptors) { i.printStream(stream); } @@ -473,6 +780,36 @@ int CTPConfiguration::getInputIndex(const std::string& name) const LOG(info) << "input:" << name << " index:" << index; return index; } +std::string CTPConfiguration::getClassNameFromIndex(int index) +{ + if (index < (int)mCTPClasses.size()) { + return mCTPClasses[index].name; + } else { + std::string name = "Cls" + std::to_string(index); + return name; + } +}; +std::string CTPConfiguration::getClassNameFromHWIndex(int index) +{ + for (auto& cls : mCTPClasses) { + if (cls.classMask == (1ull << index)) { + return cls.name; + } + } + std::string ret = "not found"; + return ret; +} +const CTPClass* CTPConfiguration::getCTPClassFromHWIndex(int index) const +{ + const CTPClass* clsfound = nullptr; + for (auto const& cls : mCTPClasses) { + if (index == cls.getIndex()) { + clsfound = &cls; + break; + } + } + return clsfound; +} bool CTPConfiguration::isMaskInInputs(const uint64_t& mask) const { for (auto const& inp : mInputs) { @@ -491,18 +828,29 @@ bool CTPConfiguration::isBCMaskInConfig(const std::string maskname) const } return false; } +const BCMask* CTPConfiguration::isBCMaskInConfigP(const std::string maskname) const +{ + for (const auto& bcm : mBCMasks) { + if (bcm.name == maskname) { + LOG(info) << "isBCMaskInConfigP found:" << maskname; + return &bcm; + } + } + LOG(info) << "isBCMaskInConfigP NOT found:" << maskname; + return nullptr; +} const CTPInput* CTPConfiguration::isInputInConfig(const std::string inpname) const { for (const auto& inp : mInputs) { if (inp.name == inpname) { - LOG(info) << "isInpuyInConfig found:" << inpname; + LOG(info) << "isInputInConfig found:" << inpname; return &inp; } } LOG(info) << "isInputInConfig NOT found:" << inpname; return nullptr; } -const CTPInput* CTPConfiguration::isInputInConfig(const int index) const +const CTPInput* CTPConfiguration::isInputInConfig(const uint32_t index) const { for (const auto& inp : mInputs) { // std::cout << "isInputINConfig:" << inp.name << " " << inp.getIndex() << " " << index << std::endl; @@ -513,6 +861,19 @@ const CTPInput* CTPConfiguration::isInputInConfig(const int index) const } return nullptr; } +const CTPDescriptor* CTPConfiguration::isDescriptorInConfig(const std::string descname, int& index) const +{ + index = 0; + for (const auto& desc : mDescriptors) { + if (desc.name == descname) { + LOG(info) << "isDescriptorInConfig found:" << descname; + return &desc; + } + index++; + } + LOG(info) << "isDescriptorInConfig NOT found:" << descname; + return nullptr; +} void CTPConfiguration::createInputsInDecriptorsFromNames() // using run3 conventions for inputs { @@ -553,6 +914,32 @@ uint64_t CTPConfiguration::getTriggerClassMask() const } return clsmask; } +uint64_t CTPConfiguration::getTriggerClassMaskWInputs() const +{ + uint64_t clsmask = 0; + for (auto const& cls : mCTPClasses) { + if (cls.name.find("TRUE") != std::string::npos) { // ignoring internal ctp generators + continue; + } + clsmask |= cls.classMask; + } + return clsmask; +} +uint64_t CTPConfiguration::getTriggerClassMaskWInputsNoTrgDets() const +{ + uint64_t clsmask = 0; + for (auto const& cls : mCTPClasses) { + bool exclude = cls.name.find("TRUE") != std::string::npos; // ignoring internal ctp generators + exclude += cls.name.find("EMC") != std::string::npos; + exclude += cls.name.find("TRD") != std::string::npos; + exclude += cls.name.find("HMP") != std::string::npos; + if (!exclude) { + clsmask |= cls.classMask; + } + } + return clsmask; +} +// Hardware positions of classes std::vector CTPConfiguration::getTriggerClassList() const { uint64_t clsmask = getTriggerClassMask(); @@ -604,393 +991,80 @@ int CTPConfiguration::assignDescriptors() } return 0; } -/// -/// Run Managet to manage Config and Scalers -/// -void CTPRunManager::init() -{ - for (auto r : mActiveRuns) { - r = nullptr; - } - loadScalerNames(); - LOG(info) << "CCDB host:" << mCCDBHost; - LOG(info) << "CTP vNew:" << mNew; - LOG(info) << "CTPRunManager initialised."; -} -int CTPRunManager::loadRun(const std::string& cfg) +int CTPConfiguration::checkConfigConsistency() const { - LOG(info) << "Loading run: " << cfg; - auto now = std::chrono::system_clock::now(); - long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); - LOG(info) << "Timestamp real time:" << timeStamp; - size_t pos = cfg.find(" "); - std::string cfgmod = cfg; - if (pos == std::string::npos) { - LOG(warning) << "Space not found in CTP config"; - } else { - std::string f = cfg.substr(0, pos); - if (f.find("run") == std::string::npos) { - double_t tt = std::stold(f); - timeStamp = (tt * 1000.); - LOG(info) << "Timestamp file:" << timeStamp; - cfgmod = cfg.substr(pos, cfg.size()); - LOG(info) << "ctpcfg: using ctp time"; + LOG(info) << "Checking consistency run:" << mRunNumber; + int ret = 0; + // All inputs used ? + // std::map inputs; + std::map inputs; + for (auto const& inp : mInputs) { + inputs[inp.name] = 0; + } + // Are all descriptors used + // std::map descs; + std::map descs; + for (auto const& desc : mDescriptors) { + descs[desc.name] = 0; + // std::cout << "1 " << &desc << std::endl; + for (auto const inp : desc.inputs) { + inputs[inp->name] += 1; } } - CTPActiveRun* activerun = new CTPActiveRun; - activerun->timeStart = timeStamp; - activerun->cfg.loadConfigurationRun3(cfgmod); - activerun->cfg.printStream(std::cout); - // - uint32_t runnumber = activerun->cfg.getRunNumber(); - activerun->scalers.setRunNumber(runnumber); - activerun->scalers.setClassMask(activerun->cfg.getTriggerClassMask()); - o2::detectors::DetID::mask_t detmask = activerun->cfg.getDetectorMask(); - activerun->scalers.setDetectorMask(detmask); + std::cout << "desc1:" << descs.size() << std::endl; // - mRunsLoaded[runnumber] = activerun; - saveRunConfigToCCDB(&activerun->cfg, timeStamp); - return 0; -} -int CTPRunManager::startRun(const std::string& cfg) -{ - return 0; -} -int CTPRunManager::stopRun(uint32_t irun, long timeStamp) -{ - LOG(info) << "Stopping run index: " << irun; - if (mActiveRuns[irun] == nullptr) { - LOG(error) << "No config for run index:" << irun; - return 1; - } - // const auto now = std::chrono::system_clock::now(); - // const long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); - mActiveRuns[irun]->timeStop = timeStamp * 1000.; - saveRunScalersToCCDB(irun); - delete mActiveRuns[irun]; - mActiveRuns[irun] = nullptr; - return 0; -} -int CTPRunManager::addScalers(uint32_t irun, std::time_t time) -{ - if (mActiveRuns[irun] == nullptr) { - LOG(error) << "No config for run index:" << irun; - return 1; - } - std::string orb = "extorb"; - std::string orbitid = "orbitid"; - CTPScalerRecordRaw scalrec; - scalrec.epochTime = time; - std::vector clslist = mActiveRuns[irun]->cfg.getTriggerClassList(); - for (auto const& cls : clslist) { - std::string cmb = "clamb" + std::to_string(cls + 1); - std::string cma = "clama" + std::to_string(cls + 1); - std::string c0b = "cla0b" + std::to_string(cls + 1); - std::string c0a = "cla0a" + std::to_string(cls + 1); - std::string c1b = "cla1b" + std::to_string(cls + 1); - std::string c1a = "cla1a" + std::to_string(cls + 1); - CTPScalerRaw scalraw; - scalraw.classIndex = (uint32_t)cls; - // std::cout << "cls:" << cls << " " << scalraw.classIndex << std::endl; - scalraw.lmBefore = mCounters[mScalerName2Position[cmb]]; - scalraw.lmAfter = mCounters[mScalerName2Position[cma]]; - scalraw.l0Before = mCounters[mScalerName2Position[c0b]]; - scalraw.l0After = mCounters[mScalerName2Position[c0a]]; - scalraw.l1Before = mCounters[mScalerName2Position[c1b]]; - scalraw.l1After = mCounters[mScalerName2Position[c1a]]; - // std::cout << "positions:" << cmb << " " << mScalerName2Position[cmb] << std::endl; - // std::cout << "positions:" << cma << " " << mScalerName2Position[cma] << std::endl; - scalrec.scalers.push_back(scalraw); - } - // detectors - // std::vector detlist = mActiveRuns[irun]->cfg.getDetectorList(); - o2::detectors::DetID::mask_t detmask = mActiveRuns[irun]->cfg.getDetectorMask(); - for (uint32_t i = 0; i < 32; i++) { - o2::detectors::DetID::mask_t deti = 1ul << i; - bool detin = (detmask & deti).count(); - if (detin) { - std::string detname(o2::detectors::DetID::getName(i)); - std::string countername = "ltg" + CTPConfiguration::detName2LTG.at(detname) + "_PH"; - uint32_t detcount = mCounters[mScalerName2Position[countername]]; - scalrec.scalersDets.push_back(detcount); - // LOG(info) << "Scaler for detector:" << countername << ":" << detcount; + for (const auto& cls : mCTPClasses) { + if (cls.classMask == 0) { + std::cout << "ERROR class:" << cls.name << " NO CLASS MASK" << std::endl; + ret++; } - } - // - if (mNew == 0) { - scalrec.intRecord.orbit = mCounters[mScalerName2Position[orb]]; - } else { - scalrec.intRecord.orbit = mCounters[mScalerName2Position[orbitid]]; - } - scalrec.intRecord.bc = 0; - mActiveRuns[irun]->scalers.addScalerRacordRaw(scalrec); - LOG(info) << "Adding scalers for orbit:" << scalrec.intRecord.orbit; - // scalrec.printStream(std::cout); - // printCounters(); - return 0; -} -int CTPRunManager::processMessage(std::string& topic, const std::string& message) -{ - LOG(info) << "Processing message with topic:" << topic; - std::string firstcounters; - if (topic.find("ctpconfig") != std::string::npos) { - LOG(info) << "ctpconfig received"; - loadRun(message); - return 0; - } - if (topic.find("sox") != std::string::npos) { - // get config - size_t irun = message.find("run"); - if (irun == std::string::npos) { - LOG(warning) << "run keyword not found in SOX"; - irun = message.size(); + if (cls.cluster == nullptr) { + std::cout << "ERROR class:" << cls.name << " NO CLUSTER" << std::endl; + ret++; } - LOG(info) << "SOX received, Run keyword position:" << irun; - std::string cfg = message.substr(irun, message.size() - irun); - startRun(cfg); - firstcounters = message.substr(0, irun); - } - if (topic.find("eox") != std::string::npos) { - LOG(info) << "EOX received"; - mEOX = 1; - } - // - std::vector tokens; - if (firstcounters.size() > 0) { - tokens = o2::utils::Str::tokenize(firstcounters, ' '); - } else { - tokens = o2::utils::Str::tokenize(message, ' '); - } - if (tokens.size() != (CTPRunScalers::NCOUNTERS + 1)) { - if (tokens.size() == (CTPRunScalers::NCOUNTERS)) { - mNew = 0; - LOG(warning) << "v1 scaler size, using external orbit"; - } else { - LOG(error) << "Scalers size wrong:" << tokens.size() << " expected:" << CTPRunScalers::NCOUNTERS + 1; - return 1; + if (cls.clusterIndex == 0xff) { + std::cout << "ERROR class:" << cls.name << " NO CLUSTER INDEX" << std::endl; + ret++; } - } - double timeStamp = std::stold(tokens.at(0)); - std::time_t tt = timeStamp; - LOG(info) << "Processing scalers, all good, time:" << tokens.at(0) << " " << std::asctime(std::localtime(&tt)); - for (int i = 1; i < tokens.size(); i++) { - mCounters[i - 1] = std::stoull(tokens.at(i)); - if (i < (NRUNS + 1)) { - std::cout << mCounters[i - 1] << " "; + if (cls.descriptor == nullptr) { + std::cout << "ERROR class:" << cls.name << " NO DESCRIPTOR" << std::endl; + ret++; + } else { + descs[cls.descriptor->name] += 1; + // std::cout << "2 " << cls.descriptor << std::endl; } - } - std::cout << std::endl; - LOG(info) << "Counter size:" << tokens.size(); - // - for (uint32_t i = 0; i < NRUNS; i++) { - if ((mCounters[i] == 0) && (mActiveRunNumbers[i] == 0)) { - // not active - } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == mCounters[i])) { - // active , do scalers - LOG(info) << "Run continue:" << mCounters[i]; - addScalers(i, tt); - } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == 0)) { - LOG(info) << "Run started:" << mCounters[i]; - auto run = mRunsLoaded.find(mCounters[i]); - if (run != mRunsLoaded.end()) { - mActiveRunNumbers[i] = mCounters[i]; - mActiveRuns[i] = run->second; - mRunsLoaded.erase(run); - addScalers(i, tt); - } else { - LOG(error) << "Trying to start run which is not loaded:" << mCounters[i]; - } - } else if ((mCounters[i] == 0) && (mActiveRunNumbers[i] != 0)) { - if (mEOX != 1) { - LOG(error) << "Internal error in processMessage: mEOX != 1 expected 0: mEOX:" << mEOX; - } - LOG(info) << "Run stopped:" << mActiveRunNumbers[i]; - addScalers(i, tt); - mActiveRunNumbers[i] = 0; - mEOX = 0; - stopRun(i, tt); + if (cls.descriptorIndex == 0xff) { + std::cout << "ERROR class:" << cls.name << " NO DESCRIPTOR INDEX" << std::endl; + ret++; + } else { + // std::cout << "3 " << &mDescriptors[cls.descriptorIndex] << std::endl; } } - mEOX = 0; - printActiveRuns(); - return 0; -} -void CTPRunManager::printActiveRuns() const -{ - std::cout << "Active runs:"; - for (auto const& arun : mActiveRunNumbers) { - std::cout << arun << " "; - } - std::cout << " #loaded runs:" << mRunsLoaded.size(); - for (auto const& lrun : mRunsLoaded) { - std::cout << " " << lrun.second->cfg.getRunNumber(); - } - std::cout << std::endl; -} -int CTPRunManager::saveRunScalersToCCDB(int i) -{ - // data base - CTPActiveRun* run = mActiveRuns[i]; - using namespace std::chrono_literals; - std::chrono::seconds days3 = 259200s; - std::chrono::seconds min10 = 600s; - long time3days = std::chrono::duration_cast(days3).count(); - long time10min = std::chrono::duration_cast(min10).count(); - long tmin = run->timeStart - time10min; - long tmax = run->timeStop + time3days; - o2::ccdb::CcdbApi api; - map metadata; // can be empty - metadata["runNumber"] = std::to_string(run->cfg.getRunNumber()); - api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(&(run->scalers), mCCDBPathCTPScalers, metadata, tmin, tmax); - LOG(info) << "CTP scalers saved in ccdb:" << mCCDBHost << " run:" << run->cfg.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; - return 0; -} -int CTPRunManager::saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart) -{ - // data base - using namespace std::chrono_literals; - std::chrono::seconds days3 = 259200s; - std::chrono::seconds min10 = 600s; - long time3days = std::chrono::duration_cast(days3).count(); - long time10min = std::chrono::duration_cast(min10).count(); - long tmin = timeStart - time10min; - long tmax = timeStart + time3days; - o2::ccdb::CcdbApi api; - map metadata; // can be empty - metadata["runNumber"] = std::to_string(cfg->getRunNumber()); - api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(cfg, CCDBPathCTPConfig, metadata, tmin, tmax); - LOG(info) << "CTP config saved in ccdb:" << mCCDBHost << " run:" << cfg->getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; - return 0; -} -CTPConfiguration CTPRunManager::getConfigFromCCDB(long timestamp, std::string run) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(mCCDBHost); - map metadata; // can be empty - metadata["runNumber"] = run; - auto ctpconfigdb = mgr.getSpecific(CCDBPathCTPConfig, timestamp, metadata); - if (ctpconfigdb == nullptr) { - LOG(info) << "CTP config not in database, timestamp:" << timestamp; - } else { - // ctpconfigdb->printStream(std::cout); - LOG(info) << "CTP config found. Run:" << run; - } - return *ctpconfigdb; -} -CTPRunScalers CTPRunManager::getScalersFromCCDB(long timestamp, std::string run, bool& ok) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(mCCDBHost); - map metadata; // can be empty - metadata["runNumber"] = run; - auto ctpscalers = mgr.getSpecific(mCCDBPathCTPScalers, timestamp, metadata); - if (ctpscalers == nullptr) { - LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; - ok = 0; - } else { - // ctpscalers->printStream(std::cout); - ok = 1; - } - return *ctpscalers; -} -int CTPRunManager::loadScalerNames() -{ - if (CTPRunScalers::NCOUNTERS != CTPRunScalers::scalerNames.size()) { - LOG(fatal) << "NCOUNTERS:" << CTPRunScalers::NCOUNTERS << " different from names vector:" << CTPRunScalers::scalerNames.size(); - return 1; - } - // try to open files of no success use default - for (uint32_t i = 0; i < CTPRunScalers::scalerNames.size(); i++) { - mScalerName2Position[CTPRunScalers::scalerNames[i]] = i; - } - return 0; -} -void CTPRunManager::printCounters() -{ - int NDET = 18; - int NINPS = 48; - int NCLKFP = 7; - int NLTG_start = NRUNS; - int NCLKFP_start = NLTG_start + NDET * 32; - int NINPS_start = NCLKFP_start + 7; - int NCLS_start = NINPS_start + NINPS; - std::cout << "====> CTP counters:" << std::endl; - std::cout << "RUNS:" << std::endl; - int ipos = 0; - for (int i = 0; i < NRUNS; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - for (int i = 0; i < NDET; i++) { - std::cout << "LTG" << i + 1 << std::endl; - for (int j = NLTG_start + i * 32; j < NLTG_start + (i + 1) * 32; j++) { - std::cout << ipos << ":" << mCounters[j] << " "; - ipos++; + int iw = 0; + for (auto const& inp : inputs) { + if (inp.second == 0) { + iw++; + std::cout << "WARNING inputs:"; } - std::cout << std::endl; - } - std::cout << "BC40,BC240,Orbit,pulser, fastlm, busy,spare" << std::endl; - for (int i = NCLKFP_start; i < NCLKFP_start + 7; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "INPUTS:" << std::endl; - for (int i = NINPS_start; i < NINPS_start + NINPS; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS M Before" << std::endl; - for (int i = NCLS_start; i < NCLS_start + 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS M After" << std::endl; - for (int i = NCLS_start + 64; i < NCLS_start + 2 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 0 Before" << std::endl; - for (int i = NCLS_start + 2 * 64; i < NCLS_start + 3 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; + std::cout << inp.first << " " << inp.second << std::endl; } - std::cout << std::endl; - std::cout << "CLASS 0 After" << std::endl; - for (int i = NCLS_start + 3 * 64; i < NCLS_start + 4 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 1 Before" << std::endl; - for (int i = NCLS_start + 4 * 64; i < NCLS_start + 5 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << "CLASS 1 After" << std::endl; - for (int i = NCLS_start + 5 * 64; i < NCLS_start + 6 * 64; i++) { - std::cout << ipos << ":" << mCounters[i] << " "; - ipos++; - } - std::cout << std::endl; - std::cout << " REST:" << std::endl; - for (int i = NCLS_start + 6 * 64; i < mCounters.size(); i++) { - if ((ipos % 10) == 0) { - std::cout << std::endl; - std::cout << ipos << ":"; + std::cout << "Descriptors check:" << descs.size() << std::endl; + for (auto const& desc : descs) { + if (desc.second == 0) { + iw++; + std::cout << "WARNING descriptors:"; } - std::cout << mCounters[i] << " "; + // std::cout << (desc.first)->name << " " << desc.second << std::endl; + std::cout << (desc.first) << " " << desc.second << std::endl; } + std::cout << "CTP Config consistency checked. WARNINGS:" << iw << " ERRORS:" << ret << std::endl; + return ret; } +void CTPConfiguration::printConfigString() +{ + std::cout << mConfigString << std::endl; +}; +// int CTPInputsConfiguration::createInputsConfigFromFile(std::string& filename) { int ret = 0; @@ -1031,7 +1105,6 @@ int CTPInputsConfiguration::createInputsConfigFromFile(std::string& filename) ret++; } return ret; - ; } /// /// CTP inputs config @@ -1044,49 +1117,27 @@ void CTPInputsConfiguration::printStream(std::ostream& stream) const input.printStream(stream); } } +// +// EMBA - software generated input for EMC - Min Bias Accepted +// +const std::vector CTPInputsConfiguration::CTPInputsDefault = + { + CTPInput("MT0A", "FT0", 1), CTPInput("MT0C", "FT0", 2), CTPInput("MTVX", "FT0", 3), CTPInput("MTSC", "FT0", 4), CTPInput("MTCE", "FT0", 5), + CTPInput("MVBA", "FV0", 6), CTPInput("MVOR", "FV0", 7), CTPInput("MVIR", "FV0", 8), CTPInput("MVNC", "FV0", 9), CTPInput("MVCH", "FV0", 10), + CTPInput("0UCE", "FDD", 13), CTPInput("0USC", "FDD", 15), CTPInput("0UVX", "FDD", 16), CTPInput("0U0C", "FDD", 17), CTPInput("0U0A", "FDD", 18), + CTPInput("0DMC", "EMC", 14), CTPInput("0DJ1", "EMC", 41), CTPInput("0DG1", "EMC", 42), CTPInput("0DJ2", "EMC", 43), CTPInput("0DG2", "EMC", 44), + CTPInput("0EMC", "EMC", 21), CTPInput("0EJ1", "EMC", 37), CTPInput("0EG1", "EMC", 38), CTPInput("0EJ2", "EMC", 39), CTPInput("0EG2", "EMC", 40), + CTPInput("0PH0", "PHS", 22), CTPInput("1PHL", "PHS", 27), CTPInput("1PHH", "PHS", 28), CTPInput("1PHL", "PHM", 29), + CTPInput("1ZED", "ZDC", 25), CTPInput("1ZNC", "ZDC", 26), CTPInput("EMBA", "EMC", 48)}; void CTPInputsConfiguration::initDefaultInputConfig() { - defaultInputConfig.CTPInputs.push_back(CTPInput("MT0A", "FT0", 1)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MT0C", "FT0", 2)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTVX", "FT0", 3)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTSC", "FT0", 4)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MTCE", "FT0", 5)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("MVBA", "FV0", 6)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVOR", "FV0", 7)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVIR", "FV0", 8)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVNC", "FV0", 9)); - defaultInputConfig.CTPInputs.push_back(CTPInput("MVCH", "FV0", 10)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0UCE", "FT0", 13)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0USC", "FT0", 15)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0UVX", "FT0", 16)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0U0C", "FT0", 17)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0U0A", "FT0", 18)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0DMC", "EMC", 14)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DJ1", "EMC", 41)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DG1", "EMC", 42)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DJ2", "EMC", 43)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0DG2", "EMC", 44)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0EMC", "EMC", 21)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EJ1", "EMC", 37)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EG1", "EMC", 38)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EJ2", "EMC", 39)); - defaultInputConfig.CTPInputs.push_back(CTPInput("0EG2", "EMC", 40)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("0PH0", "PHS", 22)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHL", "PHS", 27)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHH", "PHS", 28)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1PHL", "PHM", 29)); - // - defaultInputConfig.CTPInputs.push_back(CTPInput("1ZED", "ZDC", 25)); - defaultInputConfig.CTPInputs.push_back(CTPInput("1ZNC", "ZDC", 26)); + defaultInputConfig.CTPInputs = CTPInputsConfiguration::CTPInputsDefault; } -std::string CTPInputsConfiguration::getInputNameFromIndex(int index) +/// Return input name from default inputs configuration. +/// Take into account convention that L0 inputs has (index+100) in the first version of CTP config file (*.rcfg) +std::string CTPInputsConfiguration::getInputNameFromIndex100(uint32_t index) { - int indexcor = index; + uint32_t indexcor = index; if (index > 100) { indexcor = index - 100; } @@ -1102,17 +1153,98 @@ std::string CTPInputsConfiguration::getInputNameFromIndex(int index) LOG(info) << "Input with index:" << index << " not in deafult input config"; return ""; } +/// Return input name from default inputs configuration. +/// Index has to be in range [1::48] +std::string CTPInputsConfiguration::getInputNameFromIndex(uint32_t index) +{ + if (index > o2::ctp::CTP_NINPUTS) { + LOG(warn) << "getInputNameFRomIndex: index too big:" << index; + return "none"; + } + for (auto& inp : o2::ctp::CTPInputsConfiguration::CTPInputsDefault) { + if (inp.getIndex() == index) { + std::string name = inp.name; + return name; + } + } + LOG(info) << "Input with index:" << index << " not in deafult input config"; + return "none"; +} int CTPInputsConfiguration::getInputIndexFromName(std::string& name) { std::string namecorr = name; - if (name[0] == '0') { - name.substr(1, name.size() - 1); + if ((name[0] == '0') || (name[0] == 'M') || (name[0] == '1')) { + namecorr = namecorr.substr(1, namecorr.size() - 1); + } else { + LOG(warn) << "Input name without level:" << name; } - for (auto& inp : defaultInputConfig.CTPInputs) { + for (auto& inp : o2::ctp::CTPInputsConfiguration::CTPInputsDefault) { if (inp.name.find(namecorr) != std::string::npos) { return inp.getIndex(); } } - LOG(info) << "Input with name:" << name << " not in default input config"; + LOG(warn) << "Input with name:" << name << " not in default input config"; return 0xff; } + +int CtpCfg::readAndSave(std::string& path) +{ + std::string file = path + filename; + std::ifstream ctpcfg(file); + if (ctpcfg.is_open()) { + std::string line; + while (std::getline(ctpcfg, line)) { + o2::utils::Str::trim(line); + if (line.size() == 0) { + continue; + } + if (line[0] == '#') { + continue; + } + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + size_t ntokens = tokens.size(); + if (ntokens < 2) { + LOG(warn) << "Not enough tokens"; + continue; + } + if (tokens[0].find("TForbits") != std::string::npos) { + TFOrbits = std::atol(tokens[1].c_str()); + } else if (tokens[0].find("ccdb") != std::string::npos) { + ccdb = std::atoi(tokens[1].c_str()); + } else if (tokens[0].find("orbitshift") != std::string::npos) { + orbitShift = std::atol(tokens[1].c_str()); + } else if (tokens[0].find("ir_inputs") != std::string::npos) { + irInputs_1_24 = std::stoul(tokens[2].c_str(), nullptr, 16); + irInputs_25_48 = std::stoul(tokens[1].c_str(), nullptr, 16); + } else { + LOG(warn) << " Token not found:" << tokens[0]; + } + } + LOG(warn) << "Open file success:" << file; + } else { + LOG(warn) << "Can not open file:" << file; + return 1; + } + return 0; +} +std::vector CtpCfg::listOfUsedInputs() +{ + std::cout << std::hex << "0x" << irInputs_1_24 << " " << irInputs_25_48 << std::dec << std::endl; + std::vector inputList; + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_1_24) { + inputList.push_back(i); + } + } + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_25_48) { + inputList.push_back(i + 24); + } + } + return inputList; +} +std::ostream& o2::ctp::operator<<(std::ostream& in, const o2::ctp::CTPConfiguration& conf) +{ + conf.printStream(in); + return in; +} \ No newline at end of file diff --git a/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h b/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h index c3c657659d40f..ac2a83d31edda 100644 --- a/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h +++ b/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h @@ -14,6 +14,7 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class std::bitset < 48> + ; #pragma link C++ class o2::ctp::CTPDigit + ; #pragma link C++ class vector < o2::ctp::CTPDigit> + ; #pragma link C++ class o2::ctp::CTPInputDigit + ; @@ -43,9 +44,9 @@ #pragma link C++ class o2::ctp::CTPScalerRecordO2 + ; #pragma link C++ class vector < o2::ctp::CTPScalerRecordO2> + ; #pragma link C++ class o2::ctp::CTPRunScalers + ; -#pragma link C++ class o2::ctp::CTPRunManager + ; #pragma link C++ class o2::ctp::LumiInfo + ; #pragma link C++ class vector < o2::ctp::LumiInfo> + ; +#pragma link C++ class o2::ctp::CTPRateFetcher + ; #pragma link C++ struct o2::ctp::CTFHeader + ; #pragma link C++ struct o2::ctp::CTF + ; @@ -54,4 +55,6 @@ #pragma link C++ class o2::ctp::TriggerOffsetsParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::ctp::TriggerOffsetsParam> + ; +#pragma link C++ class o2::ctp::CtpCfg + ; + #endif diff --git a/DataFormats/Detectors/CTP/src/Digits.cxx b/DataFormats/Detectors/CTP/src/Digits.cxx index aa44216270721..205225a35500a 100644 --- a/DataFormats/Detectors/CTP/src/Digits.cxx +++ b/DataFormats/Detectors/CTP/src/Digits.cxx @@ -30,13 +30,13 @@ void CTPDigit::printStream(std::ostream& stream) const void CTPDigit::setInputMask(gbtword80_t mask) { - for (int i = 0; i < CTP_NINPUTS; i++) { + for (uint32_t i = 0; i < CTP_NINPUTS; i++) { CTPInputMask[i] = mask[i]; } } void CTPDigit::setClassMask(gbtword80_t mask) { - for (int i = 0; i < CTP_NCLASSES; i++) { + for (uint32_t i = 0; i < CTP_NCLASSES; i++) { CTPClassMask[i] = mask[i]; } } diff --git a/DataFormats/Detectors/CTP/src/LumiInfo.cxx b/DataFormats/Detectors/CTP/src/LumiInfo.cxx new file mode 100644 index 0000000000000..08f8e5e7b0279 --- /dev/null +++ b/DataFormats/Detectors/CTP/src/LumiInfo.cxx @@ -0,0 +1,23 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LumiInfo.cxx + +#include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" +#include + +using namespace o2::ctp; + +void LumiInfo::printInputs() const +{ + LOG(info) << "Lumi inp1:" << inp1 << ":" << o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(inp1) << " inp2:" << inp2 << ":" << o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(inp2); +} diff --git a/DataFormats/Detectors/CTP/src/Scalers.cxx b/DataFormats/Detectors/CTP/src/Scalers.cxx index 6f443fd5a77c4..256722fc1e5ae 100644 --- a/DataFormats/Detectors/CTP/src/Scalers.cxx +++ b/DataFormats/Detectors/CTP/src/Scalers.cxx @@ -21,9 +21,13 @@ using namespace o2::ctp; void errorCounters::printStream(std::ostream& stream) const { - stream << "Counter errorrs: lmB:" << lmB << " l0B:" << l0B << " l1B:" << l1B << " lmA:" << lmA << " l0A:" << l0A << " l1A:" << l1A; - stream << " lmBlmA:" << lmBlmA << " lmAl0B:" << lmAl0B << " l0Bl0A:" << l0Bl0A << " l0Al1B: " << l0Al1B << " l1Bl1A:" << l1Bl1A; - stream << std::endl; + // stream << "Counter warnings diff 1 lmBlmA:" << lmBlmAd1 << " lmAl0B:" << lmAl0Bd1 << " l0Bl0A:" << l0Bl0Ad1 << " l0Al1B: " << l0Al1Bd1 << " l1Bl1A:" << l1Bl1Ad1; + // stream << std::endl; + if (lmB + l0B + l1B + lmA + l0A + l1A + lmBlmA + lmAl0B + l0Bl0A + l0Al1B + l1Bl1A) { + stream << "Counter errorrs: lmB:" << lmB << " l0B:" << l0B << " l1B:" << l1B << " lmA:" << lmA << " l0A:" << l0A << " l1A:" << l1A; + stream << " lmBlmA:" << lmBlmA << " lmAl0B:" << lmAl0B << " l0Bl0A:" << l0Bl0A << " l0Al1B: " << l0Al1B << " l1Bl1A:" << l1Bl1A; + stream << std::endl; + } } void CTPScalerRaw::printStream(std::ostream& stream) const { @@ -65,7 +69,8 @@ void CTPScalerRecordRaw::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -77,7 +82,8 @@ void CTPScalerRecordO2::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -87,7 +93,7 @@ void CTPScalerRecordO2::printFromZero(std::ostream& stream, CTPScalerRecordO2& r stream << "printFromZero Orbit:" << intRecord.orbit - record0.intRecord.orbit << " BC:" << intRecord.bc; stream << " miliSeconds:" << std::setprecision(20) << epochTime - record0.epochTime << std::endl; // this-record0 - for (int i = 0; i < scalers.size(); i++) { + for (uint32_t i = 0; i < scalers.size(); i++) { scalers[i].printFromZero(stream, record0.scalers[i]); } stream << std::endl; @@ -128,7 +134,7 @@ void CTPRunScalers::printFromZero(std::ostream& stream) const void CTPRunScalers::printClasses(std::ostream& stream) const { stream << "CTP classes:"; - for (int i = 0; i < mClassMask.size(); i++) { + for (uint32_t i = 0; i < mClassMask.size(); i++) { if (mClassMask[i]) { stream << " " << i; } @@ -145,6 +151,24 @@ std::vector CTPRunScalers::getClassIndexes() const } return indexes; } +// cls counted from 0 +int CTPRunScalers::getScalerIndexForClass(uint32_t cls) const +{ + if (cls >= 64) { + LOG(error) << "Class index out of range:" << cls; + return 255; + } + std::vector clslist = getClassIndexes(); + int i = 0; + for (auto const& clsl : clslist) { + if (cls == clsl) { + return i; + } + i++; + } + LOG(error) << " Class not found:" << cls; + return 255; +} int CTPRunScalers::readScalers(const std::string& rawscalers) { LOG(info) << "Loading CTP scalers."; @@ -152,7 +176,7 @@ int CTPRunScalers::readScalers(const std::string& rawscalers) int ret = 0; int level = 0; std::string line; - int nclasses = 0; + uint32_t nclasses = 0; int nlines = 0; while (std::getline(iss, line)) { o2::utils::Str::trim(line); @@ -172,7 +196,7 @@ int CTPRunScalers::readScalers(const std::string& rawscalers) } return 0; } -int CTPRunScalers::processScalerLine(const std::string& line, int& level, int& nclasses) +int CTPRunScalers::processScalerLine(const std::string& line, int& level, uint32_t& nclasses) { //std::cout << "Processing line" << std::endl; if (line.size() == 0) { @@ -205,13 +229,13 @@ int CTPRunScalers::processScalerLine(const std::string& line, int& level, int& n return 2; } else { mRunNumber = std::stol(tokens[0]); - int numofclasses = std::stoi(tokens[1]); + uint32_t numofclasses = std::stoi(tokens[1]); if ((numofclasses + 2) != ntokens) { LOG(error) << "Wrong syntax of second line in CTP scalers"; return 3; } mClassMask.reset(); - for (int i = 0; i < numofclasses; i++) { + for (uint32_t i = 0; i < numofclasses; i++) { int index = std::stoi(tokens[i + 2]); mClassMask[index] = 1; } @@ -273,19 +297,27 @@ int CTPRunScalers::convertRawToO2() overflows[i] = {0, 0, 0, 0, 0, 0}; } } + // Input overflows + std::array overflowsInputs = {48 * 0}; errorCounters eCnts; // 1st o2 rec is just copy CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows); + copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); int j = 1; for (uint32_t i = 1; i < mScalerRecordRaw.size(); i++) { //update overflows int ret = updateOverflows(mScalerRecordRaw[i - 1], mScalerRecordRaw[i], overflows); + // for(int k = 0; k < mClassMask.size(); k++) { + // if(mClassMask[k]) { + // LOG(info) << i << " " << k << " " << overflows[k][0] << " " << overflows[k][1] << " " << overflows[k][2] << " " << overflows[k][3] << " " << overflows[k][4] << " " << overflows[k][5]; + // } + // } // if (ret == 0) { CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows); + ret = updateOverflowsInps(mScalerRecordRaw[i - 1], mScalerRecordRaw[i], overflowsInputs); + copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); // Check consistency checkConsistency(mScalerRecordO2[j - 1], mScalerRecordO2[j], eCnts); @@ -295,7 +327,7 @@ int CTPRunScalers::convertRawToO2() eCnts.printStream(std::cout); return 0; } -int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows) +int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows) { if (rawrec.scalers.size() != (mClassMask.count())) { LOG(error) << "Inconsistent scaler record size:" << rawrec.scalers.size() << " Expected:" << mClassMask.count(); @@ -313,6 +345,10 @@ int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTP o2scal.createCTPScalerO2FromRaw(rawscal, classesoverflows[k]); o2rec.scalers.push_back(o2scal); } + for (uint32_t i = 0; i < rawrec.scalersInps.size(); i++) { + uint64_t inpo2 = (uint64_t)(rawrec.scalersInps[i]) + 0xffffffffull * (uint64_t)(overflows[i]); + o2rec.scalersInps.push_back(inpo2); + } return 0; } int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& scal1, errorCounters& eCnts) const @@ -365,24 +401,34 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // LMB >= LMA >= L0B >= L0A >= L1B >= L1A: 5 relations // broken for classes started at L0 // - if ((scal1.lmAfter - scal0.lmAfter) > (scal1.lmBefore - scal0.lmBefore)) { + int64_t difThres = 6; + int64_t dif = (scal1.lmAfter - scal0.lmAfter) - (scal1.lmBefore - scal0.lmBefore); + if (dif <= difThres) { + eCnts.lmBlmAd1++; + } else if (dif > difThres) { eCnts.lmBlmA++; if (eCnts.lmBlmA < eCnts.MAXPRINT) { - LOG(error) << "LMA > LMB error:" << ((scal1.lmAfter - scal0.lmAfter) - (scal1.lmBefore - scal0.lmBefore)); + LOG(error) << "LMA > LMB error:" << dif; } ret++; } - if ((scal1.l0After - scal0.l0After) > (scal1.l0Before - scal0.l0Before)) { + dif = (scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before); + if (dif <= difThres) { + eCnts.l0Bl0Ad1++; + } else if (dif > difThres) { eCnts.l0Bl0A++; if (eCnts.l0Bl0A < eCnts.MAXPRINT) { - LOG(error) << "L0A > L0B error:" << ((scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before)); + LOG(error) << "L0A > L0B error:" << dif; } ret++; } - if ((scal1.l1After - scal0.l1After) > (scal1.l1Before - scal0.l1Before)) { + dif = (scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before); + if (dif <= difThres) { + eCnts.l1Bl1Ad1++; + } else if (dif > difThres) { eCnts.l1Bl1A++; if (eCnts.l1Bl1A < eCnts.MAXPRINT) { - LOG(error) << "L1A > L1B error:" << ((scal1.l0After - scal0.l0After) - (scal1.l0Before - scal0.l0Before)); + LOG(error) << "L1A > L1B error:" << dif; } ret++; } @@ -390,10 +436,15 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // LOG(warning) << "L0B > LMA ok if L0 class."; // ret++; } - if ((scal1.l1Before - scal0.l1Before) > (scal1.l0After - scal0.l0After)) { + dif = (scal1.l1Before - scal0.l1Before) - (scal1.l0After - scal0.l0After); + // LOG(info) << "L1B L0A " << dif << " " << scal1.l1Before << " " << scal1.l0After << " " << scal0.l1Before << " " << scal0.l0After; + if (dif <= difThres) { + eCnts.l0Al1Bd1++; + } else if (dif > difThres) { eCnts.l0Al1B++; if (eCnts.l0Al1B < eCnts.MAXPRINT) { - LOG(error) << "L1B > L0A Before error:" << ((scal1.l1Before - scal0.l1Before) - (scal1.l0After - scal0.l0After)); + // LOG(error) << "L1B > L0A Before error:" << dif << " " << scal1.l1Before << " " << scal1.l0After << " " << scal0.l1Before << " " << scal0.l0After; + LOG(warning) << "L1B > L0A Before error:" << dif; } ret++; } @@ -414,7 +465,7 @@ int CTPRunScalers::updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScal LOG(warning) << "rec0 orbit:" << rec0.intRecord.orbit << "> rec1 orbit:" << rec1.intRecord.orbit << " skipping this record"; return 1; } - for (int i = 0; i < rec0.scalers.size(); i++) { + for (uint32_t i = 0; i < rec0.scalers.size(); i++) { int k = (getClassIndexes())[i]; updateOverflows(rec0.scalers[i], rec1.scalers[i], classesoverflows[k]); } @@ -422,10 +473,11 @@ int CTPRunScalers::updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScal } int CTPRunScalers::checkConsistency(const CTPScalerRecordO2& rec0, const CTPScalerRecordO2& rec1, errorCounters& eCnts) const { - for (int i = 0; i < rec0.scalers.size(); i++) { - checkConsistency(rec0.scalers[i], rec1.scalers[i], eCnts); + int ret = 0; + for (uint32_t i = 0; i < rec0.scalers.size(); i++) { + ret += checkConsistency(rec0.scalers[i], rec1.scalers[i], eCnts); } - return 0; + return ret; } int CTPRunScalers::updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw& scal1, std::array& overflow) const { @@ -452,6 +504,33 @@ int CTPRunScalers::updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw //std::cout << std::endl; return 0; } +// +int CTPRunScalers::updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const +{ + static int iPrint = 0; + uint32_t NINPS = 48; + if (rec0.scalersInps.size() < NINPS) { + if (iPrint < 1) { + LOG(warning) << "Input scalers not available. Size:" << rec0.scalersInps.size(); + iPrint++; + } + return 1; + } + if (rec1.scalersInps.size() < NINPS) { + if (iPrint < 1) { + LOG(warning) << "Input scalers not available. Size:" << rec0.scalersInps.size(); + iPrint++; + } + return 2; + } + for (uint32_t i = 0; i < NINPS; i++) { + if (rec0.scalersInps[i] > rec1.scalersInps[i]) { + overflow[i] += 1; + } + } + return 0; +} +// int CTPRunScalers::printRates() { if (mScalerRecordO2.size() == 0) { @@ -461,14 +540,14 @@ int CTPRunScalers::printRates() LOG(info) << "Scaler rates for run:" << mRunNumber; CTPScalerRecordO2* scalrec0 = &mScalerRecordO2[0]; uint32_t orbit0 = scalrec0->intRecord.orbit; - for (int i = 1; i < mScalerRecordO2.size(); i++) { + for (uint32_t i = 1; i < mScalerRecordO2.size(); i++) { CTPScalerRecordO2* scalrec1 = &mScalerRecordO2[i]; double_t tt = (double_t)(scalrec1->intRecord.orbit - scalrec0->intRecord.orbit); double_t tinrun = (double_t)(scalrec1->intRecord.orbit - orbit0); tt = tt * 88e-6; tinrun = tinrun * 88e-6; std::cout << "==> Time wrt to SOR [s]:" << tinrun << " time intervale[s]:" << tt << std::endl; - for (int j = 0; j < scalrec1->scalers.size(); j++) { + for (uint32_t j = 0; j < scalrec1->scalers.size(); j++) { CTPScalerO2* s0 = &(scalrec0->scalers[j]); CTPScalerO2* s1 = &(scalrec1->scalers[j]); double_t rMB = (s1->lmBefore - s0->lmBefore) / tt; @@ -497,7 +576,7 @@ int CTPRunScalers::printIntegrals() double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; LOG(info) << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; - for (int i = 0; i < mScalerRecordO2[0].scalers.size(); i++) { + for (uint32_t i = 0; i < mScalerRecordO2[0].scalers.size(); i++) { std::cout << i << " LMB " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmBefore - mScalerRecordO2[0].scalers[i].lmBefore << std::endl; std::cout << i << " LMA " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].lmAfter - mScalerRecordO2[0].scalers[i].lmAfter << std::endl; std::cout << i << " L0B " << mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[i].l0Before - mScalerRecordO2[0].scalers[i].l0Before << std::endl; @@ -507,10 +586,62 @@ int CTPRunScalers::printIntegrals() } return 0; } - +// +// Input counting 1..48 +int CTPRunScalers::printInputRateAndIntegral(int inp) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalersInps[inp - 1] - mScalerRecordO2[0].scalersInps[inp - 1]; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Input " << inp << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + return 0; +} +// Prints class before counters for lumi +// Class counting 1..64 +int CTPRunScalers::printClassBRateAndIntegralII(int iclsindex) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + int iscalerindex = getScalerIndexForClass(iclsindex); + if (iscalerindex != 255) { + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[iscalerindex].lmBefore - mScalerRecordO2[0].scalers[iscalerindex].lmBefore; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Class index" << iclsindex << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + return 0; + } + return 1; +} +// Prints class before counters for lumi +// Scaler Index Class counting 1..64 +// getScalerIndexForClass(int cls) shpild be called before to convert class index to scaler class index +int CTPRunScalers::printClassBRateAndIntegral(int iclsscalerindex) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + { + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[iclsscalerindex - 1].lmBefore - mScalerRecordO2[0].scalers[iclsscalerindex - 1].lmBefore; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Class scaler index:" << iclsscalerindex << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + } + return 0; +} +// void CTPRunScalers::printLMBRateVsT() const { - for (int i = 1; i < mScalerRecordO2.size(); i++) { // loop over time + for (uint32_t i = 1; i < mScalerRecordO2.size(); i++) { // loop over time auto prev = &mScalerRecordO2[i - 1]; auto curr = &mScalerRecordO2[i]; double_t tt = (double_t)(curr->intRecord.orbit - prev->intRecord.orbit); @@ -526,10 +657,82 @@ void CTPRunScalers::printLMBRateVsT() const } } } - +// +uint64_t CTPRunScalers::getLumiNoPuCorr(int classindex, int type) const +{ + if (type < 7) { + const auto s0 = mScalerRecordO2[0].scalers[classindex]; + const auto s1 = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore); + case 2: + return (s1.lmAfter - s0.lmAfter); + case 3: + return (s1.l0Before - s0.l0Before); + case 4: + return (s1.l0After - s0.l0After); + case 5: + return (s1.l1Before - s0.l1Before); + case 6: + return (s1.l1After - s0.l1After); + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + auto s0 = mScalerRecordO2[0].scalersInps[classindex]; // type CTPScalerO2* + auto s1 = mScalerRecordO2[mScalerRecordO2.size() - 1].scalersInps[classindex]; + return (s1 - s0); + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } +}; +// +std::vector> CTPRunScalers::getRatesForIndex(int classindex, int type) const +{ + std::vector> scals; + for (int i = 0; i < mScalerRecordO2.size() - 1; i++) { + double_t diff = 0; + // double_t timeDiff = mScalerRecordO2[i + 1].epochTime - mScalerRecordO2[i].epochTime; + double_t timeDiff = (mScalerRecordO2[i + 1].intRecord.orbit - mScalerRecordO2[i].intRecord.orbit) * o2::constants::lhc::LHCOrbitMUS / 1.e6; + if (type < 7) { + const auto s0 = mScalerRecordO2[i].scalers[classindex]; + const auto s1 = mScalerRecordO2[i + 1].scalers[classindex]; + if (type == 1) { + diff = s1.lmBefore - s0.lmBefore; + } else if (type == 2) { + diff = s1.lmAfter - s0.lmAfter; + } else if (type == 3) { + diff = s1.l0Before - s0.l0Before; + } else if (type == 4) { + diff = s1.l0After - s0.l0After; + } else if (type == 5) { + diff = s1.l1Before - s0.l1Before; + } else if (type == 6) { + diff = s1.l1After - s0.l1After; + } else { + LOG(error) << "Wrong type:" << type; + return scals; // wrong type + } + } else if (type == 7) { + auto s0 = mScalerRecordO2[i].scalersInps[classindex]; // type CTPScalerO2* + auto s1 = mScalerRecordO2[i + 1].scalersInps[classindex]; + diff = s1 - s0; + } else { + LOG(error) << "Wrong type:" << type; + return scals; // wrong type + } + scals.emplace_back(std::pair{diff, timeDiff}); + } + return scals; +}; // returns the pair of global (levelled) interaction rate, as well as instantaneous interpolated // rate in Hz at a certain orbit number within the run -std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type) const +// type - 7 : inputs +// type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a +std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -540,31 +743,70 @@ std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, // orbit numbers // then we can use binary search to find the right entries - auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), orbit, [&](CTPScalerRecordO2 const& a, uint32_t value) { return a.intRecord.orbit < value; }); - auto nextindex = iter - mScalerRecordO2.begin(); // this points to the first index that has orbit greater or equal to given orbit + auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), orbit, [&](CTPScalerRecordO2 const& a, uint32_t value) { return a.intRecord.orbit <= value; }); + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); // this points to the first index that has orbit greater or equal to given orbit auto calcRate = [&](auto index1, auto index2) -> double { - auto next = &mScalerRecordO2[index2]; - auto prev = &mScalerRecordO2[index1]; - auto timedelta = (next->intRecord.orbit - prev->intRecord.orbit) * 88.e-6; // converts orbits into time - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); - return (s1->lmBefore - s0->lmBefore) / timedelta; // rate --> should be made better by selecting on type + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time + if (type < 7) { + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore) / timedelta; + case 2: + return (s1.lmAfter - s0.lmAfter) / timedelta; + case 3: + return (s1.l0Before - s0.l0Before) / timedelta; + case 4: + return (s1.l0After - s0.l0After) / timedelta; + case 5: + return (s1.l1Before - s0.l1Before) / timedelta; + case 6: + return (s1.l1After - s0.l1After) / timedelta; + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; + return (s1 - s0) / timedelta; + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } }; - - if (nextindex == 0 || nextindex == mScalerRecordO2.size()) { + // qc flag decides what to return if time outside run + if (nextindex == 0) { // orbit is out of bounds - LOG(info) << "query orbit " << orbit << " out of bounds; Just returning the global rate"; - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query orbit " << orbit << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query orbit " << orbit << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } } else { - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); } return std::make_pair(-1., -1.); } // returns the pair of global (levelled) interaction rate, as well as instantaneous interpolated // rate in Hz at a certain orbit number within the run -std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type) const +// type - 7 : inputs +// type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a +std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -575,31 +817,91 @@ std::pair CTPRunScalers::getRateGivenT(double timestamp, int cla // orbit numbers // then we can use binary search to find the right entries - auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), timestamp, [&](CTPScalerRecordO2 const& a, uint32_t value) { return a.epochTime < value; }); - auto nextindex = iter - mScalerRecordO2.begin(); // this points to the first index that has orbit greater or equal to given orbit + auto iter = std::lower_bound(mScalerRecordO2.begin(), mScalerRecordO2.end(), timestamp, [&](CTPScalerRecordO2 const& a, double value) { return a.epochTime <= value; }); + // this points to the first index that has orbit greater to given orbit; + // If this is 0, it means that the above condition was false from the beginning, basically saying that the timestamp is below any of the ScalerRecords' orbits. + // If this is mScalerRecordO2.size(), it means mScalerRecordO2.end() was returned, condition was met throughout all ScalerRecords, basically saying the timestamp is above any of the ScalarRecordss orbits. + auto nextindex = std::distance(mScalerRecordO2.begin(), iter); auto calcRate = [&](auto index1, auto index2) -> double { - auto next = &mScalerRecordO2[index2]; - auto prev = &mScalerRecordO2[index1]; - auto timedelta = (next->intRecord.orbit - prev->intRecord.orbit) * 88.e-6; // converts orbits into time - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); - return (s1->lmBefore - s0->lmBefore) / timedelta; // rate --> should be made better by selecting on type + const auto& snext = mScalerRecordO2[index2]; + const auto& sprev = mScalerRecordO2[index1]; + auto timedelta = (snext.intRecord.orbit - sprev.intRecord.orbit) * 88.e-6; // converts orbits into time + // std::cout << "timedelta:" << timedelta << std::endl; + if (type < 7) { + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; + switch (type) { + case 1: + return (s1.lmBefore - s0.lmBefore) / timedelta; + case 2: + return (s1.lmAfter - s0.lmAfter) / timedelta; + case 3: + return (s1.l0Before - s0.l0Before) / timedelta; + case 4: + return (s1.l0After - s0.l0After) / timedelta; + case 5: + return (s1.l1Before - s0.l1Before) / timedelta; + case 6: + return (s1.l1After - s0.l1After) / timedelta; + default: + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } + } else if (type == 7) { + // LOG(info) << "doing input:"; + auto s0 = sprev.scalersInps[classindex]; // type CTPScalerO2* + auto s1 = snext.scalersInps[classindex]; + return (s1 - s0) / timedelta; + } else { + LOG(error) << "Wrong type:" << type; + return -1; // wrong type + } }; - - if (nextindex == 0 || nextindex == mScalerRecordO2.size()) { + if (nextindex == 0) { // orbit is out of bounds - LOG(info) << "query timestamp " << timestamp << " out of bounds; Just returning the global rate"; - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " before first record; Returning the first rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* first rate */ calcRate(0, 1)); + } + } else if (nextindex == mScalerRecordO2.size()) { + if (qc == 0) { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Just returning the global rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ -1); + } else { + LOG(info) << "query timestamp " << (long)timestamp << " after last record; Returning the last rate"; + return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* last rate */ calcRate(mScalerRecordO2.size() - 2, mScalerRecordO2.size() - 1)); + } } else { - return std::make_pair(/*global mean rate*/ calcRate(0, mScalerRecordO2.size() - 1), /* current rate */ calcRate(nextindex - 1, nextindex)); } return std::make_pair(-1., -1.); } - +// Offset orbit of all records +// +int CTPRunScalers::addOrbitOffset(uint32_t offset) +{ + int over = 0; + LOG(info) << "Subtracting from orbit " << offset; + for (auto& screc : mScalerRecordRaw) { + uint32_t orbit = screc.intRecord.orbit; + uint32_t orbitnew = 0; + orbitnew = orbit - offset; + if (orbit < offset) { + over++; + } + screc.intRecord.orbit = orbitnew; + } + if (over != 0 && over != mScalerRecordRaw.size()) { + LOG(warning) << "Orbit overflow inside run. Run:" << mRunNumber; + } + return 0; +} std::vector CTPRunScalers::scalerNames = { "runn0", "runn1", "runn2", "runn3", "runn4", "runn5", "runn6", "runn7", "runn8", "runn9", "runn10", "runn11", "runn12", "runn13", "runn14", "runn15", "ltg1_ORB", "ltg1_HB", "ltg1_HBr", "ltg1_HC", "ltg1_PH", "ltg1_PP", "ltg1_CAL", "ltg1_SOT", "ltg1_EOT", "ltg1_SOC", "ltg1_EOC", "ltg1_TF", "ltg1_FERST", "ltg1_RT", "ltg1_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg1_GAP1", "ltg1_GAP2", "ltg1_TPC_sync", "ltg1_TPC_rst", "ltg1_TOF", "ltg2_ORB", "ltg2_HB", "ltg2_HBr", "ltg2_HC", "ltg2_PH", "ltg2_PP", "ltg2_CAL", "ltg2_SOT", "ltg2_EOT", "ltg2_SOC", "ltg2_EOC", "ltg2_TF", "ltg2_FERST", "ltg2_RT", "ltg2_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg2_GAP1", "ltg2_GAP2", "ltg2_TPC_sync", "ltg2_TPC_rst", "ltg2_TOF", "ltg3_ORB", "ltg3_HB", "ltg3_HBr", "ltg3_HC", "ltg3_PH", "ltg3_PP", "ltg3_CAL", "ltg3_SOT", "ltg3_EOT", "ltg3_SOC", "ltg3_EOC", "ltg3_TF", "ltg3_FERST", "ltg3_RT", "ltg3_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg3_GAP1", "ltg3_GAP2", "ltg3_TPC_sync", "ltg3_TPC_rst", "ltg3_TOF", "ltg4_ORB", "ltg4_HB", "ltg4_HBr", "ltg4_HC", "ltg4_PH", "ltg4_PP", "ltg4_CAL", "ltg4_SOT", "ltg4_EOT", "ltg4_SOC", "ltg4_EOC", "ltg4_TF", "ltg4_FERST", "ltg4_RT", "ltg4_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg4_GAP1", "ltg4_GAP2", "ltg4_TPC_sync", "ltg4_TPC_rst", "ltg4_TOF", "ltg5_ORB", "ltg5_HB", "ltg5_HBr", "ltg5_HC", "ltg5_PH", "ltg5_PP", "ltg5_CAL", "ltg5_SOT", "ltg5_EOT", "ltg5_SOC", "ltg5_EOC", "ltg5_TF", "ltg5_FERST", "ltg5_RT", "ltg5_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg5_GAP1", "ltg5_GAP2", "ltg5_TPC_sync", "ltg5_TPC_rst", "ltg5_TOF", "ltg6_ORB", "ltg6_HB", "ltg6_HBr", "ltg6_HC", "ltg6_PH", "ltg6_PP", "ltg6_CAL", "ltg6_SOT", "ltg6_EOT", "ltg6_SOC", "ltg6_EOC", "ltg6_TF", "ltg6_FERST", "ltg6_RT", "ltg6_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg6_GAP1", "ltg6_GAP2", "ltg6_TPC_sync", "ltg6_TPC_rst", "ltg6_TOF", "ltg7_ORB", "ltg7_HB", "ltg7_HBr", "ltg7_HC", "ltg7_PH", "ltg7_PP", "ltg7_CAL", "ltg7_SOT", "ltg7_EOT", "ltg7_SOC", "ltg7_EOC", "ltg7_TF", "ltg7_FERST", "ltg7_RT", "ltg7_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg7_GAP1", "ltg7_GAP2", "ltg7_TPC_sync", "ltg7_TPC_rst", "ltg7_TOF", "ltg8_ORB", "ltg8_HB", "ltg8_HBr", "ltg8_HC", "ltg8_PH", "ltg8_PP", "ltg8_CAL", "ltg8_SOT", "ltg8_EOT", "ltg8_SOC", "ltg8_EOC", "ltg8_TF", "ltg8_FERST", "ltg8_RT", "ltg8_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg8_GAP1", "ltg8_GAP2", "ltg8_TPC_sync", "ltg8_TPC_rst", "ltg8_TOF", "ltg9_ORB", "ltg9_HB", "ltg9_HBr", "ltg9_HC", "ltg9_PH", "ltg9_PP", "ltg9_CAL", "ltg9_SOT", "ltg9_EOT", "ltg9_SOC", "ltg9_EOC", "ltg9_TF", "ltg9_FERST", "ltg9_RT", "ltg9_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg9_GAP1", "ltg9_GAP2", "ltg9_TPC_sync", "ltg9_TPC_rst", "ltg9_TOF", "ltg10_ORB", "ltg10_HB", "ltg10_HBr", "ltg10_HC", "ltg10_PH", "ltg10_PP", "ltg10_CAL", "ltg10_SOT", "ltg10_EOT", "ltg10_SOC", "ltg10_EOC", "ltg10_TF", "ltg10_FERST", "ltg10_RT", "ltg10_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg10_GAP1", "ltg10_GAP2", "ltg10_TPC_sync", "ltg10_TPC_rst", "ltg10_TOF", "ltg11_ORB", "ltg11_HB", "ltg11_HBr", "ltg11_HC", "ltg11_PH", "ltg11_PP", "ltg11_CAL", "ltg11_SOT", "ltg11_EOT", "ltg11_SOC", "ltg11_EOC", "ltg11_TF", "ltg11_FERST", "ltg11_RT", "ltg11_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg11_GAP1", "ltg11_GAP2", "ltg11_TPC_sync", "ltg11_TPC_rst", "ltg11_TOF", "ltg12_ORB", "ltg12_HB", "ltg12_HBr", "ltg12_HC", "ltg12_PH", "ltg12_PP", "ltg12_CAL", "ltg12_SOT", "ltg12_EOT", "ltg12_SOC", "ltg12_EOC", "ltg12_TF", "ltg12_FERST", "ltg12_RT", "ltg12_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg12_GAP1", "ltg12_GAP2", "ltg12_TPC_sync", "ltg12_TPC_rst", "ltg12_TOF", "ltg13_ORB", "ltg13_HB", "ltg13_HBr", "ltg13_HC", "ltg13_PH", "ltg13_PP", "ltg13_CAL", "ltg13_SOT", "ltg13_EOT", "ltg13_SOC", "ltg13_EOC", "ltg13_TF", "ltg13_FERST", "ltg13_RT", "ltg13_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg13_GAP1", "ltg13_GAP2", "ltg13_TPC_sync", "ltg13_TPC_rst", "ltg13_TOF", "ltg14_ORB", "ltg14_HB", "ltg14_HBr", "ltg14_HC", "ltg14_PH", "ltg14_PP", "ltg14_CAL", "ltg14_SOT", "ltg14_EOT", "ltg14_SOC", "ltg14_EOC", "ltg14_TF", "ltg14_FERST", "ltg14_RT", "ltg14_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg14_GAP1", "ltg14_GAP2", "ltg14_TPC_sync", "ltg14_TPC_rst", "ltg14_TOF", "ltg15_ORB", "ltg15_HB", "ltg15_HBr", "ltg15_HC", "ltg15_PH", "ltg15_PP", "ltg15_CAL", "ltg15_SOT", "ltg15_EOT", "ltg15_SOC", "ltg15_EOC", "ltg15_TF", "ltg15_FERST", "ltg15_RT", "ltg15_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg15_GAP1", "ltg15_GAP2", "ltg15_TPC_sync", "ltg15_TPC_rst", "ltg15_TOF", "ltg16_ORB", "ltg16_HB", "ltg16_HBr", "ltg16_HC", "ltg16_PH", "ltg16_PP", "ltg16_CAL", "ltg16_SOT", "ltg16_EOT", "ltg16_SOC", "ltg16_EOC", "ltg16_TF", "ltg16_FERST", "ltg16_RT", "ltg16_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg16_GAP1", "ltg16_GAP2", "ltg16_TPC_sync", "ltg16_TPC_rst", "ltg16_TOF", "ltg17_ORB", "ltg17_HB", "ltg17_HBr", "ltg17_HC", "ltg17_PH", "ltg17_PP", "ltg17_CAL", "ltg17_SOT", "ltg17_EOT", "ltg17_SOC", "ltg17_EOC", "ltg17_TF", "ltg17_FERST", "ltg17_RT", "ltg17_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg17_GAP1", "ltg17_GAP2", "ltg17_TPC_sync", "ltg17_TPC_rst", "ltg17_TOF", "ltg18_ORB", "ltg18_HB", "ltg18_HBr", "ltg18_HC", "ltg18_PH", "ltg18_PP", "ltg18_CAL", "ltg18_SOT", "ltg18_EOT", "ltg18_SOC", "ltg18_EOC", "ltg18_TF", "ltg18_FERST", "ltg18_RT", "ltg18_RS", "", "", "", "", "", "", "", "", "", "", "", "", "ltg18_GAP1", "ltg18_GAP2", "ltg18_TPC_sync", "ltg18_TPC_rst", "ltg18_TOF", "bc40", "clk240", "extorb", "PLSRin", "FastLMin", "BUSYin", "SPAREin", "inp1", "inp2", "inp3", "inp4", "inp5", "inp6", "inp7", "inp8", "inp9", "inp10", "inp11", "inp12", "inp13", "inp14", "inp15", "inp16", "inp17", "inp18", "inp19", "inp20", "inp21", "inp22", "inp23", "inp24", "inp25", "inp26", "inp27", "inp28", "inp29", "inp30", "inp31", "inp32", "inp33", "inp34", "inp35", "inp36", "inp37", "inp38", "inp39", "inp40", "inp41", "inp42", "inp43", "inp44", "inp45", "inp46", "inp47", "inp48", "clamb1", "clamb2", "clamb3", "clamb4", "clamb5", "clamb6", "clamb7", "clamb8", "clamb9", "clamb10", "clamb11", "clamb12", "clamb13", "clamb14", "clamb15", "clamb16", "clamb17", "clamb18", "clamb19", "clamb20", "clamb21", "clamb22", "clamb23", "clamb24", "clamb25", "clamb26", "clamb27", "clamb28", "clamb29", "clamb30", "clamb31", "clamb32", "clamb33", "clamb34", "clamb35", "clamb36", "clamb37", "clamb38", "clamb39", "clamb40", "clamb41", "clamb42", "clamb43", "clamb44", "clamb45", "clamb46", "clamb47", "clamb48", "clamb49", "clamb50", "clamb51", "clamb52", "clamb53", "clamb54", "clamb55", "clamb56", "clamb57", "clamb58", "clamb59", "clamb60", "clamb61", "clamb62", "clamb63", "clamb64", "clama1", "clama2", "clama3", "clama4", "clama5", "clama6", "clama7", "clama8", "clama9", "clama10", "clama11", "clama12", "clama13", "clama14", "clama15", "clama16", "clama17", "clama18", "clama19", "clama20", "clama21", "clama22", "clama23", "clama24", "clama25", "clama26", "clama27", "clama28", "clama29", "clama30", "clama31", "clama32", "clama33", "clama34", "clama35", "clama36", "clama37", "clama38", "clama39", "clama40", "clama41", "clama42", "clama43", "clama44", "clama45", "clama46", "clama47", "clama48", "clama49", "clama50", "clama51", "clama52", "clama53", "clama54", "clama55", "clama56", "clama57", "clama58", "clama59", "clama60", "clama61", "clama62", "clama63", "clama64", "cla0b1", "cla0b2", "cla0b3", "cla0b4", "cla0b5", "cla0b6", "cla0b7", "cla0b8", "cla0b9", "cla0b10", "cla0b11", "cla0b12", "cla0b13", "cla0b14", "cla0b15", "cla0b16", "cla0b17", "cla0b18", "cla0b19", "cla0b20", "cla0b21", "cla0b22", "cla0b23", "cla0b24", "cla0b25", "cla0b26", "cla0b27", "cla0b28", "cla0b29", "cla0b30", "cla0b31", "cla0b32", "cla0b33", "cla0b34", "cla0b35", "cla0b36", "cla0b37", "cla0b38", "cla0b39", "cla0b40", "cla0b41", "cla0b42", "cla0b43", "cla0b44", "cla0b45", "cla0b46", "cla0b47", "cla0b48", "cla0b49", "cla0b50", "cla0b51", "cla0b52", "cla0b53", "cla0b54", "cla0b55", "cla0b56", "cla0b57", "cla0b58", "cla0b59", "cla0b60", "cla0b61", "cla0b62", "cla0b63", "cla0b64", "cla0a1", "cla0a2", "cla0a3", "cla0a4", "cla0a5", "cla0a6", "cla0a7", "cla0a8", "cla0a9", "cla0a10", "cla0a11", "cla0a12", "cla0a13", "cla0a14", "cla0a15", "cla0a16", "cla0a17", "cla0a18", "cla0a19", "cla0a20", "cla0a21", "cla0a22", "cla0a23", "cla0a24", "cla0a25", "cla0a26", "cla0a27", "cla0a28", "cla0a29", "cla0a30", "cla0a31", "cla0a32", "cla0a33", "cla0a34", "cla0a35", "cla0a36", "cla0a37", "cla0a38", "cla0a39", "cla0a40", "cla0a41", "cla0a42", "cla0a43", "cla0a44", "cla0a45", "cla0a46", "cla0a47", "cla0a48", "cla0a49", "cla0a50", "cla0a51", "cla0a52", "cla0a53", "cla0a54", "cla0a55", "cla0a56", "cla0a57", "cla0a58", "cla0a59", "cla0a60", "cla0a61", "cla0a62", "cla0a63", "cla0a64", "cla1b1", "cla1b2", "cla1b3", "cla1b4", "cla1b5", "cla1b6", "cla1b7", "cla1b8", "cla1b9", "cla1b10", "cla1b11", "cla1b12", "cla1b13", "cla1b14", "cla1b15", "cla1b16", "cla1b17", "cla1b18", "cla1b19", "cla1b20", "cla1b21", "cla1b22", "cla1b23", "cla1b24", "cla1b25", "cla1b26", "cla1b27", "cla1b28", "cla1b29", "cla1b30", "cla1b31", "cla1b32", "cla1b33", "cla1b34", "cla1b35", "cla1b36", "cla1b37", "cla1b38", "cla1b39", "cla1b40", "cla1b41", "cla1b42", "cla1b43", "cla1b44", "cla1b45", "cla1b46", "cla1b47", "cla1b48", "cla1b49", "cla1b50", "cla1b51", "cla1b52", "cla1b53", "cla1b54", "cla1b55", "cla1b56", "cla1b57", "cla1b58", "cla1b59", "cla1b60", "cla1b61", "cla1b62", "cla1b63", "cla1b64", "cla1a1", "cla1a2", "cla1a3", "cla1a4", "cla1a5", "cla1a6", "cla1a7", "cla1a8", "cla1a9", "cla1a10", "cla1a11", "cla1a12", "cla1a13", "cla1a14", "cla1a15", "cla1a16", "cla1a17", "cla1a18", "cla1a19", "cla1a20", "cla1a21", "cla1a22", "cla1a23", "cla1a24", "cla1a25", "cla1a26", "cla1a27", "cla1a28", "cla1a29", "cla1a30", "cla1a31", "cla1a32", "cla1a33", "cla1a34", "cla1a35", "cla1a36", "cla1a37", "cla1a38", "cla1a39", "cla1a40", "cla1a41", "cla1a42", "cla1a43", "cla1a44", "cla1a45", "cla1a46", "cla1a47", "cla1a48", "cla1a49", "cla1a50", "cla1a51", "cla1a52", "cla1a53", "cla1a54", "cla1a55", "cla1a56", "cla1a57", "cla1a58", "cla1a59", "cla1a60", "cla1a61", "cla1a62", "cla1a63", "cla1a64", "l0_trigger", "l1_trigger", "l2_trigger", "clum1", "clum2", "clum3", "clum4", "clum5", "clum6", "clu01", "clu02", "clu03", "clu04", "clu05", "clu06", "clu11", "clu12", "clu13", "clu14", "clu15", "clu16", "ltg1_busy", "ltg2_busy", "ltg3_busy", "ltg4_busy", "ltg5_busy", "ltg6_busy", "ltg7_busy", "ltg8_busy", "ltg9_busy", - "ltg10_busy", "ltg11_busy", "ltg12_busy", "ltg13_busy", "ltg14_busy", "ltg15_busy", "ltg16_busy", "ltg17_busy", "ltg18_busy", "orbitid"}; + "ltg10_busy", "ltg11_busy", "ltg12_busy", "ltg13_busy", "ltg14_busy", "ltg15_busy", "ltg16_busy", "ltg17_busy", "ltg18_busy", "orbitid", "clum7", "clum8", "clu07", "clu08", "clu17", "clu18", "clu_busy1", "clu_busy2", "clu_busy3", "clu_busy4", "clu_busy5", "clu_busy6", "clu_busy7", "clu_busy8"}; diff --git a/DataFormats/Detectors/Common/CMakeLists.txt b/DataFormats/Detectors/Common/CMakeLists.txt index 6b090771f292a..bbc0aaf9e8320 100644 --- a/DataFormats/Detectors/Common/CMakeLists.txt +++ b/DataFormats/Detectors/Common/CMakeLists.txt @@ -52,3 +52,14 @@ o2_add_test(DetID PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats COMPONENT_NAME DetectorsCommonDataFormats LABELS dataformats) + +o2_add_test(CTFEntropyCoder + NAME CTFEntropyCoder + SOURCES test/testCTFEntropyCoder.cxx + PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats O2::rANS + COMPONENT_NAME DetectorsCommonDataFormats + TARGETVARNAME TEST_CTF_ENTROPY_CODER + LABELS detectorsbase) +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + target_compile_options(${TEST_CTF_ENTROPY_CODER} PRIVATE -march=native) +endif() diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h new file mode 100644 index 0000000000000..110af41cddd49 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/ANSHeader.h @@ -0,0 +1,70 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ANSHeader.h +/// \author michael.lettrich@cern.ch +/// \brief representation of ANS Version number in a comparable way + +#ifndef _ALICEO2_ANSHEADER_H_ +#define _ALICEO2_ANSHEADER_H_ + +#include +#include +#include +#include + +namespace o2::ctf +{ + +struct ANSHeader { + uint8_t majorVersion; + uint8_t minorVersion; + + void clear() { majorVersion = minorVersion = 0; } + inline constexpr operator uint16_t() const noexcept + { + uint16_t major = majorVersion; + uint16_t minor = minorVersion; + return (major << 8) | minor; + }; + + inline operator std::string() const + { + return fmt::format("{}.{}", majorVersion, minorVersion); + }; + inline constexpr uint32_t version() const noexcept { return static_cast(*this); }; + inline constexpr bool operator==(const ANSHeader& other) const noexcept { return static_cast(*this) == static_cast(other); }; + inline constexpr bool operator!=(const ANSHeader& other) const noexcept { return static_cast(*this) != static_cast(other); }; + inline constexpr bool operator<(const ANSHeader& other) const noexcept { return static_cast(*this) < static_cast(other); }; + inline constexpr bool operator>(const ANSHeader& other) const noexcept { return static_cast(*this) > static_cast(other); }; + inline constexpr bool operator>=(const ANSHeader& other) const noexcept { return static_cast(*this) >= static_cast(other); }; + inline constexpr bool operator<=(const ANSHeader& other) const noexcept { return static_cast(*this) <= static_cast(other); }; + ClassDefNV(ANSHeader, 2); +}; + +inline constexpr ANSHeader ANSVersionUnspecified{0, 0}; +inline constexpr ANSHeader ANSVersionCompat{0, 1}; +inline constexpr ANSHeader ANSVersion1{1, 0}; + +inline ANSHeader ansVersionFromString(const std::string& ansVersionString) +{ + if (ansVersionString == "0.1" || ansVersionString == "compat") { + return ctf::ANSVersionCompat; + } else if (ansVersionString == "1.0") { + return ctf::ANSVersion1; + } else { + return ctf::ANSVersionUnspecified; + } +} + +} // namespace o2::ctf + +#endif /* _ALICEO2_ANSHEADER_H_ */ diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h index 74610264336be..5a0d2d64b0ff5 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/AlignParam.h @@ -37,9 +37,12 @@ class AlignParam AlignParam(const char* symname, int algID, // volume symbolic name and its alignable ID double x, double y, double z, // delta translation double psi, double theta, double phi, // delta rotation - bool global = true); // global (preferable) or local delta definition + bool global = true, // global (preferable) or local delta definition + bool convertLocalToGlobal = true); // if local is provided, convert it to global - AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global = true); + AlignParam(const char* symname, int algID, TGeoMatrix& m, + bool global = true, // global (preferable) or local delta definition + bool convertLocalToGlobal = true); // if local is provided, convert it to global /// return symbolic name of the volume const std::string& getSymName() const { return mSymName; } @@ -52,7 +55,7 @@ class AlignParam double getZ() const { return mZ; } /// apply object to geoemetry - bool applyToGeometry() const; + bool applyToGeometry(int printLevel = -1) const; /// extract global delta matrix TGeoHMatrix createMatrix() const; @@ -70,6 +73,9 @@ class AlignParam void setAlignableID(int id) { mAlignableID = id; } /// ================ methods for direct setting of delta params + /// set parameters + void setParams(double x, double y, double z, double psi, double theta, double phi); + /// set parameters of global delta void setGlobalParams(double x, double y, double z, double psi, double theta, double phi); @@ -114,6 +120,9 @@ class AlignParam int rectify(double zero = 1e-13); + bool isGlobal() const { return mIsGlobal; } + void setIsGlobal(bool v) { mIsGlobal = v; } + protected: bool matrixToAngles(const double* rot, double& psi, double& theta, double& phi) const; void anglesToMatrix(double psi, double theta, double phi, double* rot) const; @@ -123,8 +132,8 @@ class AlignParam private: std::string mSymName{}; + bool mIsGlobal = true; /// is this global delta? int mAlignableID = -1; /// alignable ID (set for sensors only) - double mX = 0.; ///< X translation of global delta double mY = 0.; ///< Y translation of global delta double mZ = 0.; ///< Z translation of global delta @@ -133,10 +142,10 @@ class AlignParam double mTheta = 0.; ///< "roll" : Euler angle of rotation around Y axis after 1st rotation (radians) double mPhi = 0.; ///< "yaw" : Euler angle of rotation around Z axis (radians) - ClassDefNV(AlignParam, 1); + ClassDefNV(AlignParam, 2); }; -} -} +} // namespace detectors +} // namespace o2 #endif diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h index d8dd485fa0852..2d2383783cfc3 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -83,7 +83,12 @@ class DetID static constexpr ID TRK = 19; static constexpr ID FT3 = 20; static constexpr ID FCT = 21; - static constexpr ID Last = FCT; + static constexpr ID TF3 = 22; + static constexpr ID RCH = 23; + static constexpr ID MI3 = 24; + static constexpr ID ECL = 25; + static constexpr ID FD3 = 26; + static constexpr ID Last = FD3; #else static constexpr ID Last = FOC; ///< if extra detectors added, update this !!! #endif @@ -177,7 +182,7 @@ class DetID // detector names, will be defined in DataSources static constexpr const char* sDetNames[nDetectors + 1] = ///< defined detector names #ifdef ENABLE_UPGRADES - {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", nullptr}; + {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", "IT3", "TRK", "FT3", "FCT", "TF3", "RCH", "MI3", "ECL", "FD3", nullptr}; #else {"ITS", "TPC", "TRD", "TOF", "PHS", "CPV", "EMC", "HMP", "MFT", "MCH", "MID", "ZDC", "FT0", "FV0", "FDD", "TST", "CTP", "FOC", nullptr}; #endif @@ -190,7 +195,8 @@ class DetID o2h::gDataOriginTST, o2h::gDataOriginCTP, o2h::gDataOriginFOC #ifdef ENABLE_UPGRADES , - o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT + o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT, o2h::gDataOriginTF3, + o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL, o2h::gDataOriginFD3 #endif }; #endif // GPUCA_GPUCODE_DEVICE @@ -206,9 +212,11 @@ GPUconstexpr() DetID::mask_t sMasks[DetID::nDetectors] = ///< detectot masks DetID::mask_t(math_utils::bit2Mask(DetID::CPV)), DetID::mask_t(math_utils::bit2Mask(DetID::EMC)), DetID::mask_t(math_utils::bit2Mask(DetID::HMP)), DetID::mask_t(math_utils::bit2Mask(DetID::MFT)), DetID::mask_t(math_utils::bit2Mask(DetID::MCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MID)), DetID::mask_t(math_utils::bit2Mask(DetID::ZDC)), DetID::mask_t(math_utils::bit2Mask(DetID::FT0)), DetID::mask_t(math_utils::bit2Mask(DetID::FV0)), DetID::mask_t(math_utils::bit2Mask(DetID::FDD)), DetID::mask_t(math_utils::bit2Mask(DetID::TST)), DetID::mask_t(math_utils::bit2Mask(DetID::CTP)), DetID::mask_t(math_utils::bit2Mask(DetID::FOC)) + #ifdef ENABLE_UPGRADES , - DetID::mask_t(math_utils::bit2Mask(DetID::IT3)), DetID::mask_t(math_utils::bit2Mask(DetID::TRK)), DetID::mask_t(math_utils::bit2Mask(DetID::FT3)), DetID::mask_t(math_utils::bit2Mask(DetID::FCT)) + DetID::mask_t(math_utils::bit2Mask(DetID::IT3)), DetID::mask_t(math_utils::bit2Mask(DetID::TRK)), DetID::mask_t(math_utils::bit2Mask(DetID::FT3)), DetID::mask_t(math_utils::bit2Mask(DetID::FCT)), DetID::mask_t(math_utils::bit2Mask(DetID::TF3)), + DetID::mask_t(math_utils::bit2Mask(DetID::RCH)), DetID::mask_t(math_utils::bit2Mask(DetID::MI3)), DetID::mask_t(math_utils::bit2Mask(DetID::ECL)), DetID::mask_t(math_utils::bit2Mask(DetID::FD3)) #endif }; } // namespace detid_internal diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h index e29067e6d6030..ba6b853f7fb23 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h @@ -16,18 +16,31 @@ #ifndef ALICEO2_ENCODED_BLOCKS_H #define ALICEO2_ENCODED_BLOCKS_H -#undef NDEBUG -#include +// #undef NDEBUG +// #include #include #include #include -#include "rANS/rans.h" -#include "rANS/utils.h" +#include + #include "TTree.h" #include "CommonUtils/StringUtils.h" #include "Framework/Logger.h" #include "DetectorsCommonDataFormats/CTFDictHeader.h" #include "DetectorsCommonDataFormats/CTFIOSize.h" +#include "DetectorsCommonDataFormats/ANSHeader.h" +#include "DetectorsCommonDataFormats/internal/Packer.h" +#include "DetectorsCommonDataFormats/Metadata.h" +#ifndef __CLING__ +#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" +#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" +#include "rANS/compat.h" +#include "rANS/histogram.h" +#include "rANS/serialize.h" +#include "rANS/factory.h" +#include "rANS/metrics.h" +#include "rANS/utils.h" +#endif namespace o2 { @@ -50,9 +63,20 @@ struct is_iterator inline constexpr bool is_iterator_v = is_iterator::value; + +inline constexpr bool mayEEncode(Metadata::OptStore opt) noexcept +{ + return (opt == Metadata::OptStore::EENCODE) || (opt == Metadata::OptStore::EENCODE_OR_PACK); +} + +inline constexpr bool mayPack(Metadata::OptStore opt) noexcept +{ + return (opt == Metadata::OptStore::PACK) || (opt == Metadata::OptStore::EENCODE_OR_PACK); +} + } // namespace detail +constexpr size_t PackingThreshold = 512; -using namespace o2::rans; constexpr size_t Alignment = 16; constexpr int WrappersSplitLevel = 99; @@ -76,10 +100,10 @@ inline T* relocatePointer(const char* oldBase, char* newBase, const T* ptr) } template = sizeof(source_T)), bool> = true> -inline size_t calculateNDestTElements(size_t sourceElems) noexcept +inline constexpr size_t calculateNDestTElements(size_t nElems) noexcept { - const size_t sizeOfSourceArray = sourceElems * sizeof(source_T); - return sizeOfSourceArray / sizeof(dest_T) + (sizeOfSourceArray % sizeof(dest_T) != 0); + const size_t srcBufferSize = nElems * sizeof(source_T); + return srcBufferSize / sizeof(dest_T) + (srcBufferSize % sizeof(dest_T) != 0); }; template = sizeof(source_T)), bool> = true> @@ -94,52 +118,6 @@ inline size_t calculatePaddedSize(size_t nElems) noexcept ///>>======================== Auxiliary classes =======================>> -struct ANSHeader { - uint8_t majorVersion; - uint8_t minorVersion; - - void clear() { majorVersion = minorVersion = 0; } - ClassDefNV(ANSHeader, 1); -}; - -struct Metadata { - enum class OptStore : uint8_t { // describe how the store the data described by this metadata - EENCODE, // entropy encoding applied - ROOTCompression, // original data repacked to array with slot-size = streamSize and saved with root compression - NONE, // original data repacked to array with slot-size = streamSize and saved w/o compression - NODATA // no data was provided - }; - size_t messageLength = 0; - size_t nLiterals = 0; - uint8_t messageWordSize = 0; - uint8_t coderType = 0; - uint8_t streamSize = 0; - uint8_t probabilityBits = 0; - OptStore opt = OptStore::EENCODE; - int32_t min = 0; - int32_t max = 0; - int nDictWords = 0; - int nDataWords = 0; - int nLiteralWords = 0; - - size_t getUncompressedSize() const { return messageLength * messageWordSize; } - size_t getCompressedSize() const { return (nDictWords + nDataWords + nLiteralWords) * streamSize; } - void clear() - { - min = max = 0; - messageLength = 0; - messageWordSize = 0; - nLiterals = 0; - coderType = 0; - streamSize = 0; - probabilityBits = 0; - nDictWords = 0; - nDataWords = 0; - nLiteralWords = 0; - } - ClassDefNV(Metadata, 2); -}; - /// registry struct for the buffer start and offsets of writable space struct Registry { char* head = nullptr; //! pointer on the head of the CTF @@ -160,6 +138,12 @@ struct Registry { return size - offsFreeStart; } + char* getFreeBlockEnd() const + { + assert(offsFreeStart <= size); + return getFreeBlockStart() + getFreeSize(); + } + ClassDefNV(Registry, 1); }; @@ -178,11 +162,21 @@ struct Block { inline const W* getData() const { return nData ? (payload + nDict) : nullptr; } inline const W* getDataPointer() const { return payload ? (payload + nDict) : nullptr; } // needed when nData is not set yet inline const W* getLiterals() const { return nLiterals ? (payload + nDict + nData) : nullptr; } + inline const W* getEndOfBlock() const + { + if (!registry) { + return nullptr; + } + // get last legal W*, since unaligned data is undefined behavior! + const size_t delta = reinterpret_cast(registry->getFreeBlockEnd()) % sizeof(W); + return reinterpret_cast(registry->getFreeBlockEnd() - delta); + } inline W* getCreatePayload() { return payload ? payload : (registry ? (payload = reinterpret_cast(registry->getFreeBlockStart())) : nullptr); } inline W* getCreateDict() { return payload ? payload : getCreatePayload(); } inline W* getCreateData() { return payload ? (payload + nDict) : getCreatePayload(); } inline W* getCreateLiterals() { return payload ? payload + (nDict + nData) : getCreatePayload(); } + inline W* getEndOfBlock() { return const_cast(static_cast(*this).getEndOfBlock()); }; inline auto getOffsDict() { return reinterpret_cast(getCreateDict()) - reinterpret_cast(registry->head); } inline auto getOffsData() { return reinterpret_cast(getCreateData()) - reinterpret_cast(registry->head); } @@ -290,8 +284,10 @@ struct Block { // resize block and free up unused buffer space. void realignBlock() { - size_t sz = estimateSize(getNStored()); - registry->offsFreeStart = (reinterpret_cast(payload) - registry->head) + sz; + if (payload) { + size_t sz = estimateSize(getNStored()); + registry->offsFreeStart = (reinterpret_cast(payload) - registry->head) + sz; + } } /// store binary blob data (buffer filled from head to tail) @@ -340,7 +336,15 @@ class EncodedBlocks public: typedef EncodedBlocks base; - void setHeader(const H& h) { mHeader = h; } +#ifndef __CLING__ + template + using dictionaryType = std::variant, rans::RenormedDenseHistogram>; +#endif + + void setHeader(const H& h) + { + mHeader = h; + } const H& getHeader() const { return mHeader; } H& getHeader() { return mHeader; } std::shared_ptr cloneHeader() const { return std::shared_ptr(new H(mHeader)); } // for dictionary creation @@ -361,15 +365,70 @@ class EncodedBlocks return mBlocks[i]; } - o2::rans::RenormedFrequencyTable getFrequencyTable(int i) const +#ifndef __CLING__ + template + dictionaryType getDictionary(int i, ANSHeader ansVersion = ANSVersionUnspecified) const { const auto& block = getBlock(i); const auto& metadata = getMetadata(i); - rans::FrequencyTable frequencyTable{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; - return rans::renorm(std::move(frequencyTable), metadata.probabilityBits); - } + ansVersion = checkANSVersion(ansVersion); + + assert(static_cast(std::numeric_limits::min()) <= static_cast(metadata.max)); + assert(static_cast(std::numeric_limits::max()) >= static_cast(metadata.min)); + + // check consistency of metadata and type + [&]() { + const int64_t sourceMin = std::numeric_limits::min(); + const int64_t sourceMax = std::numeric_limits::max(); + + auto view = rans::trim(rans::HistogramView{block.getDict(), block.getDict() + block.getNDict(), metadata.min}); + const int64_t dictMin = view.getMin(); + const int64_t dictMax = view.getMax(); + assert(dictMin >= metadata.min); + assert(dictMax <= metadata.max); + + if ((dictMin < sourceMin) || (dictMax > sourceMax)) { + if (ansVersion == ANSVersionCompat && mHeader.majorVersion == 1 && mHeader.minorVersion == 0 && mHeader.dictTimeStamp < 1653192000000) { + LOGP(warn, "value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}], tolerate in compat mode for old dictionaries", sourceMin, sourceMax, dictMin, dictMax); + } else { + throw std::runtime_error(fmt::format("value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}]", sourceMin, sourceMax, dictMin, dictMax)); + } + } + }(); - void setANSHeader(const ANSHeader& h) { mANSHeader = h; } + if (ansVersion == ANSVersionCompat) { + rans::DenseHistogram histogram{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; + return rans::compat::renorm(std::move(histogram), metadata.probabilityBits); + } else if (ansVersion == ANSVersion1) { + // dictionary is loaded from an explicit dict file and is stored densly + if (getANSHeader() == ANSVersionUnspecified) { + rans::DenseHistogram histogram{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; + size_t renormingBits = rans::utils::sanitizeRenormingBitRange(metadata.probabilityBits); + LOG_IF(debug, renormingBits != metadata.probabilityBits) + << fmt::format("While reading metadata from external dictionary, rANSV1 is rounding renorming precision from {} to {}", metadata.probabilityBits, renormingBits); + return rans::renorm(std::move(histogram), renormingBits, rans::RenormingPolicy::ForceIncompressible); + } else { + // dictionary is elias-delta coded inside the block + if constexpr (sizeof(source_T) > 2) { + return rans::readRenormedSetDictionary(block.getDict(), block.getDict() + block.getNDict(), + static_cast(metadata.min), static_cast(metadata.max), + metadata.probabilityBits); + } else { + return rans::readRenormedDictionary(block.getDict(), block.getDict() + block.getNDict(), + static_cast(metadata.min), static_cast(metadata.max), + metadata.probabilityBits); + } + } + } else { + throw std::runtime_error(fmt::format("Failed to load serialized Dictionary. Unsupported ANS Version: {}", static_cast(ansVersion))); + } + }; +#endif + + void setANSHeader(const ANSHeader& h) + { + mANSHeader = h; + } const ANSHeader& getANSHeader() const { return mANSHeader; } ANSHeader& getANSHeader() { return mANSHeader; } @@ -409,6 +468,9 @@ class EncodedBlocks /// total allocated size in bytes size_t size() const { return mRegistry.size; } + /// used part of total allocated size in bytes (output size) + size_t outputsize() const { return mRegistry.offsFreeStart; } + /// size remaining for additional data size_t getFreeSize() const { return mRegistry.getFreeSize(); } @@ -435,25 +497,27 @@ class EncodedBlocks /// encode vector src to bloc at provided slot template - inline o2::ctf::CTFIOSize encode(const VE& src, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const void* encoderExt = nullptr, float memfc = 1.f) + inline o2::ctf::CTFIOSize encode(const VE& src, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f) { return encode(std::begin(src), std::end(src), slot, symbolTablePrecision, opt, buffer, encoderExt, memfc); } /// encode vector src to bloc at provided slot template - o2::ctf::CTFIOSize encode(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const void* encoderExt = nullptr, float memfc = 1.f); + o2::ctf::CTFIOSize encode(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); /// decode block at provided slot to destination vector (will be resized as needed) template - o2::ctf::CTFIOSize decode(container_T& dest, int slot, const void* decoderExt = nullptr) const; + o2::ctf::CTFIOSize decode(container_T& dest, int slot, const std::any& decoderExt = {}) const; /// decode block at provided slot to destination pointer, the needed space assumed to be available template , bool> = true> - o2::ctf::CTFIOSize decode(D_IT dest, int slot, const void* decoderExt = nullptr) const; + o2::ctf::CTFIOSize decode(D_IT dest, int slot, const std::any& decoderExt = {}) const; +#ifndef __CLING__ /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables - static std::vector createDictionaryBlocks(const std::vector& vfreq, const std::vector& prbits); + static std::vector createDictionaryBlocks(const std::vector>& vfreq, const std::vector& prbits); +#endif /// print itself void print(const std::string& prefix = "", int verbosity = 1) const; @@ -468,6 +532,8 @@ class EncodedBlocks std::array mMetadata; // compressed block's details std::array, N> mBlocks; //! this is in fact stored, but to overcome TBuffer limits we have to define the branches per block!!! + inline static constexpr Metadata::OptStore FallbackStorageType{Metadata::OptStore::NONE}; + /// setup internal structure and registry for given buffer size (in bytes!!!) void init(size_t sz); @@ -495,8 +561,93 @@ class EncodedBlocks template static bool readTreeBranch(TTree& tree, const std::string& brname, D& dt, int ev = 0); - ClassDefNV(EncodedBlocks, 2); -}; + template + auto expandStorage(size_t slot, size_t nElemets, T* buffer = nullptr) -> decltype(auto); + + inline ANSHeader checkANSVersion(ANSHeader ansVersion) const + { + auto ctfANSHeader = getANSHeader(); + ANSHeader ret{ANSVersionUnspecified}; + + const bool isEqual{ansVersion == ctfANSHeader}; + const bool isHeaderUnspecified{ctfANSHeader == ANSVersionUnspecified}; + + if (isEqual) { + if (isHeaderUnspecified) { + throw std::runtime_error{fmt::format("Missmatch of ANSVersions, trying to encode/decode CTF with ANS Version Header {} with ANS Version {}", + static_cast(ctfANSHeader), + static_cast(ansVersion))}; + } else { + ret = ctfANSHeader; + } + } else { + if (isHeaderUnspecified) { + ret = ansVersion; + } else { + ret = ctfANSHeader; + } + } + + return ret; + }; + + template + o2::ctf::CTFIOSize entropyCodeRANSCompat(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); + + template + o2::ctf::CTFIOSize entropyCodeRANSV1(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr, const std::any& encoderExt = {}, float memfc = 1.f); + + template + o2::ctf::CTFIOSize encodeRANSV1External(const input_IT srcBegin, const input_IT srcEnd, int slot, const std::any& encoderExt, buffer_T* buffer = nullptr, double_t sizeEstimateSafetyFactor = 1); + + template + o2::ctf::CTFIOSize encodeRANSV1Inplace(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr, double_t sizeEstimateSafetyFactor = 1); + +#ifndef __CLING__ + template + o2::ctf::CTFIOSize pack(const input_IT srcBegin, const input_IT srcEnd, int slot, rans::Metrics::value_type> metrics, buffer_T* buffer = nullptr); + + template + inline o2::ctf::CTFIOSize pack(const input_IT srcBegin, const input_IT srcEnd, int slot, buffer_T* buffer = nullptr) + { + using source_type = typename std::iterator_traits::value_type; + + rans::Metrics metrics{}; + metrics.getDatasetProperties().numSamples = std::distance(srcBegin, srcEnd); + + if (metrics.getDatasetProperties().numSamples != 0) { + const auto [minIter, maxIter] = std::minmax_element(srcBegin, srcEnd); + metrics.getDatasetProperties().min = *minIter; + metrics.getDatasetProperties().max = *maxIter; + + // special case: if min === max, the range is 0 and the data can be reconstructed just via the metadata. + metrics.getDatasetProperties().alphabetRangeBits = + rans::utils::getRangeBits(metrics.getDatasetProperties().min, + metrics.getDatasetProperties().max); + } + + return pack(srcBegin, srcEnd, slot, metrics, buffer); + } +#endif + + template + o2::ctf::CTFIOSize store(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr); + + // decode + template + CTFIOSize decodeCompatImpl(dst_IT dest, int slot, const std::any& decoderExt) const; + + template + CTFIOSize decodeRansV1Impl(dst_IT dest, int slot, const std::any& decoderExt) const; + + template + CTFIOSize decodeUnpackImpl(dst_IT dest, int slot) const; + + template + CTFIOSize decodeCopyImpl(dst_IT dest, int slot) const; + + ClassDefNV(EncodedBlocks, 3); +}; // namespace ctf ///_____________________________________________________________________________ /// read from tree to non-flat object @@ -701,7 +852,7 @@ auto EncodedBlocks::getImage(const void* newHead) // we don't modify newHead, but still need to remove constness for relocation interface relocate(image.mRegistry.head, const_cast(reinterpret_cast(newHead)), reinterpret_cast(&image)); - return std::move(image); + return image; } ///_____________________________________________________________________________ @@ -751,7 +902,7 @@ void EncodedBlocks::print(const std::string& prefix, int verbosity) con ndata += mBlocks[i].getNData(); nlit += mBlocks[i].getNLiterals(); } - LOG(info) << prefix << N << " blocks, input size: " << inpSize << ", output size: " << size() + LOG(info) << prefix << N << " blocks, input size: " << inpSize << ", output size: " << outputsize() << " NDictWords: " << ndict << " NDataWords: " << ndata << " NLiteralWords: " << nlit; } } @@ -759,9 +910,9 @@ void EncodedBlocks::print(const std::string& prefix, int verbosity) con ///_____________________________________________________________________________ template template -inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, // destination container - int slot, // slot of the block to decode - const void* decoderExt) const // optional externally provided decoder +inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, // destination container + int slot, // slot of the block to decode + const std::any& decoderExt) const // optional externally provided decoder { dest.resize(mMetadata[slot].messageLength); // allocate output buffer return decode(std::begin(dest), slot, decoderExt); @@ -770,54 +921,190 @@ inline o2::ctf::CTFIOSize EncodedBlocks::decode(container_T& dest, ///_____________________________________________________________________________ template template , bool>> -o2::ctf::CTFIOSize EncodedBlocks::decode(D_IT dest, // iterator to destination - int slot, // slot of the block to decode - const void* decoderExt) const // optional externally provided decoder +CTFIOSize EncodedBlocks::decode(D_IT dest, // iterator to destination + int slot, // slot of the block to decode + const std::any& decoderExt) const // optional externally provided decoder { + // get references to the right data + const auto& ansVersion = getANSHeader(); const auto& block = mBlocks[slot]; const auto& md = mMetadata[slot]; + LOGP(debug, "Slot{} | NStored={} Ndict={} nData={}, MD: messageLength:{} opt:{} min:{} max:{} offs:{} width:{} ", slot, block.getNStored(), block.getNDict(), block.getNData(), md.messageLength, (int)md.opt, md.min, md.max, md.literalsPackingOffset, md.literalsPackingWidth); - using dest_t = typename std::iterator_traits::value_type; + constexpr size_t word_size = sizeof(W); - // decode - if (block.getNStored()) { + if (ansVersion == ANSVersionCompat) { + if (!block.getNStored()) { + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; + } if (md.opt == Metadata::OptStore::EENCODE) { - if (!decoderExt && !block.getNDict()) { - LOG(error) << "Dictionaty is not saved for slot " << slot << " and no external decoder is provided"; - throw std::runtime_error("Dictionary is not saved and no external decoder provided"); - } - const o2::rans::LiteralDecoder64* decoder = reinterpret_cast*>(decoderExt); - std::unique_ptr> decoderLoc; - if (block.getNDict()) { // if dictionaty is saved, prefer it - decoderLoc = std::make_unique>(this->getFrequencyTable(slot)); - decoder = decoderLoc.get(); - } else { // verify that decoded corresponds to stored metadata - if (md.min != decoder->getMinSymbol()) { - LOG(error) << "Mismatch between min=" << md.min << " symbol in metadata and those in external decoder " - << decoder->getMinSymbol() << " for slot " << slot; - throw std::runtime_error("Mismatch between min symbol in metadata and the one in external decoder"); - } - } - // load incompressible symbols if they existed - std::vector literals; - if (block.getNLiterals()) { - // note: here we have to use md.nLiterals (original number of literal words) rather than md.nLiteralWords == block.getNLiterals() - // (number of W-words in the EncodedBlock occupied by literals) as we cast literals stored in W-word array - // to D-word array - literals = std::vector{reinterpret_cast(block.getLiterals()), reinterpret_cast(block.getLiterals()) + md.nLiterals}; + return decodeCompatImpl(dest, slot, decoderExt); + } else { + return decodeCopyImpl(dest, slot); + } + } else if (ansVersion == ANSVersion1) { + if (md.opt == Metadata::OptStore::PACK) { + return decodeUnpackImpl(dest, slot); + } + if (!block.getNStored()) { + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; + } + if (md.opt == Metadata::OptStore::EENCODE) { + return decodeRansV1Impl(dest, slot, decoderExt); + } else { + return decodeCopyImpl(dest, slot); + } + } else { + throw std::runtime_error("unsupported ANS Version"); + } +}; + +#ifndef __CLING__ +template +template +CTFIOSize EncodedBlocks::decodeCompatImpl(dst_IT dstBegin, int slot, const std::any& decoderExt) const +{ + + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dst_type = typename std::iterator_traits::value_type; + using decoder_type = typename rans::compat::decoder_type; + + std::optional inplaceDecoder{}; + if (md.nDictWords > 0) { + inplaceDecoder = decoder_type{std::get>(this->getDictionary(slot))}; + } else if (!decoderExt.has_value()) { + throw std::runtime_error("neither dictionary nor external decoder provided"); + } + + auto getDecoder = [&]() -> const decoder_type& { + if (inplaceDecoder.has_value()) { + return inplaceDecoder.value(); + } else { + return std::any_cast(decoderExt); + } + }; + + const size_t NDecoderStreams = rans::compat::defaults::CoderPreset::nStreams; + + if (block.getNLiterals()) { + auto* literalsEnd = reinterpret_cast(block.getLiterals()) + md.nLiterals; + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, NDecoderStreams, literalsEnd); + } else { + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, NDecoderStreams); + } + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeRansV1Impl(dst_IT dstBegin, int slot, const std::any& decoderExt) const +{ + + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dst_type = typename std::iterator_traits::value_type; + using decoder_type = typename rans::defaultDecoder_type; + + std::optional inplaceDecoder{}; + if (md.nDictWords > 0) { + std::visit([&](auto&& arg) { inplaceDecoder = decoder_type{arg}; }, this->getDictionary(slot)); + } else if (!decoderExt.has_value()) { + throw std::runtime_error("no dictionary nor external decoder provided"); + } + + auto getDecoder = [&]() -> const decoder_type& { + if (inplaceDecoder.has_value()) { + return inplaceDecoder.value(); + } else { + return std::any_cast(decoderExt); + } + }; + + // verify decoders + [&]() { + const decoder_type& decoder = getDecoder(); + const size_t decoderSymbolTablePrecision = decoder.getSymbolTablePrecision(); + + if (md.probabilityBits != decoderSymbolTablePrecision) { + throw std::runtime_error(fmt::format( + "Missmatch in decoder renorming precision vs metadata:{} Bits vs {} Bits.", + md.probabilityBits, decoderSymbolTablePrecision)); + } + + if (md.streamSize != rans::utils::getStreamingLowerBound_v) { + throw std::runtime_error("Streaming lower bound of dataset and decoder do not match"); + } + }(); + + // do the actual decoding + if (block.getNLiterals()) { + std::vector literals(md.nLiterals); + rans::unpack(block.getLiterals(), md.nLiterals, literals.data(), md.literalsPackingWidth, md.literalsPackingOffset); + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, md.nStreams, literals.end()); + } else { + getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, md.nStreams); + } + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeUnpackImpl(dst_IT dest, int slot) const +{ + using dest_t = typename std::iterator_traits::value_type; + + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + const size_t messageLength = md.messageLength; + const size_t packingWidth = md.probabilityBits; + const dest_t offset = md.min; + const auto* srcIt = block.getData(); + // we have a vector of one and the same value. All information is in the metadata + if (packingWidth == 0) { + const dest_t value = [&]() -> dest_t { + // Bugfix: We tried packing values with a width of 0 Bits; + if (md.nDataWords > 0) { + LOGP(debug, "packing bug recovery: MD nStreams:{} messageLength:{} nLiterals:{} messageWordSize:{} coderType:{} streamSize:{} probabilityBits:{} (int)opt:{} min:{} max:{} literalsPackingOffset:{} literalsPackingWidth:{} nDictWords:{} nDataWords:{} nLiteralWords:{}", + value, md.nStreams, md.messageLength, md.nLiterals, md.messageWordSize, md.coderType, md.streamSize, md.probabilityBits, (int)md.opt, md.min, md.max, md.literalsPackingOffset, md.literalsPackingWidth, md.nDictWords, md.nDataWords, md.nLiteralWords); + return offset + static_cast(*srcIt); } - decoder->process(block.getData() + block.getNData(), dest, md.messageLength, literals); - } else { // data was stored as is - using destPtr_t = typename std::iterator_traits::pointer; - destPtr_t srcBegin = reinterpret_cast(block.payload); - destPtr_t srcEnd = srcBegin + md.messageLength * sizeof(dest_t); - std::copy(srcBegin, srcEnd, dest); - // std::memcpy(dest, block.payload, md.messageLength * sizeof(dest_t)); + // normal case: + return offset; + }(); + for (size_t i = 0; i < messageLength; ++i) { + *dest++ = value; } + } else { + rans::unpack(srcIt, messageLength, dest, packingWidth, offset); } - return {0, md.getUncompressedSize(), md.getCompressedSize()}; -} + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::decodeCopyImpl(dst_IT dest, int slot) const +{ + // get references to the right data + const auto& block = mBlocks[slot]; + const auto& md = mMetadata[slot]; + + using dest_t = typename std::iterator_traits::value_type; + using decoder_t = typename rans::compat::decoder_type; + using destPtr_t = typename std::iterator_traits::pointer; + + destPtr_t srcBegin = reinterpret_cast(block.payload); + destPtr_t srcEnd = srcBegin + md.messageLength * sizeof(dest_t); + std::copy(srcBegin, srcEnd, dest); + + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; +}; ///_____________________________________________________________________________ template @@ -828,147 +1115,417 @@ o2::ctf::CTFIOSize EncodedBlocks::encode(const input_IT srcBegin, uint8_t symbolTablePrecision, // encoding into Metadata::OptStore opt, // option for data compression buffer_T* buffer, // optional buffer (vector) providing memory for encoded blocks - const void* encoderExt, // optional external encoder + const std::any& encoderExt, // optional external encoder float memfc) // memory allocation margin factor { - - using storageBuffer_t = W; - using input_t = typename std::iterator_traits::value_type; - using ransEncoder_t = typename rans::LiteralEncoder64; - using ransState_t = typename ransEncoder_t::coder_t; - using ransStream_t = typename ransEncoder_t::stream_t; - - // assert at compile time that output types align so that padding is not necessary. - static_assert(std::is_same_v); - static_assert(std::is_same_v); - // fill a new block assert(slot == mRegistry.nFilledBlocks); mRegistry.nFilledBlocks++; const size_t messageLength = std::distance(srcBegin, srcEnd); // cover three cases: - // * empty source message: no entropy coding + // * empty source message: no co // * source message to pass through without any entropy coding // * source message where entropy coding should be applied // case 1: empty source message if (messageLength == 0) { - mMetadata[slot] = Metadata{0, 0, sizeof(input_t), sizeof(ransState_t), sizeof(ransStream_t), symbolTablePrecision, Metadata::OptStore::NODATA, 0, 0, 0, 0, 0}; + mMetadata[slot] = Metadata{}; + mMetadata[slot].opt = Metadata::OptStore::NODATA; return {}; } + if (detail::mayEEncode(opt)) { + const ANSHeader& ansVersion = getANSHeader(); + if (ansVersion == ANSVersionCompat) { + return entropyCodeRANSCompat(srcBegin, srcEnd, slot, symbolTablePrecision, buffer, encoderExt, memfc); + } else if (ansVersion == ANSVersion1) { + return entropyCodeRANSV1(srcBegin, srcEnd, slot, opt, buffer, encoderExt, memfc); + } else { + throw std::runtime_error(fmt::format("Unsupported ANS Coder Version: {}.{}", ansVersion.majorVersion, ansVersion.minorVersion)); + } + } else if (detail::mayPack(opt)) { + return pack(srcBegin, srcEnd, slot, buffer); + } else { + return store(srcBegin, srcEnd, slot, opt, buffer); + } +}; - auto* thisBlock = &mBlocks[slot]; - auto* thisMetadata = &mMetadata[slot]; +template +template +[[nodiscard]] auto EncodedBlocks::expandStorage(size_t slot, size_t nElements, T* buffer) -> decltype(auto) +{ + // after previous relocation this (hence its data members) are not guaranteed to be valid + auto* old = get(buffer->data()); + auto* thisBlock = &(old->mBlocks[slot]); + auto* thisMetadata = &(old->mMetadata[slot]); // resize underlying buffer of block if necessary and update all pointers. - auto expandStorage = [&](int additionalElements) { - auto* const blockHead = get(thisBlock->registry->head); // extract pointer from the block, as "this" might be invalid - const size_t additionalSize = blockHead->estimateBlockSize(additionalElements); // size in bytes!!! - if (additionalSize >= thisBlock->registry->getFreeSize()) { - LOG(debug) << "Slot " << slot << ": free size: " << thisBlock->registry->getFreeSize() << ", need " << additionalSize << " for " << additionalElements << " words"; - if (buffer) { - blockHead->expand(*buffer, blockHead->size() + (additionalSize - blockHead->getFreeSize())); - thisMetadata = &(get(buffer->data())->mMetadata[slot]); - thisBlock = &(get(buffer->data())->mBlocks[slot]); // in case of resizing this and any this.xxx becomes invalid - } else { - throw std::runtime_error("no room for encoded block in provided container"); - } + auto* const blockHead = get(thisBlock->registry->head); // extract pointer from the block, as "this" might be invalid + const size_t additionalSize = blockHead->estimateBlockSize(nElements); // additionalSize is in bytes!!! + if (additionalSize >= thisBlock->registry->getFreeSize()) { + LOGP(debug, "Slot {} with {} available words needs to allocate {} bytes for a total of {} words.", slot, thisBlock->registry->getFreeSize(), additionalSize, nElements); + if (buffer) { + blockHead->expand(*buffer, blockHead->size() + (additionalSize - blockHead->getFreeSize())); + thisMetadata = &(get(buffer->data())->mMetadata[slot]); + thisBlock = &(get(buffer->data())->mBlocks[slot]); // in case of resizing this and any this.xxx becomes invalid + } else { + throw std::runtime_error("failed to allocate additional space in provided external buffer"); } - }; + } + return std::make_pair(thisBlock, thisMetadata); +}; - // case 3: message where entropy coding should be applied - if (opt == Metadata::OptStore::EENCODE) { - // build symbol statistics - constexpr size_t SizeEstMarginAbs = 10 * 1024; - const float SizeEstMarginRel = 1.5 * memfc; +template +template +o2::ctf::CTFIOSize EncodedBlocks::entropyCodeRANSCompat(const input_IT srcBegin, const input_IT srcEnd, int slot, uint8_t symbolTablePrecision, buffer_T* buffer, const std::any& encoderExt, float memfc) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename rans::compat::encoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; - const auto [inplaceEncoder, frequencyTable] = [&]() { - if (encoderExt) { - return std::make_tuple(ransEncoder_t{}, rans::FrequencyTable{}); + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + // build symbol statistics + constexpr size_t SizeEstMarginAbs = 10 * 1024; + const float SizeEstMarginRel = 1.5 * memfc; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + rans::DenseHistogram frequencyTable{}; + rans::compat::encoder_type inplaceEncoder{}; + + try { + std::tie(inplaceEncoder, frequencyTable) = [&]() { + if (encoderExt.has_value()) { + return std::make_tuple(ransEncoder_t{}, rans::DenseHistogram{}); } else { - rans::FrequencyTable frequencyTable = rans::makeFrequencyTableFromSamples(srcBegin, srcEnd); - RenormedFrequencyTable renormedFrequencyTable = rans::renorm(frequencyTable, symbolTablePrecision); - return std::make_tuple(ransEncoder_t{renormedFrequencyTable}, frequencyTable); + auto histogram = rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd); + auto encoder = rans::compat::makeEncoder::fromHistogram(histogram, symbolTablePrecision); + return std::make_tuple(std::move(encoder), std::move(histogram)); } }(); - ransEncoder_t const* const encoder = encoderExt ? reinterpret_cast(encoderExt) : &inplaceEncoder; - - // estimate size of encode buffer - int dataSize = rans::calculateMaxBufferSize(messageLength, encoder->getAlphabetRangeBits(), sizeof(input_t)); // size in bytes - // preliminary expansion of storage based on dict size + estimated size of encode buffer - dataSize = SizeEstMarginAbs + int(SizeEstMarginRel * (dataSize / sizeof(storageBuffer_t))) + (sizeof(input_t) < sizeof(storageBuffer_t)); // size in words of output stream - expandStorage(frequencyTable.size() + dataSize); - // store dictionary first - if (!frequencyTable.empty()) { - thisBlock->storeDict(frequencyTable.size(), frequencyTable.data()); - LOGP(debug, "StoreDict {} bytes, offs: {}:{}", frequencyTable.size() * sizeof(W), thisBlock->getOffsDict(), thisBlock->getOffsDict() + frequencyTable.size() * sizeof(W)); + } catch (const rans::HistogramError& error) { + LOGP(warning, "Failed to build Dictionary for rANS encoding, using fallback option"); + return store(srcBegin, srcEnd, slot, this->FallbackStorageType, buffer); + } + const ransEncoder_t& encoder = encoderExt.has_value() ? std::any_cast(encoderExt) : inplaceEncoder; + + // estimate size of encode buffer + int dataSize = rans::compat::calculateMaxBufferSizeB(messageLength, rans::compat::getAlphabetRangeBits(encoder.getSymbolTable())); // size in bytes + // preliminary expansion of storage based on dict size + estimated size of encode buffer + dataSize = SizeEstMarginAbs + int(SizeEstMarginRel * (dataSize / sizeof(storageBuffer_t))) + (sizeof(input_t) < sizeof(storageBuffer_t)); // size in words of output stream + + const auto view = rans::trim(rans::makeHistogramView(frequencyTable)); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, view.size() + dataSize, buffer); + + // store dictionary first + + if (!view.empty()) { + thisBlock->storeDict(view.size(), view.data()); + LOGP(debug, "StoreDict {} bytes, offs: {}:{}", view.size() * sizeof(W), thisBlock->getOffsDict(), thisBlock->getOffsDict() + view.size() * sizeof(W)); + } + // vector of incompressible literal symbols + std::vector literals; + // directly encode source message into block buffer. + storageBuffer_t* const blockBufferBegin = thisBlock->getCreateData(); + const size_t maxBufferSize = thisBlock->registry->getFreeSize(); // note: "this" might be not valid after expandStorage call!!! + const auto [encodedMessageEnd, literalsEnd] = encoder.process(srcBegin, srcEnd, blockBufferBegin, std::back_inserter(literals)); + rans::utils::checkBounds(encodedMessageEnd, blockBufferBegin + maxBufferSize / sizeof(W)); + dataSize = encodedMessageEnd - thisBlock->getDataPointer(); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(W), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(W)); + // update the size claimed by encode message directly inside the block + + // store incompressible symbols if any + const size_t nLiteralSymbols = literals.size(); + const size_t nLiteralWords = [&]() { + if (!literals.empty()) { + const size_t nSymbols = literals.size(); + // introduce padding in case literals don't align; + const size_t nLiteralSymbolsPadded = calculatePaddedSize(nSymbols); + literals.resize(nLiteralSymbolsPadded, {}); + + const size_t nLiteralStorageElems = calculateNDestTElements(nSymbols); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, nLiteralStorageElems, buffer); + thisBlock->storeLiterals(nLiteralStorageElems, reinterpret_cast(literals.data())); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", nLiteralStorageElems * sizeof(W), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + nLiteralStorageElems * sizeof(W)); + return nLiteralStorageElems; } - // vector of incompressible literal symbols - std::vector literals; - // directly encode source message into block buffer. - storageBuffer_t* const blockBufferBegin = thisBlock->getCreateData(); - const size_t maxBufferSize = thisBlock->registry->getFreeSize(); // note: "this" might be not valid after expandStorage call!!! - const auto encodedMessageEnd = encoder->process(srcBegin, srcEnd, blockBufferBegin, literals); - rans::utils::checkBounds(encodedMessageEnd, blockBufferBegin + maxBufferSize / sizeof(W)); - dataSize = encodedMessageEnd - thisBlock->getDataPointer(); - thisBlock->setNData(dataSize); - thisBlock->realignBlock(); - LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(W), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(W)); - // update the size claimed by encode message directly inside the block - - // store incompressible symbols if any - const size_t nLiteralSymbols = literals.size(); - const size_t nLiteralWords = [&]() { - if (!literals.empty()) { - const size_t nSymbols = literals.size(); - // introduce padding in case literals don't align; - const size_t nLiteralSymbolsPadded = calculatePaddedSize(nSymbols); - literals.resize(nLiteralSymbolsPadded, {}); - - const size_t nLiteralStorageElems = calculateNDestTElements(nSymbols); - expandStorage(nLiteralStorageElems); - thisBlock->storeLiterals(nLiteralStorageElems, reinterpret_cast(literals.data())); - LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", nLiteralStorageElems * sizeof(W), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + nLiteralStorageElems * sizeof(W)); - return nLiteralStorageElems; - } - return size_t(0); - }(); + return size_t(0); + }(); + + LOGP(debug, "Min, {} Max, {}, size, {}, nSamples {}", view.getMin(), view.getMax(), view.size(), frequencyTable.getNumSamples()); + + *thisMetadata = detail::makeMetadataRansCompat(encoder.getNStreams(), + messageLength, + nLiteralSymbols, + encoder.getSymbolTable().getPrecision(), + view.getMin(), + view.getMax(), + view.size(), + dataSize, + nLiteralWords); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +} + +template +template +o2::ctf::CTFIOSize EncodedBlocks::entropyCodeRANSV1(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer, const std::any& encoderExt, float memfc) +{ + CTFIOSize encoderStatistics{}; + + const size_t nSamples = std::distance(srcBegin, srcEnd); + if (detail::mayPack(opt) && nSamples < PackingThreshold) { + encoderStatistics = pack(srcBegin, srcEnd, slot, buffer); + } else { - *thisMetadata = Metadata{messageLength, - nLiteralSymbols, - sizeof(input_t), - sizeof(ransState_t), - sizeof(ransStream_t), - static_cast(encoder->getSymbolTablePrecision()), - opt, - encoder->getMinSymbol(), - encoder->getMaxSymbol(), - static_cast(frequencyTable.size()), - dataSize, - static_cast(nLiteralWords)}; - } else { // store original data w/o EEncoding - // FIXME(milettri): we should be able to do without an intermediate vector; - // provided iterator is not necessarily pointer, need to use intermediate vector!!! - - // introduce padding in case literals don't align; - const size_t nSourceElemsPadded = calculatePaddedSize(messageLength); - std::vector tmp(nSourceElemsPadded, {}); - std::copy(srcBegin, srcEnd, std::begin(tmp)); - - const size_t nBufferElems = calculateNDestTElements(messageLength); - expandStorage(nBufferElems); - thisBlock->storeData(nBufferElems, reinterpret_cast(tmp.data())); - - *thisMetadata = Metadata{messageLength, 0, sizeof(input_t), sizeof(ransState_t), sizeof(storageBuffer_t), symbolTablePrecision, opt, 0, 0, 0, static_cast(nBufferElems), 0}; + if (encoderExt.has_value()) { + encoderStatistics = encodeRANSV1External(srcBegin, srcEnd, slot, encoderExt, buffer, memfc); + } else { + encoderStatistics = encodeRANSV1Inplace(srcBegin, srcEnd, slot, opt, buffer, memfc); + } } - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return encoderStatistics; } +template +template +CTFIOSize EncodedBlocks::encodeRANSV1External(const input_IT srcBegin, const input_IT srcEnd, int slot, const std::any& encoderExt, buffer_T* buffer, double_t sizeEstimateSafetyFactor) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename internal::ExternalEntropyCoder::encoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; + + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + internal::ExternalEntropyCoder encoder{std::any_cast(encoderExt)}; + + const size_t payloadSizeWords = encoder.template computePayloadSizeEstimate(messageLength); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, payloadSizeWords, buffer); + + // encode payload + auto encodedMessageEnd = encoder.encode(srcBegin, srcEnd, thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + const size_t dataSize = std::distance(thisBlock->getCreateData(), encodedMessageEnd); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(storageBuffer_t)); + // update the size claimed by encoded message directly inside the block + + // encode literals + size_t literalsSize = 0; + if (encoder.getNIncompressibleSamples() > 0) { + const size_t literalsBufferSizeWords = encoder.template computePackedIncompressibleSize(); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, literalsBufferSizeWords, buffer); + auto literalsEnd = encoder.writeIncompressible(thisBlock->getCreateLiterals(), thisBlock->getEndOfBlock()); + literalsSize = std::distance(thisBlock->getCreateLiterals(), literalsEnd); + thisBlock->setNLiterals(literalsSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", literalsSize * sizeof(storageBuffer_t), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + literalsSize * sizeof(storageBuffer_t)); + } + + // write metadata + const auto& symbolTable = encoder.getEncoder().getSymbolTable(); + *thisMetadata = detail::makeMetadataRansV1(encoder.getEncoder().getNStreams(), + rans::utils::getStreamingLowerBound_v, + messageLength, + encoder.getNIncompressibleSamples(), + symbolTable.getPrecision(), + symbolTable.getOffset(), + symbolTable.getOffset() + symbolTable.size(), + encoder.getIncompressibleSymbolOffset(), + encoder.getIncompressibleSymbolPackingBits(), + 0, + dataSize, + literalsSize); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + +template +template +CTFIOSize EncodedBlocks::encodeRANSV1Inplace(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer, double_t sizeEstimateSafetyFactor) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + using ransEncoder_t = typename rans::denseEncoder_type; + using ransState_t = typename ransEncoder_t::coder_type::state_type; + using ransStream_t = typename ransEncoder_t::stream_type; + + // assert at compile time that output types align so that padding is not necessary. + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + + internal::InplaceEntropyCoder encoder{}; + rans::SourceProxy proxy{srcBegin, srcEnd, [](input_IT begin, input_IT end) { + const size_t nSamples = std::distance(begin, end); + return (!std::is_pointer_v && (nSamples < rans::utils::pow2(23))); + }}; + + try { + if (proxy.isCached()) { + encoder = internal::InplaceEntropyCoder{proxy.beginCache(), proxy.endCache()}; + } else { + encoder = internal::InplaceEntropyCoder{proxy.beginIter(), proxy.endIter()}; + } + } catch (const rans::HistogramError& error) { + LOGP(warning, "Failed to build Dictionary for rANS encoding, using fallback option"); + if (proxy.isCached()) { + return store(proxy.beginCache(), proxy.endCache(), slot, this->FallbackStorageType, buffer); + } else { + return store(proxy.beginIter(), proxy.endIter(), slot, this->FallbackStorageType, buffer); + } + } + + const rans::Metrics& metrics = encoder.getMetrics(); + /* + if constexpr (sizeof(input_t) > 2) { + const auto& dp = metrics.getDatasetProperties(); + LOGP(info, "Metrics:{{slot: {}, numSamples: {}, min: {}, max: {}, alphabetRangeBits: {}, nUsedAlphabetSymbols: {}, preferPacking: {}}}", slot, dp.numSamples, dp.min, dp.max, dp.alphabetRangeBits, dp.nUsedAlphabetSymbols, metrics.getSizeEstimate().preferPacking()); + } + */ + if (detail::mayPack(opt) && metrics.getSizeEstimate().preferPacking()) { + if (proxy.isCached()) { + return pack(proxy.beginCache(), proxy.endCache(), slot, metrics, buffer); + } else { + return pack(proxy.beginIter(), proxy.endIter(), slot, metrics, buffer); + }; + } + + encoder.makeEncoder(); + + const rans::SizeEstimate sizeEstimate = metrics.getSizeEstimate(); + const size_t bufferSizeWords = rans::utils::nBytesTo((sizeEstimate.getCompressedDictionarySize() + + sizeEstimate.getCompressedDatasetSize() + + sizeEstimate.getIncompressibleSize()) * + sizeEstimateSafetyFactor); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, bufferSizeWords, buffer); + + // encode dict + auto encodedDictEnd = encoder.writeDictionary(thisBlock->getCreateDict(), thisBlock->getEndOfBlock()); + const size_t dictSize = std::distance(thisBlock->getCreateDict(), encodedDictEnd); + thisBlock->setNDict(dictSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreDict {} bytes, offs: {}:{}", dictSize * sizeof(storageBuffer_t), thisBlock->getOffsDict(), thisBlock->getOffsDict() + dictSize * sizeof(storageBuffer_t)); + + // encode payload + auto encodedMessageEnd = thisBlock->getCreateData(); + if (proxy.isCached()) { + encodedMessageEnd = encoder.encode(proxy.beginCache(), proxy.endCache(), thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + } else { + encodedMessageEnd = encoder.encode(proxy.beginIter(), proxy.endIter(), thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + } + const size_t dataSize = std::distance(thisBlock->getCreateData(), encodedMessageEnd); + thisBlock->setNData(dataSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreData {} bytes, offs: {}:{}", dataSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + dataSize * sizeof(storageBuffer_t)); + // update the size claimed by encoded message directly inside the block + + // encode literals + size_t literalsSize{}; + if (encoder.getNIncompressibleSamples() > 0) { + auto literalsEnd = encoder.writeIncompressible(thisBlock->getCreateLiterals(), thisBlock->getEndOfBlock()); + literalsSize = std::distance(thisBlock->getCreateLiterals(), literalsEnd); + thisBlock->setNLiterals(literalsSize); + thisBlock->realignBlock(); + LOGP(debug, "StoreLiterals {} bytes, offs: {}:{}", literalsSize * sizeof(storageBuffer_t), thisBlock->getOffsLiterals(), thisBlock->getOffsLiterals() + literalsSize * sizeof(storageBuffer_t)); + } + + // write metadata + *thisMetadata = detail::makeMetadataRansV1(encoder.getNStreams(), + rans::utils::getStreamingLowerBound_v, + std::distance(srcBegin, srcEnd), + encoder.getNIncompressibleSamples(), + encoder.getSymbolTablePrecision(), + *metrics.getCoderProperties().min, + *metrics.getCoderProperties().max, + metrics.getDatasetProperties().min, + metrics.getDatasetProperties().alphabetRangeBits, + dictSize, + dataSize, + literalsSize); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; // namespace ctf + +template +template +o2::ctf::CTFIOSize EncodedBlocks::pack(const input_IT srcBegin, const input_IT srcEnd, int slot, rans::Metrics::value_type> metrics, buffer_T* buffer) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + + const size_t messageLength = metrics.getDatasetProperties().numSamples; + const auto alphabetRangeBits = metrics.getDatasetProperties().alphabetRangeBits; + + auto* thisBlock = &mBlocks[slot]; + auto* thisMetadata = &mMetadata[slot]; + size_t packedSize = 0; + + if (messageLength == 0) { + *thisMetadata = detail::makeMetadataPack(0, 0, 0, 0); + } else if (metrics.getDatasetProperties().alphabetRangeBits == 0) { + *thisMetadata = detail::makeMetadataPack(messageLength, 0, *srcBegin, 0); + } else { + internal::Packer packer{metrics}; + size_t packingBufferWords = packer.template getPackingBufferSize(messageLength); + std::tie(thisBlock, thisMetadata) = expandStorage(slot, packingBufferWords, buffer); + auto packedMessageEnd = packer.pack(srcBegin, srcEnd, thisBlock->getCreateData(), thisBlock->getEndOfBlock()); + packedSize = std::distance(thisBlock->getCreateData(), packedMessageEnd); + *thisMetadata = detail::makeMetadataPack(messageLength, packer.getPackingWidth(), packer.getOffset(), packedSize); + thisBlock->setNData(packedSize); + thisBlock->realignBlock(); + } + + LOGP(debug, "StoreData {} bytes, offs: {}:{}", packedSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + packedSize * sizeof(storageBuffer_t)); + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + +template +template +o2::ctf::CTFIOSize EncodedBlocks::store(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer) +{ + using storageBuffer_t = W; + using input_t = typename std::iterator_traits::value_type; + + const size_t messageLength = std::distance(srcBegin, srcEnd); + // introduce padding in case literals don't align; + const size_t nSourceElemsPadded = calculatePaddedSize(messageLength); + std::vector tmp(nSourceElemsPadded, {}); + std::copy(srcBegin, srcEnd, std::begin(tmp)); + + const size_t nBufferElems = calculateNDestTElements(messageLength); + auto [thisBlock, thisMetadata] = expandStorage(slot, nBufferElems, buffer); + thisBlock->storeData(nBufferElems, reinterpret_cast(tmp.data())); + + *thisMetadata = detail::makeMetadataStore(messageLength, opt, nBufferElems); + + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; +}; + /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables template -std::vector EncodedBlocks::createDictionaryBlocks(const std::vector& vfreq, const std::vector& vmd) +std::vector EncodedBlocks::createDictionaryBlocks(const std::vector>& vfreq, const std::vector& vmd) { + if (vfreq.size() != N) { throw std::runtime_error(fmt::format("mismatch between the size of frequencies vector {} and number of blocks {}", vfreq.size(), N)); } @@ -979,9 +1536,12 @@ std::vector EncodedBlocks::createDictionaryBlocks(const std::vect std::vector vdict(sz); // memory space for dictionary auto dictBlocks = create(vdict.data(), sz); for (int ib = 0; ib < N; ib++) { - if (vfreq[ib].size()) { - LOG(info) << "adding dictionary of " << vfreq[ib].size() << " words for block " << ib << ", min/max= " << vfreq[ib].getMinSymbol() << "/" << vfreq[ib].getMaxSymbol(); - dictBlocks->mBlocks[ib].storeDict(vfreq[ib].size(), vfreq[ib].data()); + const auto& thisHistogram = vfreq[ib]; + const auto view = rans::trim(rans::makeHistogramView(thisHistogram)); + + if (!view.empty()) { + LOG(info) << "adding dictionary of " << view.size() << " words for block " << ib << ", min/max= " << view.getMin() << "/" << view.getMax(); + dictBlocks->mBlocks[ib].storeDict(view.size(), view.data()); dictBlocks = get(vdict.data()); // !!! rellocation might have invalidated dictBlocks pointer dictBlocks->mMetadata[ib] = vmd[ib]; dictBlocks->mMetadata[ib].opt = Metadata::OptStore::ROOTCompression; // we will compress the dictionary with root! @@ -991,8 +1551,9 @@ std::vector EncodedBlocks::createDictionaryBlocks(const std::vect } dictBlocks->mRegistry.nFilledBlocks++; } - return std::move(vdict); + return vdict; } +#endif template void EncodedBlocks::dump(const std::string& prefix, int ncol) const diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h index e696e3798c47d..1408e0e11714a 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/FileMetaData.h @@ -16,6 +16,7 @@ #include #include +#include namespace o2 { @@ -26,25 +27,25 @@ class DataTakingContext; namespace dataformats { -struct FileMetaData { // https://docs.google.com/document/d/1nH9EZEFBSpuZwOWs3RBcfy-6aRChgAqClBv6G06MjH4/edit - std::string LHCPeriod{}; // 1, LHC data taking period + detector name, in case of individual detector data stream, required - std::string lurl{}; // 3, the local EPN path to the CTF or calibration file, required - std::string type{}; // 4, raw or calib or other; default is other, optional - std::string guid{}; // 7, default is auto-generated, optional - std::string surl{}; // 8, the remote storage path where we store the data file, optional - std::string curl{}; // 9, the Grid catalogue path, optional - std::string md5{}; //10, default the checksum of the lurl file; only filled after a successful transfer, if needed, optional - std::string xxhash{}; //11, default calculated from the lurl file, only filled after a successful transfer, if needed, optional - std::string seName{}; // 12, default is taken from the configuration file, optional - std::string seioDaemons{}; // 13, default is taken from the configuration file, optional - std::string priority{}; // 14, low or high; default is low, optional - std::string detComposition{}; // 17, contains a list of detectors, optional - std::string run{}; // 2, run number, required - long ctime{}; // 5, default the timestamp of the lurl file, optional - size_t size{}; // 6, default the size of the lurl file, optional - int persistent{}; // 16, default is forever, optional +struct FileMetaData { // https://docs.google.com/document/d/1nH9EZEFBSpuZwOWs3RBcfy-6aRChgAqClBv6G06MjH4/edit + std::string LHCPeriod{}; // 1, LHC data taking period + detector name, in case of individual detector data stream, required + std::string lurl{}; // 3, the local EPN path to the CTF or calibration file, required + std::string type{}; // 4, raw or calib or other; default is other, optional + std::string guid{}; // 7, default is auto-generated, optional + std::string surl{}; // 8, the remote storage path where we store the data file, optional + std::string curl{}; // 9, the Grid catalogue path, optional + std::string md5{}; // 10, default the checksum of the lurl file; only filled after a successful transfer, if needed, optional + std::string xxhash{}; // 11, default calculated from the lurl file, only filled after a successful transfer, if needed, optional + std::string seName{}; // 12, default is taken from the configuration file, optional + std::string seioDaemons{}; // 13, default is taken from the configuration file, optional + std::string priority{}; // 14, low or high; default is low, optional + std::string detComposition{}; // 17, contains a list of detectors, optional + std::string run{}; // 2, run number, required + long ctime{}; // 5, default the timestamp of the lurl file, optional + size_t size{}; // 6, default the size of the lurl file, optional + int persistent{}; // 16, default is forever, optional std::vector tfOrbits; // 15, comma-sep. list of 1st orbits of TFs, optional - bool fillFileData(const std::string& fname); + bool fillFileData(const std::string& fname, bool fillmd5 = false, const std::string& tmpEnding = ""); void setDataTakingContext(const o2::framework::DataTakingContext& dtc); std::string asString() const; }; diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h new file mode 100644 index 0000000000000..975522767dce1 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h @@ -0,0 +1,206 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Metadata.h +/// \brief Metadata required to decode a Block + +/// The Metadata required to decoded the CTF of particular detector. + +#ifndef ALICEO2_METADATA_H +#define ALICEO2_METADATA_H + +#include +#include + +namespace o2::ctf +{ + +struct Metadata { + enum class OptStore : uint8_t { // describe how the store the data described by this metadata + EENCODE, // entropy encoding applied + ROOTCompression, // original data repacked to array with slot-size = streamSize and saved with root compression + NONE, // original data repacked to array with slot-size = streamSize and saved w/o compression + NODATA, // no data was provided + PACK, // use Bitpacking + EENCODE_OR_PACK // decide at runtime if to encode or pack + }; + uint8_t nStreams = 0; // Amount of concurrent Streams used by the encoder. only used by rANS version >=1. + size_t messageLength = 0; // Message length (multiply with messageWordSize to get size in Bytes). + size_t nLiterals = 0; // Number of samples that were stored as literals. + uint8_t messageWordSize = 0; // size in Bytes of a symbol in the encoded message. + uint8_t coderType = 0; // what type of CTF Coder is used? (32 vs 64 bit coders). + uint8_t streamSize = 0; // number of Bytes emmitted during rANS stream out (ransCompat) or lower renorming bound (ransV1). + uint8_t probabilityBits = 0; // The encoder renormed the distribution of source symbols to sum up to 2^probabilityBits. + OptStore opt = OptStore::EENCODE; // The type of storage operation that was conducted. + int32_t min = 0; // min symbol of the source dataset. + int32_t max = 0; // max symbol of the source dataset. + int32_t literalsPackingOffset = 0; // Offset from 0 used for bit packing of literal (incompressible) symbols. only used by rANS version >=1. + uint8_t literalsPackingWidth = 0; // Amount of bits used to pack literal (incompressible) symbols. only used by rANS version >=1. + int nDictWords = 0; // Amount of words used to store the encoding dictionary. + int nDataWords = 0; // Amount of words used to store the actual data. + int nLiteralWords = 0; // Amount of words used to store literal (incompressible) samples. + + /** + * @brief Uncompressed size of stored data in bytes + * + * @return size_t Uncompressed size in bytes + */ + size_t getUncompressedSize() const { return messageLength * messageWordSize; } + + /** + * @brief Size of the stored, compressed data in multiples of the underlying buffer word size + * + * @return size_t The size in multiples of the underlying buffer word size + * @warning This size is in number of words of the underlying storage buffer. + * Multiply with the size of the storage buffer type to get the correct size in bytes. + */ + size_t getCompressedSize() const { return nDictWords + nDataWords + nLiteralWords; } + void clear() + { + nStreams = 0; + messageLength = 0; + nLiterals = 0; + messageWordSize = 0; + coderType = 0; + streamSize = 0; + probabilityBits = 0; + min = 0; + max = 0; + literalsPackingOffset = 0; + literalsPackingWidth = 0; + nDictWords = 0; + nDataWords = 0; + nLiteralWords = 0; + } + ClassDefNV(Metadata, 3); +}; + +namespace detail +{ + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansCompat(size_t nStreams, size_t messageLength, + size_t nLiterals, size_t symbolTablePrecision, + source_T min, source_T max, size_t dictWords, + size_t dataWords, size_t literalWords) noexcept +{ + return Metadata{ + static_cast(nStreams), + messageLength, + nLiterals, + static_cast(sizeof(source_T)), + static_cast(sizeof(state_T)), + static_cast(sizeof(stream_T)), + static_cast(symbolTablePrecision), + Metadata::OptStore::EENCODE, + static_cast(min), + static_cast(max), + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(dictWords), + static_cast(dataWords), + static_cast(literalWords)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansDict(size_t symbolTablePrecision, source_T min, + source_T max, size_t dictWords, ctf::Metadata::OptStore optStore) noexcept +{ + return Metadata{ + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(0), + static_cast(symbolTablePrecision), + optStore, + static_cast(min), + static_cast(max), + static_cast(0), + static_cast(0), + static_cast(dictWords), + static_cast(0), + static_cast(0)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataRansV1(size_t nStreams, size_t streamingLowerBound, size_t messageLength, + size_t nLiterals, size_t symbolTablePrecision, + source_T dictMin, source_T dictMax, + source_T literalsOffset, size_t literalsPackingWidth, size_t dictWords, + size_t dataWords, size_t literalWords) noexcept +{ + return Metadata{ + static_cast(nStreams), + messageLength, + nLiterals, + static_cast(sizeof(source_T)), + static_cast(sizeof(state_T)), + static_cast(streamingLowerBound), + static_cast(symbolTablePrecision), + Metadata::OptStore::EENCODE, + static_cast(dictMin), + static_cast(dictMax), + static_cast(literalsOffset), + static_cast(literalsPackingWidth), + static_cast(dictWords), + static_cast(dataWords), + static_cast(literalWords)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataPack(size_t messageLength, size_t packingWidth, + source_T packingOffset, size_t dataWords) noexcept +{ + return Metadata{ + static_cast(1), + messageLength, + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(0), + static_cast(packingWidth), + Metadata::OptStore::PACK, + static_cast(packingOffset), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(dataWords), + static_cast(0)}; +}; + +template +[[nodiscard]] inline constexpr Metadata makeMetadataStore(size_t messageLength, Metadata::OptStore opStore, size_t dataWords) noexcept +{ + return Metadata{ + static_cast(0), + messageLength, + static_cast(0), + static_cast(sizeof(source_T)), + static_cast(0), + static_cast(sizeof(buffer_T)), + static_cast(0), + opStore, + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(0), + static_cast(dataWords), + static_cast(0)}; +}; + +} // namespace detail +} // namespace o2::ctf + +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 92fc2bfa46a6d..37c4b790d181b 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -95,7 +95,12 @@ class SimTraits /*IT3*/ VS{ "IT3Hit" }, /*TRK*/ VS{ "TRKHit" }, /*FT3*/ VS{ "FT3Hit" }, - /*FCT*/ VS{ "FCTHit" } + /*FCT*/ VS{ "FCTHit" }, + /*TF3*/ VS{ "TF3Hit" }, + /*RCH*/ VS{ "RCHHit" }, + /*MI3*/ VS{ "MI3Hit" }, + /*ECL*/ VS{ "ECLHit" }, + /*FD */ VS{ "FDHit" } #endif }; // clang-format on @@ -163,7 +168,10 @@ namespace tpc { class HitGroup; } - +namespace focal +{ +class Hit; +} namespace detectors { @@ -227,6 +235,10 @@ template <> struct DetIDToHitTypes { using HitType = o2::tpc::HitGroup; }; +template <> +struct DetIDToHitTypes { + using HitType = o2::focal::Hit; +}; #ifdef ENABLE_UPGRADES template <> struct DetIDToHitTypes { diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h new file mode 100644 index 0000000000000..5335514278e26 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h @@ -0,0 +1,119 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CTFEntropyCoder.h +/// \author michael.lettrich@cern.ch +/// \brief Interface for externally provided rANS entropy coders + +#ifndef ALICEO2_EXTERNALENTROPYCODER_H_ +#define ALICEO2_EXTERNALENTROPYCODER_H_ + +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" + +#include "rANS/encode.h" +#include "rANS/factory.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/serialize.h" + +namespace o2::ctf::internal +{ + +template +class ExternalEntropyCoder +{ + public: + using source_type = source_T; + using encoder_type = typename rans::denseEncoder_type; + + ExternalEntropyCoder(const encoder_type& encoder); + + [[nodiscard]] inline const encoder_type& getEncoder() const noexcept { return *mEncoder; }; + + template + [[nodiscard]] inline size_t computePayloadSizeEstimate(size_t nElements, double_t safetyFactor = 1); + + template + [[nodiscard]] dst_IT encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd); + + [[nodiscard]] inline size_t getNIncompressibleSamples() const noexcept { return mIncompressibleBuffer.size(); }; + + [[nodiscard]] inline source_type getIncompressibleSymbolOffset() const noexcept { return mIncompressiblePacker.getOffset(); }; + + [[nodiscard]] inline size_t getIncompressibleSymbolPackingBits() const noexcept { return mIncompressiblePacker.getPackingWidth(); }; + + template + [[nodiscard]] size_t computePackedIncompressibleSize() const noexcept; + + template + [[nodiscard]] dst_T* writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) const; + + private: + const encoder_type* mEncoder{}; + std::vector mIncompressibleBuffer{}; + Packer mIncompressiblePacker{}; +}; + +template +ExternalEntropyCoder::ExternalEntropyCoder(const encoder_type& encoder) : mEncoder{&encoder} +{ + if (!getEncoder().getSymbolTable().hasEscapeSymbol()) { + throw std::runtime_error("External entropy encoder must be able to handle incompressible symbols."); + } +}; + +template +template +[[nodiscard]] inline size_t ExternalEntropyCoder::computePayloadSizeEstimate(size_t nElements, double_t safetyFactor) +{ + constexpr size_t Overhead = 10 * rans::utils::pow2(10); // 10KB overhead safety margin + const double_t RelativeSafetyFactor = 2.0 * safetyFactor; + const size_t messageSizeB = nElements * sizeof(source_type); + return rans::utils::nBytesTo(std::ceil(RelativeSafetyFactor * messageSizeB) + Overhead); +} + +template +template +[[nodiscard]] dst_IT ExternalEntropyCoder::encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd) +{ + const size_t incompressibleSymbolFrequency = [&]() { + const auto& symbolTable = mEncoder->getSymbolTable(); + const double_t incompressibleSymbolProbability = static_cast(symbolTable.getEscapeSymbol().getFrequency()) / rans::utils::pow2(symbolTable.getPrecision()); + return std::ceil(std::distance(srcBegin, srcEnd) * incompressibleSymbolProbability); + }(); + + mIncompressibleBuffer.reserve(incompressibleSymbolFrequency); + auto [encodedMessageEnd, literalsEnd] = mEncoder->process(srcBegin, srcEnd, dstBegin, std::back_inserter(mIncompressibleBuffer)); + rans::utils::checkBounds(encodedMessageEnd, dstEnd); + mIncompressiblePacker = Packer{mIncompressibleBuffer.data(), mIncompressibleBuffer.data() + mIncompressibleBuffer.size()}; + + return encodedMessageEnd; +}; + +template +template +[[nodiscard]] inline size_t ExternalEntropyCoder::computePackedIncompressibleSize() const noexcept +{ + return mIncompressiblePacker.template getPackingBufferSize(mIncompressibleBuffer.size()); +}; + +template +template +[[nodiscard]] inline dst_T* ExternalEntropyCoder::writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) const +{ + return mIncompressiblePacker.pack(mIncompressibleBuffer.data(), mIncompressibleBuffer.size(), dstBegin, dstEnd); +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_EXTERNALENTROPYCODER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h new file mode 100644 index 0000000000000..8601469b34142 --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h @@ -0,0 +1,312 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CTFEntropyCoder.h +/// \author michael.lettrich@cern.ch +/// \brief ANS Entropy Coding and packing specialization for CTF Coders + +#ifndef ALICEO2_INPLACEENTROPYCODER_H_ +#define ALICEO2_INPLACEENTROPYCODER_H_ + +#include +#include +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" + +#include "rANS/encode.h" +#include "rANS/factory.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/serialize.h" + +namespace o2::ctf::internal +{ + +template +class InplaceEntropyCoder +{ + using dense_histogram_type = rans::DenseHistogram; + using adaptive_histogram_type = rans::AdaptiveHistogram; + using sparse_histogram_type = rans::SparseHistogram; + + using dense_encoder_type = rans::denseEncoder_type; + using adaptive_encoder_type = rans::adaptiveEncoder_type; + using sparse_encoder_type = rans::sparseEncoder_type; + + using dict_buffer_type = std::vector; + + public: + using source_type = source_T; + using metrics_type = rans::Metrics; + using packer_type = Packer; + using histogram_type = std::variant; + using encoder_type = std::variant; + using incompressible_buffer_type = std::vector; + + InplaceEntropyCoder() = default; + + template + InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd); + + template + InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + void makeEncoder(); + + // getters + + [[nodiscard]] inline const metrics_type& getMetrics() const noexcept { return mMetrics; }; + + [[nodiscard]] inline size_t getNIncompressibleSamples() const noexcept { return mIncompressibleBuffer.size(); }; + + [[nodiscard]] size_t getNStreams() const; + + [[nodiscard]] size_t getSymbolTablePrecision() const; + + template + [[nodiscard]] size_t getPackedIncompressibleSize() const noexcept; + + // operations + template + [[nodiscard]] dst_IT encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd); + + template + [[nodiscard]] dst_IT writeDictionary(dst_IT dstBegin, dst_IT dstEnd); + + template + [[nodiscard]] dst_T* writeIncompressible(dst_T* dstBegin, dst_T* dstEnd); + + private: + template ::value_type) < 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + template ::value_type) == 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max); + + template ::value_type) < 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd); + + template ::value_type) == 4), bool> = true> + void init(source_IT srcBegin, source_IT srcEnd); + + template + void serializeDictionary(const container_T&); + + std::optional mHistogram{}; + metrics_type mMetrics{}; + std::optional mEncoder{}; + incompressible_buffer_type mIncompressibleBuffer{}; + dict_buffer_type mDictBuffer{}; + packer_type mIncompressiblePacker{}; +}; + +template +template +InplaceEntropyCoder::InplaceEntropyCoder(src_IT srcBegin, src_IT srcEnd) +{ + static_assert(std::is_same_v::value_type>); + + const size_t nSamples = std::distance(srcBegin, srcEnd); + if constexpr (std::is_pointer_v) { + if (sizeof(source_type) > 2 && nSamples > 0) { + const auto [min, max] = rans::internal::minmax(gsl::span(srcBegin, srcEnd)); + init(srcBegin, srcEnd, min, max); + } else { + init(srcBegin, srcEnd); + } + } else { + init(srcBegin, srcEnd); + } + + mIncompressiblePacker = Packer(mMetrics); +}; + +template +template +InplaceEntropyCoder::InplaceEntropyCoder(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + static_assert(std::is_same_v::value_type>); + init(srcBegin, srcEnd, min, max); + mIncompressiblePacker = Packer(mMetrics); +}; + +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getNStreams() const +{ + size_t nStreams{}; + std::visit([&, this](auto&& encoder) { nStreams = encoder.getNStreams(); }, *mEncoder); + return nStreams; +} + +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getSymbolTablePrecision() const +{ + size_t precision{}; + std::visit([&, this](auto&& encoder) { precision = encoder.getSymbolTable().getPrecision(); }, *mEncoder); + return precision; +} + +template +void InplaceEntropyCoder::makeEncoder() +{ + std::visit([this](auto&& histogram) { + auto renormed = rans::renorm(std::move(histogram), mMetrics); + + if (std::holds_alternative(*mHistogram)) { + serializeDictionary(renormed); + } + + const size_t rangeBits = rans::utils::getRangeBits(*mMetrics.getCoderProperties().min, *mMetrics.getCoderProperties().max); + const size_t nUsedAlphabetSymbols = mMetrics.getDatasetProperties().nUsedAlphabetSymbols; + + if (rangeBits <= 18) { + // dense symbol tables if they fit into cache, or source data covers the range of the alphabet well + mEncoder = encoder_type{std::in_place_type, renormed}; + } else if (nUsedAlphabetSymbols < rans::utils::pow2(14)) { + // sparse symbol table makes sense if it fits into L3 Cache + mEncoder = encoder_type{std::in_place_type, renormed}; + } else { + // adaptive symbol table otherwise + mEncoder = encoder_type{std::in_place_type, renormed}; + } + }, + *mHistogram); +}; + +template +template +[[nodiscard]] dst_IT InplaceEntropyCoder::encode(src_IT srcBegin, src_IT srcEnd, dst_IT dstBegin, dst_IT dstEnd) +{ + static_assert(std::is_same_v::value_type>); + + dst_IT messageEnd = dstBegin; + + std::visit([&, this](auto&& encoder) { + if (encoder.getSymbolTable().hasEscapeSymbol()) { + mIncompressibleBuffer.reserve(*mMetrics.getCoderProperties().nIncompressibleSamples); + auto [encodedMessageEnd, literalsEnd] = encoder.process(srcBegin, srcEnd, dstBegin, std::back_inserter(mIncompressibleBuffer)); + messageEnd = encodedMessageEnd; + } else { + messageEnd = encoder.process(srcBegin, srcEnd, dstBegin); + } + rans::utils::checkBounds(messageEnd, dstEnd); + }, + *mEncoder); + + return messageEnd; +}; + +template +template +[[nodiscard]] inline dst_IT InplaceEntropyCoder::writeDictionary(dst_IT dstBegin, dst_IT dstEnd) +{ + static_assert(std::is_pointer_v); + + using dst_type = std::remove_pointer_t; + + dst_IT ret{}; + if (mDictBuffer.empty()) { + std::visit([&, this](auto&& encoder) { ret = rans::compressRenormedDictionary(encoder.getSymbolTable(), dstBegin); }, *mEncoder); + } else { + // copy + std::memcpy(dstBegin, mDictBuffer.data(), mDictBuffer.size()); + + // determine location of end + auto end = reinterpret_cast(dstBegin) + mDictBuffer.size(); + // realign pointer + constexpr size_t alignment = std::alignment_of_v; + end += (alignment - reinterpret_cast(end) % alignment) % alignment; + // and convert it back to ret + ret = reinterpret_cast(end); + } + + rans::utils::checkBounds(ret, dstEnd); + return ret; +}; + +template +template +inline dst_T* InplaceEntropyCoder::writeIncompressible(dst_T* dstBegin, dst_T* dstEnd) +{ + return mIncompressiblePacker.pack(mIncompressibleBuffer.data(), mIncompressibleBuffer.size(), dstBegin, dstEnd); +}; + +template +template +[[nodiscard]] inline size_t InplaceEntropyCoder::getPackedIncompressibleSize() const noexcept +{ + return mIncompressiblePacker.template getPackingBufferSize(getNIncompressibleSamples()); +} + +template +template ::value_type) < 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + mHistogram.emplace(histogram_type{rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd)}); + mMetrics = metrics_type{std::get(*mHistogram), min, max}; +}; + +template +template ::value_type) == 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd, source_type min, source_type max) +{ + const size_t nSamples = std::distance(srcBegin, srcEnd); + const size_t rangeBits = rans::utils::getRangeBits(min, max); + + if ((rangeBits <= 18) || ((nSamples / rans::utils::pow2(rangeBits)) >= 0.80)) { + // either the range of source symbols is distrubuted such that it fits into L3 Cache + // Or it is possible for the data to cover a very significant fraction of the total [min,max] range + mHistogram = histogram_type{std::in_place_type, rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd, min, max)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } else if (nSamples / rans::utils::pow2(rangeBits) <= 0.3) { + // or the range of source symbols is spread very thinly accross a large range + mHistogram = histogram_type{std::in_place_type, rans::makeSparseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } else { + // no strong evidence of either extreme case + mHistogram = histogram_type{std::in_place_type, rans::makeAdaptiveHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram), min, max}; + } +}; + +template +template ::value_type) < 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd) +{ + mHistogram = histogram_type{std::in_place_type, rans::makeDenseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram)}; +}; + +template +template ::value_type) == 4), bool>> +void InplaceEntropyCoder::init(source_IT srcBegin, source_IT srcEnd) +{ + mHistogram = histogram_type{std::in_place_type, rans::makeSparseHistogram::fromSamples(srcBegin, srcEnd)}; + mMetrics = metrics_type{std::get(*mHistogram)}; +}; + +template +template +void InplaceEntropyCoder::serializeDictionary(const container_T& renormedHistogram) +{ + + mDictBuffer.resize(mMetrics.getSizeEstimate().getCompressedDictionarySize(), 0); + auto end = rans::compressRenormedDictionary(renormedHistogram, mDictBuffer.data()); + rans::utils::checkBounds(end, mDictBuffer.data() + mDictBuffer.size()); + mDictBuffer.resize(std::distance(mDictBuffer.data(), end)); + + assert(mDictBuffer.size() > 0); +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_INPLACEENTROPYCODER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h new file mode 100644 index 0000000000000..fa11e9f6dac6d --- /dev/null +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h @@ -0,0 +1,124 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Packer.h +/// \author michael.lettrich@cern.ch +/// \brief Interfaces for BitPacking using librans + +#ifndef ALICEO2_PACKER_H_ +#define ALICEO2_PACKER_H_ + +#ifndef __CLING__ +#include "rANS/pack.h" +#include "rANS/metrics.h" +#endif + +namespace o2::ctf::internal +{ + +template +class Packer +{ + public: + using source_type = source_T; + + Packer() = default; +#ifndef __CLING__ + explicit Packer(rans::Metrics& metrics) : mOffset{metrics.getDatasetProperties().min}, + mPackingWidth{metrics.getDatasetProperties().alphabetRangeBits} {}; +#endif + + template + Packer(source_IT srcBegin, source_IT srcEnd); + + [[nodiscard]] inline source_type getOffset() const noexcept { return mOffset; }; + + [[nodiscard]] inline size_t getPackingWidth() const noexcept { return mPackingWidth; }; + + template + [[nodiscard]] size_t getPackingBufferSize(size_t messageLength) const noexcept; + + template + [[nodiscard]] dst_T* pack(source_IT srcBegin, source_IT srcEnd, dst_T* dstBegin, dst_T* dstEnd) const; + + template + [[nodiscard]] dst_T* pack(const source_T* __restrict srcBegin, size_t extent, dst_T* dstBegin, dst_T* dstEnd) const; + + private: + source_type mOffset{}; + size_t mPackingWidth{}; +}; + +template +template +Packer::Packer(source_IT srcBegin, source_IT srcEnd) +{ +#ifndef __CLING__ + static_assert(rans::utils::isCompatibleIter_v); + if (srcBegin != srcEnd) { + + const auto [min, max] = [&]() { + if constexpr (std::is_pointer_v) { + return rans::utils::minmax(gsl::span(srcBegin, srcEnd)); + } else { + const auto [minIter, maxIter] = std::minmax_element(srcBegin, srcEnd); + return std::make_pair(*minIter, *maxIter); + } + }(); + + mOffset = min; + mPackingWidth = rans::utils::getRangeBits(min, max); + } +#endif +}; + +template +template +[[nodiscard]] inline size_t Packer::getPackingBufferSize(size_t messageLength) const noexcept +{ +#ifndef __CLING__ + return rans::computePackingBufferSize(messageLength, mPackingWidth); +#else + return 0; +#endif +}; + +template +template +[[nodiscard]] inline dst_T* Packer::pack(const source_T* __restrict srcBegin, size_t extent, dst_T* dstBegin, dst_T* dstEnd) const +{ + return pack(srcBegin, srcBegin + extent, dstBegin, dstEnd); +} + +template +template +[[nodiscard]] dst_T* Packer::pack(source_IT srcBegin, source_IT srcEnd, dst_T* dstBegin, dst_T* dstEnd) const +{ + static_assert(std::is_same_v::value_type>); + size_t extent = std::distance(srcBegin, srcEnd); + + if (extent == 0) { + return dstBegin; + } +#ifndef __CLING__ + rans::BitPtr packEnd = rans::pack(srcBegin, extent, dstBegin, mPackingWidth, mOffset); + auto* end = packEnd.toPtr(); + ++end; // one past end iterator; + rans::utils::checkBounds(end, dstEnd); + return end; +#else + return nullptr; +#endif +}; + +} // namespace o2::ctf::internal + +#endif /* ALICEO2_PACKER_H_ */ \ No newline at end of file diff --git a/DataFormats/Detectors/Common/src/AlignParam.cxx b/DataFormats/Detectors/Common/src/AlignParam.cxx index 90f2a349607a1..2061726a29c66 100644 --- a/DataFormats/Detectors/Common/src/AlignParam.cxx +++ b/DataFormats/Detectors/Common/src/AlignParam.cxx @@ -12,12 +12,12 @@ /// \file AlignParam.cxx /// \brief Implementation of the base alignment parameters class -#include #include #include #include #include +#include "Framework/Logger.h" #include "DetectorsCommonDataFormats/AlignParam.h" using namespace o2::detectors; @@ -26,8 +26,9 @@ using namespace o2::detectors; AlignParam::AlignParam(const char* symname, int algID, // volume symbolic name and its alignable ID double x, double y, double z, // delta translation double psi, double theta, double phi, // delta rotation - bool global) // global (preferable) or local delta definition - : mSymName(symname), mAlignableID(algID) + bool global, // global (preferable) or local delta definition + bool convertLocalToGlobal) // if local is provided, convert it to global + : mSymName(symname), mIsGlobal(global || convertLocalToGlobal), mAlignableID(algID) { /// standard constructor with 3 translation + 3 rotation parameters /// If the user explicitly sets the global variable to false then the @@ -35,23 +36,22 @@ AlignParam::AlignParam(const char* symname, int algID, // volume symbolic /// This requires to have a gGeoMenager active instance, otherwise the /// constructor will fail (no object created) - if (global) { - setGlobalParams(x, y, z, psi, theta, phi); - } else { + setParams(x, y, z, psi, theta, phi); + if (!global && convertLocalToGlobal) { setLocalParams(x, y, z, psi, theta, phi); } } //___________________________________________________ -AlignParam::AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global) - : mSymName(symname), mAlignableID(algID) +AlignParam::AlignParam(const char* symname, int algID, TGeoMatrix& m, bool global, bool convertLocalToGlobal) + : mSymName(symname), mIsGlobal(global || convertLocalToGlobal), mAlignableID(algID) { setTranslation(m); if (!setRotation(m)) { const double* rot = m.GetRotationMatrix(); throw std::runtime_error(fmt::format("Failed to extract roll-pitch-yall angles from [[{},{},{}], [{},{},{}], [{},{},{}] for {}", rot[0], rot[1], rot[2], rot[3], rot[4], rot[5], rot[6], rot[7], rot[8], symname)); } - if (!global && !setLocalParams(mX, mY, mZ, mPsi, mTheta, mPhi)) { + if (!global && convertLocalToGlobal && !setLocalParams(mX, mY, mZ, mPsi, mTheta, mPhi)) { throw std::runtime_error(fmt::format("Alignment creation for {} failed: geomManager is absent", symname)); } } @@ -223,6 +223,10 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const // In case that the TGeo was not initialized or not closed, // returns false and the object parameters are not set. // + m = createMatrix(); + if (!mIsGlobal) { + return true; + } if (!gGeoManager || !gGeoManager->IsClosed()) { LOG(error) << "Can't get the local alignment object parameters! gGeoManager doesn't exist or it is still open!"; return false; @@ -247,7 +251,6 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const LOG(error) << "Volume name or path " << symname << " is not valid!"; return false; } - m = createMatrix(); TGeoHMatrix gprime, gprimeinv; gprime = *node->GetMatrix(); gprimeinv = gprime.Inverse(); @@ -258,7 +261,7 @@ bool AlignParam::createLocalMatrix(TGeoHMatrix& m) const } //_____________________________________________________________________________ -bool AlignParam::applyToGeometry() const +bool AlignParam::applyToGeometry(int printLevel) const { /// Apply the current alignment object to the TGeo geometry /// This method returns FALSE if the symname of the object was not @@ -302,18 +305,26 @@ bool AlignParam::applyToGeometry() const } // double threshold = 0.001; + TGeoHMatrix* align = new TGeoHMatrix(createMatrix()); + if (mIsGlobal) { + align->Multiply(node->GetMatrix()); + align->MultiplyLeft(node->GetMatrix(node->GetLevel() - 1)->Inverse()); + } else { + align->MultiplyLeft(node->GetOriginalMatrix()); + } - TGeoHMatrix gprime = *node->GetMatrix(); - TGeoHMatrix align = createMatrix(); - gprime.MultiplyLeft(&align); - TGeoHMatrix* ginv = new TGeoHMatrix; // TGeoPhysicalNode takes and manages raw pointer, need naked new! - TGeoHMatrix* g = node->GetMatrix(node->GetLevel() - 1); - *ginv = g->Inverse(); - *ginv *= gprime; - - LOG(debug) << "Aligning volume " << symname; - - node->Align(ginv); + node->Align(align); + + if (getLevel() <= printLevel) { + LOGP(info, "{:*^100}", symname); + LOGP(info, " - Alignment parameter:"); + print(); + LOGP(info, " - Alignment matrix:"); + align->Print(); + LOGP(info, " - Node:"); + node->Print(); + LOGP(info, "{:~^100}", symname); + } return true; } @@ -347,8 +358,8 @@ int AlignParam::getLevel() const void AlignParam::print() const { // print parameters - printf("%s : %6d | X: %+e Y: %+e Z: %+e | pitch: %+e roll: %+e yaw: %e\n", getSymName().c_str(), getAlignableID(), getX(), - getY(), getZ(), getPsi(), getTheta(), getPhi()); + printf("%s (Lvl:%2d): %6d | %s | tra: X: %+e Y: %+e Z: %+e | pitch: %+e roll: %+e yaw: %e\n", getSymName().c_str(), getLevel(), getAlignableID(), (mIsGlobal) ? "G" : "L", + getX(), getY(), getZ(), getPsi(), getTheta(), getPhi()); } //_____________________________________________________________________________ @@ -359,6 +370,14 @@ void AlignParam::setGlobalParams(double x, double y, double z, double psi, doubl setRotation(psi, theta, phi); } +//_____________________________________________________________________________ +void AlignParam::setParams(double x, double y, double z, double psi, double theta, double phi) +{ + /// set parameters of global delta + setTranslation(x, y, z); + setRotation(psi, theta, phi); +} + //_____________________________________________________________________________ void AlignParam::setRotation(double psi, double theta, double phi) { diff --git a/DataFormats/Detectors/Common/src/CTFHeader.cxx b/DataFormats/Detectors/Common/src/CTFHeader.cxx index e704b46c1e240..4332f8aecd3d6 100644 --- a/DataFormats/Detectors/Common/src/CTFHeader.cxx +++ b/DataFormats/Detectors/Common/src/CTFHeader.cxx @@ -18,7 +18,7 @@ using DetID = o2::detectors::DetID; /// describe itsel as a string std::string CTFHeader::describe() const { - return fmt::format("Run:{:07d} TF:{} Orbit:{:08d} CteationTime:{} Detectors: {}", run, tfCounter, firstTForbit, creationTime, DetID::getNames(detectors)); + return fmt::format("Run:{:07d} TF:{} Orbit:{:08d} CreationTime:{} Detectors: {}", run, tfCounter, firstTForbit, creationTime, DetID::getNames(detectors)); } void CTFHeader::print() const diff --git a/DataFormats/Detectors/Common/src/FileMetaData.cxx b/DataFormats/Detectors/Common/src/FileMetaData.cxx index 7a025648a32d3..f9230fd8ac7a3 100644 --- a/DataFormats/Detectors/Common/src/FileMetaData.cxx +++ b/DataFormats/Detectors/Common/src/FileMetaData.cxx @@ -13,6 +13,7 @@ #include "DetectorsCommonDataFormats/FileMetaData.h" #include "Framework/DataTakingContext.h" +#include "CommonUtils/StringUtils.h" #include #include #include @@ -20,14 +21,22 @@ using namespace o2::dataformats; -bool FileMetaData::fillFileData(const std::string& fname) +bool FileMetaData::fillFileData(const std::string& fname, bool fillmd5, const std::string& tmpEnding) { + // fill metadata for fname, accounting that the fname might be temporary one while the real one is fnameFinal try { lurl = std::filesystem::canonical(fname).string(); size = std::filesystem::file_size(lurl); - std::unique_ptr md5ptr{TMD5::FileChecksum(fname.c_str())}; - md5 = md5ptr->AsString(); + if (fillmd5) { + std::unique_ptr md5ptr{TMD5::FileChecksum(fname.c_str())}; + if (md5ptr) { + md5 = md5ptr->AsString(); + } + } ctime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (tmpEnding.size()) { + o2::utils::Str::rtrim(lurl, tmpEnding); + } } catch (std::exception const& e) { LOG(error) << "Failed to fill metadata for file " << fname << ", reason: " << e.what(); return false; diff --git a/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx new file mode 100644 index 0000000000000..6b688fcebc7f7 --- /dev/null +++ b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx @@ -0,0 +1,350 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file testCTFEntropyCoder +/// @author Michael Lettrich +/// @brief Test entropy coding using rANS algorithm + +#define BOOST_TEST_MODULE Test CTFEntropyCoder class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DetectorsCommonDataFormats/internal/Packer.h" +#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" +#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" +#include "rANS/histogram.h" +#include "rANS/metrics.h" +#include "rANS/factory.h" +#include "rANS/iterator.h" + +using namespace o2; + +using buffer_type = uint32_t; +using source_types = boost::mp11::mp_list; + +template +class SourceMessage +{ + public: + SourceMessage(size_t messageSize, source_T max = std::numeric_limits::max(), source_T min = std::numeric_limits::min()) : mMin{min}, mMax{max} + { + if (mSourceMessage.empty()) { + std::mt19937 mt(0); // same seed we want always the same distrubution of random numbers; + assert(max >= min); + const size_t draws = (max - min) + 1; + const double probability = 0.5; + std::binomial_distribution dist(draws, probability); + mSourceMessage.resize(messageSize); + std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt, min]() -> source_T { return static_cast(dist(mt)) + min; }); + } + } + + inline constexpr source_T getMin() const noexcept { return mMin; }; + inline constexpr source_T getMax() const noexcept { return mMax; }; + inline constexpr auto& get() const noexcept { return mSourceMessage; }; + + private: + source_T mMin{}; + source_T mMax{}; + std::vector mSourceMessage{}; +}; + +class SourceMessageProxy +{ + public: + SourceMessageProxy() = default; + + template + const auto& getMessage() const noexcept + { + if constexpr (std::is_same_v) { + return sourceMessage8u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage8.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage16u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage16.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage32u.get(); + } else if constexpr (std::is_same_v) { + return sourceMessage32.get(); + } else { + throw std::runtime_error{"unsupported source type"}; + } + }; + + private: + inline static constexpr size_t MessageSize = rans::utils::pow2(10); + SourceMessage sourceMessage8u{MessageSize}; + SourceMessage sourceMessage8{MessageSize}; + SourceMessage sourceMessage16u{MessageSize}; + SourceMessage sourceMessage16{MessageSize}; + SourceMessage sourceMessage32u{MessageSize, rans::utils::pow2(27)}; + SourceMessage sourceMessage32{MessageSize, rans::utils::pow2(26), -static_cast(rans::utils::pow2(26))}; +}; + +inline const SourceMessageProxy MessageProxy{}; + +template +void encodeInplace(source_IT begin, source_IT end) +{ + using source_type = typename std::iterator_traits::value_type; + + ctf::internal::InplaceEntropyCoder entropyCoder{begin, end}; + // BOOST_CHECK_THROW(entropyCoder.getEncoder(), std::runtime_error); + entropyCoder.makeEncoder(); + + const rans::Metrics& metrics = entropyCoder.getMetrics(); + const rans::SizeEstimate sizeEstimate = metrics.getSizeEstimate(); + + LOGP(info, "dataset[{},{}], coder[{},{}]", metrics.getDatasetProperties().min, metrics.getDatasetProperties().max, *metrics.getCoderProperties().min, *metrics.getCoderProperties().max); + + std::vector encodeBuffer(sizeEstimate.getCompressedDatasetSize(), 0); + std::vector literalSymbolsBuffer(sizeEstimate.getIncompressibleSize(), 0); + std::vector dictBuffer(sizeEstimate.getCompressedDictionarySize(), 0); + + auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + auto dictEnd = entropyCoder.writeDictionary(dictBuffer.data(), dictBuffer.data() + dictBuffer.size()); + // decode + const auto& coderProperties = metrics.getCoderProperties(); + auto decoder = rans::makeDecoder<>::fromRenormed(rans::readRenormedDictionary(dictBuffer.data(), dictEnd, + *coderProperties.min, *coderProperties.max, + *coderProperties.renormingPrecisionBits)); + std::vector literals(entropyCoder.getNIncompressibleSamples()); + + const auto& datasetPropterties = metrics.getDatasetProperties(); + rans::unpack(literalSymbolsBuffer.data(), literals.size(), literals.data(), + datasetPropterties.alphabetRangeBits, datasetPropterties.min); + + size_t messageLength = std::distance(begin, end); + std::vector sourceBuffer(messageLength, 0); + + decoder.process(encoderEnd, sourceBuffer.data(), messageLength, entropyCoder.getNStreams(), literals.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS(sourceBuffer.begin(), sourceBuffer.end(), begin, end); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderEmpty, source_T, source_types) +{ + std::vector testMessage{}; + encodeInplace(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderPTR, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeInplace(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testInplaceEncoderIter, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeInplace(testMessage.begin(), testMessage.end()); +}; + +template +class ShiftFunctor + +{ + public: + template + inline value_T operator()(iterA_T iterA, iterB_T iterB) const + { + return *iterB + (static_cast(*iterA) << shift); + }; + + template + inline void operator()(iterA_T iterA, iterB_T iterB, value_T value) const + { + *iterA = value >> shift; + *iterB = value & ((0x1 << shift) - 0x1); + }; +}; + +template +auto makeInputIterators(iterA_T iterA, iterB_T iterB, size_t nElements, F functor) +{ + using namespace o2::rans::utils; + + return std::make_tuple(rans::CombinedInputIterator{iterA, iterB, functor}, + rans::CombinedInputIterator{advanceIter(iterA, nElements), advanceIter(iterB, nElements), functor}); +}; + +BOOST_AUTO_TEST_CASE(testInplaceEncoderCombinedIterator) +{ + + const auto& testMessage1 = MessageProxy.getMessage(); + const auto& testMessage2 = MessageProxy.getMessage(); + + auto [begin, end] = makeInputIterators(testMessage1.data(), testMessage2.data(), testMessage1.size(), ShiftFunctor()>{}); + + encodeInplace(begin, end); +}; + +class ExternalEncoderDecoderProxy +{ + public: + ExternalEncoderDecoderProxy() + { + SourceMessageProxy proxy{}; + + auto renormed8u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed8 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed16u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed16 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed32u = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + auto renormed32 = rans::renorm(rans::makeDenseHistogram::fromSamples(proxy.getMessage().begin(), proxy.getMessage().end()), rans::RenormingPolicy::ForceIncompressible); + + encoder8u = rans::makeDenseEncoder<>::fromRenormed(renormed8u); + encoder8 = rans::makeDenseEncoder<>::fromRenormed(renormed8); + encoder16u = rans::makeDenseEncoder<>::fromRenormed(renormed16u); + encoder16 = rans::makeDenseEncoder<>::fromRenormed(renormed16); + encoder32u = rans::makeDenseEncoder<>::fromRenormed(renormed32u); + encoder32 = rans::makeDenseEncoder<>::fromRenormed(renormed32); + + decoder8u = rans::makeDecoder<>::fromRenormed(renormed8u); + decoder8 = rans::makeDecoder<>::fromRenormed(renormed8); + decoder16u = rans::makeDecoder<>::fromRenormed(renormed16u); + decoder16 = rans::makeDecoder<>::fromRenormed(renormed16); + decoder32u = rans::makeDecoder<>::fromRenormed(renormed32u); + decoder32 = rans::makeDecoder<>::fromRenormed(renormed32); + } + + template + const auto& getEncoder() const noexcept + { + if constexpr (std::is_same_v) { + return encoder8u; + } else if constexpr (std::is_same_v) { + return encoder8; + } else if constexpr (std::is_same_v) { + return encoder16u; + } else if constexpr (std::is_same_v) { + return encoder16; + } else if constexpr (std::is_same_v) { + return encoder32u; + } else if constexpr (std::is_same_v) { + return encoder32; + } else { + throw std::runtime_error{"unsupported encoder type"}; + } + }; + + template + const auto& getDecoder() const noexcept + { + if constexpr (std::is_same_v) { + return decoder8u; + } else if constexpr (std::is_same_v) { + return decoder8; + } else if constexpr (std::is_same_v) { + return decoder16u; + } else if constexpr (std::is_same_v) { + return decoder16; + } else if constexpr (std::is_same_v) { + return decoder32u; + } else if constexpr (std::is_same_v) { + return decoder32; + } else { + throw std::runtime_error{"unsupported encoder type"}; + } + }; + + private: + rans::denseEncoder_type encoder8u{}; + rans::denseEncoder_type encoder8{}; + rans::denseEncoder_type encoder16u{}; + rans::denseEncoder_type encoder16{}; + rans::denseEncoder_type encoder32u{}; + rans::denseEncoder_type encoder32{}; + + rans::defaultDecoder_type decoder8u{}; + rans::defaultDecoder_type decoder8{}; + rans::defaultDecoder_type decoder16u{}; + rans::defaultDecoder_type decoder16{}; + rans::defaultDecoder_type decoder32u{}; + rans::defaultDecoder_type decoder32{}; +}; + +ExternalEncoderDecoderProxy ExternalEncoders{}; + +template +void encodeExternal(source_IT begin, source_IT end) +{ + using source_type = typename std::iterator_traits::value_type; + + ctf::internal::ExternalEntropyCoder entropyCoder{ExternalEncoders.getEncoder()}; + + const size_t sourceExtent = std::distance(begin, end); + std::vector encodeBuffer(entropyCoder.template computePayloadSizeEstimate(sourceExtent), 0); + auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); + + std::vector literalSymbolsBuffer(entropyCoder.template computePackedIncompressibleSize(), 0); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + + // decode + auto decoder = ExternalEncoders.getDecoder(); + std::vector literals((entropyCoder.getNIncompressibleSamples())); + + rans::unpack(literalSymbolsBuffer.data(), literals.size(), literals.data(), + entropyCoder.getIncompressibleSymbolPackingBits(), entropyCoder.getIncompressibleSymbolOffset()); + + size_t messageLength = std::distance(begin, end); + std::vector sourceBuffer(messageLength, 0); + + decoder.process(encoderEnd, sourceBuffer.data(), messageLength, entropyCoder.getEncoder().getNStreams(), literals.end()); + + BOOST_CHECK_EQUAL_COLLECTIONS(sourceBuffer.begin(), sourceBuffer.end(), begin, end); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderEmpty, source_T, source_types) +{ + std::vector testMessage{}; + encodeExternal(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderPTR, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeExternal(testMessage.data(), testMessage.data() + testMessage.size()); +}; + +BOOST_AUTO_TEST_CASE_TEMPLATE(testExternalEncoderIter, source_T, source_types) +{ + const auto& testMessage = MessageProxy.getMessage(); + encodeExternal(testMessage.begin(), testMessage.end()); +}; + +BOOST_AUTO_TEST_CASE(testExternalEncoderCombinedIterator) +{ + + const auto& testMessage1 = MessageProxy.getMessage(); + const auto& testMessage2 = MessageProxy.getMessage(); + + auto [begin, end] = makeInputIterators(testMessage1.data(), testMessage2.data(), testMessage1.size(), ShiftFunctor()>{}); + + encodeExternal(begin, end); +}; \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/CMakeLists.txt b/DataFormats/Detectors/EMCAL/CMakeLists.txt index 1e7c4f4739a3b..9c93bae30ddf6 100644 --- a/DataFormats/Detectors/EMCAL/CMakeLists.txt +++ b/DataFormats/Detectors/EMCAL/CMakeLists.txt @@ -20,6 +20,9 @@ o2_add_library(DataFormatsEMCAL src/EventHandler.cxx src/CTF.cxx src/ErrorTypeFEE.cxx + src/CellLabel.cxx + src/ClusterLabel.cxx + src/CompressedTriggerData.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::MathUtils @@ -37,7 +40,9 @@ o2_target_root_dictionary(DataFormatsEMCAL include/DataFormatsEMCAL/EventHandler.h include/DataFormatsEMCAL/MCLabel.h include/DataFormatsEMCAL/CTF.h - include/DataFormatsEMCAL/ErrorTypeFEE.h) + include/DataFormatsEMCAL/ErrorTypeFEE.h + include/DataFormatsEMCAL/CellLabel.h + include/DataFormatsEMCAL/ClusterLabel.h) o2_add_test(Cell SOURCES test/testCell.cxx diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h index 758e0a1fa0b47..e19fd17dea2ce 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/AnalysisCluster.h @@ -182,6 +182,9 @@ class AnalysisCluster float getCoreEnergy() const { return mCoreEnergy; } void setCoreEnergy(float energy) { mCoreEnergy = energy; } + float getFCross() const { return mFCross; } + void setFCross(float fCross) { mFCross = fCross; } + /// /// Returns TLorentzVector with momentum of the cluster. Only valid for clusters /// identified as photons or pi0 (overlapped gamma) produced on the vertex @@ -223,12 +226,13 @@ class AnalysisCluster float mTime = 0.; ///< Time of the digit/cell with maximal energy deposition bool mIsExotic = false; //! +#include +#include #include "DataFormatsEMCAL/Constants.h" namespace o2 @@ -27,104 +29,105 @@ namespace emcal /// \since March 6, 2019 /// \ingroup EMCALDataFormat /// -/// # Base format for EMCAL cell information in the Compressed Timeframe +/// # Cell content /// /// The cell class contains the relevant information for each tower per event /// - Tower ID /// - Energy of the raw fit /// - Time of the raw fit /// - Type of the cell +/// +/// # Compression for CTF +/// /// While cell type and tower ID have a predefined range based on the hardware /// design, energy and time have a finite resolution influenced by the resolution /// of the digitizer. This is used in order to compress the information stored /// in the compressed timeframe by not storing the full double values but instead /// assigning a certain amount of bits to each information. Therefore for certain -/// information (energy, time) precision loss has to be taken into account. -/// -/// # Internal structure and resolution -/// -/// The internal structure is a bit field compressing the information to -/// 48 bits. The definition of the bit field as well as the value range and the resolution -/// is listed in the table below: +/// information (energy, time) precision loss has to be taken into account. The number +/// of bits assigned to each data member in the encoding are as follows: /// -/// | Bits | Content | Resolution | Range | -/// |-------|---------------|---------------|-----------------------------| -/// | 0-14 | Tower ID | - | 0 to 17644 | -/// | 15-26 | Time (ns) | 0.73 ns | -600 to 900 ns | -/// | 27-40 | Energy (GeV) | 0.0153 GeV | 0 to 250 GeV | -/// | 41-42 | Cell type | - | 0=LG, 1=HG, 2=LEMon, 4=TRU | +/// | Content | Number of bits |Resolution | Range | +/// |---------------|----------------|--------------|-----------------------------| +/// | Tower ID | 15 | - | 0 to 17644 | +/// | Time (ns) | 11 | 0.73 ns | -600 to 900 ns | +/// | Energy (GeV) | 14 | 0.0153 GeV | 0 to 250 GeV | +/// | Cell type | 2 | - | 0=LG, 1=HG, 2=LEMon, 4=TRU | /// /// The remaining bits are 0 class Cell { public: - Cell(); - Cell(short tower, float energy, float time, ChannelType_t ctype = ChannelType_t::LOW_GAIN); + enum class EncoderVersion { + EncodingV0, + EncodingV1, + EncodingV2 + }; + /// \brief Default constructor + Cell() = default; + + /// \brief Constructor + /// \param tower Tower ID + /// \param energy Energy + /// \param timestamp Cell time + /// \param ctype Channel type + Cell(short tower, float energy, float timestamp, ChannelType_t ctype = ChannelType_t::LOW_GAIN); + + /// \brief Constructor, from encoded bit representation + /// \param tower Tower bitsets + /// \param energy Energy bits + /// \param timestamp Cell time bits + /// \param ctype Channel type bits + /// \param version Encoding version + Cell(uint16_t towerBits, uint16_t energyBits, uint16_t timestampBits, uint16_t channelBits, EncoderVersion version = EncoderVersion::EncodingV1); + + /// \brief Destructor ~Cell() = default; // override - void setTower(short tower) { getDataRepresentation()->mTowerID = tower; } - short getTower() const { return getDataRepresentation()->mTowerID; } + /// \brief Set the tower ID + /// \param tower Tower ID + void setTower(short tower) { mTowerID = tower; } + + /// \brief Get the tower ID + /// \return Tower ID + short getTower() const { return mTowerID; } /// \brief Set the time stamp - /// \param time Time in ns - /// - /// The time stamp is expressed in ns and has - /// a resolution of 1 ns. The time range which can - /// be stored is from -1023 to 1023 ns. In case the - /// range is exceeded the time is set to the limit - /// of the range. - void setTimeStamp(float time); + /// \param timestamp Time in ns + void setTimeStamp(float timestamp) { mTimestamp = timestamp; } /// \brief Get the time stamp /// \return Time in ns - /// - /// Time has a resolution of 1 ns and can cover - /// a range from -1023 to 1023 ns - float getTimeStamp() const; + float getTimeStamp() const { return mTimestamp; } /// \brief Set the energy of the cell /// \brief Energy of the cell in GeV - /// - /// The energy range covered by the cell - /// is 0 - 250 GeV, with a resolution of - /// 0.0153 GeV. In case an energy exceeding - /// the limits is provided the energy is - /// set to the limits (0 in case of negative - /// energy, 250. in case of energies > 250 GeV) - void setEnergy(float energy); + void setEnergy(float energy) { mEnergy = energy; } /// \brief Get the energy of the cell /// \return Energy of the cell - /// - /// The energy is truncated to a range - /// covering 0 to 250 GeV with a resolution - /// of 0.0153 GeV - float getEnergy() const; + float getEnergy() const { return mEnergy; } /// \brief Set the amplitude of the cell /// \param amplitude Cell amplitude - /// - /// See setEnergy for more information void setAmplitude(float amplitude) { setEnergy(amplitude); } /// \brief Get cell amplitude - /// \return cell Amplitude - /// - /// Set getEnergy for more information + /// \return Cell amplitude in GeV float getAmplitude() const { return getEnergy(); } /// \brief Set the type of the cell /// \param ctype Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) - void setType(ChannelType_t ctype) { getDataRepresentation()->mCellStatus = static_cast(ctype); } + void setType(ChannelType_t ctype) { mChannelType = ctype; } /// \brief Get the type of the cell /// \return Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) - ChannelType_t getType() const { return static_cast(getDataRepresentation()->mCellStatus); } + ChannelType_t getType() const { return mChannelType; } /// \brief Check whether the cell is of a given type /// \param ctype Type of the cell (HIGH_GAIN, LOW_GAIN, LEDMON, TRU) /// \return True if the type of the cell matches the requested type, false otherwise - bool isChannelType(ChannelType_t ctype) const { return getType() == ctype; } + bool isChannelType(ChannelType_t ctype) const { return mChannelType == ctype; } /// \brief Mark cell as low gain cell void setLowGain() { setType(ChannelType_t::LOW_GAIN); } @@ -154,38 +157,101 @@ class Cell /// \return True if the cell type is TRU, false otherwise Bool_t getTRU() const { return isChannelType(ChannelType_t::TRU); } + /// \brief Apply compression as done during writing to / reading from CTF + /// \param version Encoder version + void truncate(EncoderVersion version = EncoderVersion::EncodingV1); + void PrintStream(std::ostream& stream) const; - /// used for CTF encoding/decoding: access to packed data - void setPacked(uint16_t tower, uint16_t t, uint16_t en, uint16_t status) + /// \brief Initialize cell class from bit representation (for CTF decoding) + /// \param towerIDBits Encoded tower ID + /// \param timestampBits Encoded timestamp + /// \param energyBits Encoded energy + /// \param celltypeBits Encoded cell type + /// \param version Encoder version + void initialiseFromEncoded(uint16_t towerIDBits, uint16_t timestampBits, uint16_t energyBits, uint16_t celltypeBits, EncoderVersion version = EncoderVersion::EncodingV1) { - auto dt = getDataRepresentation(); - dt->mTowerID = tower; - dt->mTime = t; - dt->mEnergy = en; - dt->mCellStatus = status; + setEnergyEncoded(energyBits, static_cast(celltypeBits), version); + setTimestampEncoded(timestampBits); + setTowerIDEncoded(towerIDBits); + setChannelTypeEncoded(celltypeBits); } - auto getPackedTowerID() const { return getDataRepresentation()->mTowerID; } - auto getPackedTime() const { return getDataRepresentation()->mTime; } - auto getPackedEnergy() const { return getDataRepresentation()->mEnergy; } - auto getPackedCellStatus() const { return getDataRepresentation()->mCellStatus; } + /// \brief Get encoded bit representation of tower ID (for CTF) + /// \return Encoded bit representation + /// + /// Same as getTower - no compression applied for tower ID + uint16_t getTowerIDEncoded() const; + + /// \brief Get encoded bit representation of timestamp (for CTF) + /// \return Encoded bit representation + /// + /// The time stamp is expressed in ns and has + /// a resolution of 1 ns. The time range which can + /// be stored is from -1023 to 1023 ns. In case the + /// range is exceeded the time is set to the limit + /// of the range. + uint16_t getTimeStampEncoded() const; + + /// \brief Get encoded bit representation of energy (for CTF) + /// \param version Encoding verions + /// \return Encoded bit representation + /// + /// The energy range covered by the cell + /// is 0 - 250 GeV, with a resolution of + /// 0.0153 GeV. In case an energy exceeding + /// the limits is provided the energy is + /// set to the limits (0 in case of negative + /// energy, 250. in case of energies > 250 GeV) + uint16_t getEnergyEncoded(EncoderVersion version = EncoderVersion::EncodingV2) const; + + /// \brief Get encoded bit representation of cell type (for CTF) + /// \return Encoded bit representation + uint16_t getCellTypeEncoded() const; + + void initializeFromPackedBitfieldV0(const char* bitfield); + + static float getEnergyFromPackedBitfieldV0(const char* bitfield); + static float getTimeFromPackedBitfieldV0(const char* bitfield); + static ChannelType_t getCellTypeFromPackedBitfieldV0(const char* bitfield); + static short getTowerFromPackedBitfieldV0(const char* bitfield); + + static uint16_t encodeTime(float timestamp); + static uint16_t encodeEnergyV0(float energy); + static uint16_t encodeEnergyV1(float energy, ChannelType_t celltype); + static uint16_t encodeEnergyV2(float energy, ChannelType_t celltype); + static uint16_t V0toV1(uint16_t energybits, ChannelType_t celltype); + static uint16_t V0toV2(uint16_t energybits, ChannelType_t celltype); + static uint16_t V1toV2(uint16_t energybits, ChannelType_t celltype); + static float decodeTime(uint16_t timestampBits); + static float decodeEnergyV0(uint16_t energybits); + static float decodeEnergyV1(uint16_t energybits, ChannelType_t celltype); + static float decodeEnergyV2(uint16_t energybits, ChannelType_t celltype); private: - struct __attribute__((packed)) CellData { - uint16_t mTowerID : 15; ///< bits 0-14 Tower ID - uint16_t mTime : 11; ///< bits 15-25: Time (signed, can become negative after calibration) - uint16_t mEnergy : 14; ///< bits 26-39: Energy - uint16_t mCellStatus : 2; ///< bits 40-41: Cell status - uint16_t mZerod : 6; ///< bits 42-47: Zerod - }; + /// \brief Set cell energy from encoded bit representation (from CTF) + /// \param energyBits Bit representation of energy + /// \param cellTypeBits Bit representation of cell type + void setEnergyEncoded(uint16_t energyBits, uint16_t cellTypeBits, EncoderVersion version = EncoderVersion::EncodingV1); + + /// \brief Set cell time from encoded bit representation (from CTF) + /// \param timestampBits Bit representation of timestamp + void setTimestampEncoded(uint16_t timestampBits); + + /// \brief Set tower ID from encoded bit representation (from CTF) + /// \param towerIDBits Bit representation of towerID + void setTowerIDEncoded(uint16_t towerIDBits); - CellData* getDataRepresentation() { return reinterpret_cast(mCellWords); } - const CellData* getDataRepresentation() const { return reinterpret_cast(mCellWords); } + /// \brief Set cell type from encoded bit representation (from CTF) + /// \param channelTypeBits Bit representation of cell type + void setChannelTypeEncoded(uint16_t channelTypeBits); - uint16_t mCellWords[3]; ///< data word + float mEnergy = FLT_MIN; ///< Energy + float mTimestamp = FLT_MIN; ///< Timestamp + short mTowerID = SHRT_MAX; ///< Tower ID + ChannelType_t mChannelType = ChannelType_t::HIGH_GAIN; ///< Cell type - ClassDefNV(Cell, 1); + ClassDefNV(Cell, 3); }; /// \brief Stream operator for EMCAL cell diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h new file mode 100644 index 0000000000000..543e49fb06dd8 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CellLabel.h @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_EMCAL_CELLLABEL_H_ +#define ALICEO2_EMCAL_CELLLABEL_H_ + +#include +#include +#include +#include + +namespace o2 +{ + +namespace emcal +{ + +/// \class CellLabel +/// \brief cell class for MC particle IDs and their respective amplitude fraction +/// \ingroup EMCALDataFormat +/// \author Marvin Hemmer , Goethe university Frankfurt +/// \since December 13, 2023 +/// + +class CellLabel +{ + public: + // CellLabel() = default; + + /// \brief Constructor using std::vector by moving NOT copying + /// \param labels list of mc labels + /// \param amplitudeFractions list of amplitude fractions + CellLabel(std::vector labels, std::vector amplitudeFractions); + + /// \brief Constructor using gsl::span + /// \param labels list of mc labels + /// \param amplitudeFractions list of amplitude fractions + CellLabel(gsl::span labels, gsl::span amplitudeFractions); + + // ~CellLabel() = default; + // CellLabel(const CellLabel& clus) = default; + // CellLabel& operator=(const CellLabel& source) = default; + + /// \brief Getter of label size + /// \param index index which label to get + size_t GetLabelSize(void) const { return mLabels.size(); } + + /// \brief Getter for label + /// \param index index which label to get + int32_t GetLabel(size_t index) const { return mLabels[index]; } + + /// \brief Getter for labels + std::vector GetLabels() const { return mLabels; } + + /// \brief Getter for amplitude fraction + /// \param index index which amplitude fraction to get + float GetAmplitudeFraction(size_t index) const { return mAmplitudeFraction[index]; } + + /// \brief Getter for amplitude fractions + std::vector GetAmplitudeFractions() const { return mAmplitudeFraction; } + + /// \brief Getter for label with leading amplitude fraction + int32_t GetLeadingMCLabel() const; + + protected: + std::vector mLabels; ///< List of MC particles that generated the cluster, ordered in deposited energy. + std::vector mAmplitudeFraction; ///< List of the fraction of the cell energy coming from a MC particle. Index aligns with mLabels! +}; + +} // namespace emcal +} // namespace o2 +#endif // ALICEO2_EMCAL_CELLLABEL_H_ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h new file mode 100644 index 0000000000000..b6db76f91ff34 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ClusterLabel.h @@ -0,0 +1,95 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_EMCAL_CLUSTERLABEL_H_ +#define ALICEO2_EMCAL_CLUSTERLABEL_H_ + +#include +#include +#include +#include "Rtypes.h" + +namespace o2 +{ + +namespace emcal +{ + +/// \class ClusterLabel +/// \brief cluster class for MC particle IDs and their respective energy fraction +/// \ingroup EMCALDataFormat +/// \author Marvin Hemmer , Goethe university Frankfurt +/// \since December 13, 2023 +/// + +class ClusterLabel +{ + public: + /// \struct labelWithE + /// \brief Wrapper structure to make cluster label sortable in energy fraction + struct labelWithE { + + /// \brief Constructor + labelWithE() : label(0), energyFraction(0.) {} + + /// \brief Constructor + /// \param l MC label + /// \param e Energy fraction + labelWithE(int l, float e) : label(l), energyFraction(e) {} + + /// \brief Comparison lower operator comparing cells based on energy + /// + /// std::sort will require operator>= to compile. + /// + /// \param rhs Label to compare to + /// \return True if this cell is has a lower energy, false otherwise + bool operator>=(labelWithE const& rhs) const + { + return energyFraction >= rhs.energyFraction; + } + + float energyFraction; ///< Energy Fraction + int label; ///< MC label + }; + + // ClusterLabel() = default; + // ~ClusterLabel() = default; + // ClusterLabel(const ClusterLabel& clus) = default; + // ClusterLabel& operator=(const ClusterLabel& source) = default; + + /// \brief Clear the member variables + void clear(); + + /// \brief Add label and energy fraction to the + /// \param label MC label + /// \param energyFraction Energy fraction + void addValue(int label, float energyFraction); + + /// \brief Normalize the energy fraction + /// \param factor normalization factor + void normalize(float factor); + + /// \brief Getter for vector of labels + std::vector getLabels(); + + /// \brief Getter for vector of energy fractions + std::vector getEnergyFractions(); + + /// \brief Sort the labels and energy fraction in descending order (largest energy fraction to smallest) + void orderLabels(); + + protected: + std::vector mClusterLabels; ///< List of MC particles that generated the cluster, paired with energy fraction +}; + +} // namespace emcal +} // namespace o2 +#endif // ALICEO2_EMCAL_CLUSTERLABEL_H_ diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h new file mode 100644 index 0000000000000..5fbf2187ab5dd --- /dev/null +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/CompressedTriggerData.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H +#define ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H + +#include +#include + +namespace o2::emcal +{ + +/// \struct CompressedTRU +/// \brief Compressed reconstructed TRU information +/// \ingroup EMCALDataFormat +struct CompressedTRU { + uint8_t mTRUIndex; ///< TRU index + uint8_t mTriggerTime; ///< Trigger time of the TRU + bool mFired; ///< Fired status of the TRU + uint8_t mNumberOfPatches; ///< Number of patches found for the TRU +}; + +/// \struct CompressedTriggerPatch +/// \brief Compressed reconstructed L0 trigger patch information +/// \ingroup EMCALDataFormat +struct CompressedTriggerPatch { + uint8_t mTRUIndex; ///< Index of the TRU where the trigger patch has been found + uint8_t mPatchIndexInTRU; ///< Index of the trigger patch in the TRU + uint8_t mTime; ///< Reconstructed time of the trigger patch + uint16_t mADC; ///< ADC sum of the trigger patch +}; + +/// \struct CompressedL0TimeSum +/// \brief Compressed L0 timesum information +/// \ingroup EMCALDataFormat +struct CompressedL0TimeSum { + uint16_t mIndex; ///< Absolute ID of the FastOR + uint16_t mTimesum; ///< ADC value of the time-sum (4-integral) +}; + +/// \brief Output stream operator of the CompressedTRU +/// \param stream Stream to write to +/// \param tru TRU data to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedTRU& tru); + +/// \brief Output stream operator of the CompressedTriggerPatch +/// \param stream Stream to write to +/// \param patch Trigger patch to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedTriggerPatch& patch); + +/// \brief Output stream operator of the CompressedL0TimeSum +/// \param stream Stream to write to +/// \param timesum FastOR L0 timesum to be streamed +/// \return Stream after writing +std::ostream& operator<<(std::ostream& stream, const CompressedL0TimeSum& timesum); + +} // namespace o2::emcal + +#endif // ALICEO2_EMCAL_COMPRESSEDTRIGGERDATA_H \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h index 103a9419b5e08..015c77deccc46 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h @@ -103,6 +103,11 @@ constexpr int MAX_RANGE_ADC = 0x3FF; ///< Dynamic range of the ADCs (1 constexpr double EMCAL_TRU_ADCENERGY = 0.0786; ///< resolution of the TRU digitizer, @TODO check exact value } // namespace constants +namespace triggerbits +{ +constexpr uint32_t Inc = 0x1 << 20; ///< trigger bit marking incomplete event +} + enum FitAlgorithm { Standard = 0, ///< Standard raw fitter Gamma2 = 1, ///< Gamma2 raw fitter @@ -110,6 +115,35 @@ enum FitAlgorithm { NONE = 3 }; +enum STUtype_t { + ESTU = 0, ///< EMCAL STU + DSTU = 1 ///< DCAL STU +}; + +namespace STUparam //[0]->EMCAL STU, [1]->DCAL STU +{ +constexpr int FeeID[2] = {44, 45}; ///< FEE_ID in RDH +constexpr int NTRU[2] = {32, 14}; ///< number of TRUs +constexpr int CFG_nWords[2] = {17, 17}; ///< number of configuration words +constexpr int L1JetIndex_nWords[2] = {11, 11}; ///< number of words with Jet indices +constexpr int L0index_nWords[2] = {96, 42}; ///< number of words with null data +constexpr int L1GammaIndex_nWords[2] = {128, 56}; ///< number of words with Gamma indices +constexpr int Raw_nWords[2] = {1536, 672}; ///< number of words with ADC +constexpr int SubregionsEta[2] = {12, 12}; ///< number of subregions over eta +constexpr int SubregionsPhi[2] = {16, 10}; ///< number of subregions over phi +constexpr int PaloadSizeFull[2] = {1928, 866}; ///< number of words in full payload = 1944/882-16 +constexpr int PaloadSizeShort[2] = {391, 193}; ///< number of words in shorts payload = 407/209-16 +} // namespace STUparam + +namespace TRUparam +{ +constexpr int Nchannels = 96; ///< number of FastORs per TRU +constexpr int NchannelsOverEta = 8; ///< number of FastORs over Eta for full- and 2/3-size SMs +constexpr int NchannelsOverPhi = 12; ///< number of FastORs over Phi for full- and 2/3-size SMs +constexpr int NchannelsOverEta_long = 24; ///< number of FastORs over Eta for 1/3-size SMs +constexpr int NchannelsOverPhi_long = 4; ///< number of FastORs over Phi for 1/3-size SMs +} // namespace TRUparam + } // namespace emcal } // namespace o2 #endif diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h deleted file mode 100644 index 3c014d37e6f9e..0000000000000 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.h -/// \brief - -/// \class EMCALChannelCalibrator -/// \brief Class to store the data format for calibraton of the EMCal -/// \author Hannah Bossi, Yale University -/// \ingroup DetectorEMCAL -/// \since Feb 11, 2021 - -#ifndef ALICEO2_EMCALCHANNELDATA_H -#define ALICEO2_EMCALCHANNELDATA_H - -#include "Rtypes.h" - -namespace o2 -{ -namespace dataformats -{ -class EMCALChannelData -{ - public: - EMCALChannelData(int cellID, int timestamp, int flags = 0, int events) : mEMCALCellID(cellID), mTimestamp(timestamp), mFlags(flags){}; - EMCALChannelData() = default; - ~EMCALChannelData() = default; - - void setEMCALCellID(int index) { mEMCALCellID = index; } - int getEMCALCellID() const { return mEMCALCellID; } - - void setTimestamp(int ts) { mTimestamp = ts; } - int getTimestamp() const { return mTimestamp; } - - void setFlags(int flags) { mFlags = flags; } - float getFlags() const { return mFlags; } - - private: - int mEMCALCellID; ///< EMCal Cell ID - int mTimestamp; ///< timestamp in seconds - unsigned char mFlags; ///< bit mask with quality flags (to be defined) - - ClassDefNV(EMCALChannelData, 1); -}; -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h index 54f0d3be5f2a8..cc3540cd388d3 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/ErrorTypeFEE.h @@ -57,6 +57,9 @@ class ErrorTypeFEE FIT_ERROR, ///< Raw fit failed GEOMETRY_ERROR, ///< Decoded position outside EMCAL GAIN_ERROR, ///< Error due to gain type + LINK_ERROR, ///< Error due to missing DDL links + TRU_ERROR, ///< Errors from TRU data + STU_ERROR, ///< Error from STU data UNDEFINED ///< Error source undefined }; /// \brief Constructor @@ -97,6 +100,18 @@ class ErrorTypeFEE /// \param gainError Error code of the gain type error void setGainErrorType(int gainError) { setError(ErrorSource_t::GAIN_ERROR, gainError); } + /// \brief Set the error type as link error and store the error code + /// \param linkError Error code of the link error + void setLinkErrorTYpe(int linkError) { setError(ErrorSource_t::LINK_ERROR, linkError); } + + /// \brief Set the error as STU decoder error and store the error code + /// \param gainError Error code of the STU decoder error + void setSTUDecoderErrorType(int stuerror) { setError(ErrorSource_t::STU_ERROR, stuerror); } + + /// \brief Set the error as TRU decoder error and store the error code + /// \param gainError Error code of the TRU decoder error + void setTRUDecoderErrorType(int truerror) { setError(ErrorSource_t::TRU_ERROR, truerror); } + /// \brief Set the error type of the object /// \param errorsource Error type of the object void setErrorType(ErrorSource_t errorsource) { mErrorSource = errorsource; } @@ -157,6 +172,18 @@ class ErrorTypeFEE /// \return Error code (-1 in case the object is not a gain type error) int getGainTypeErrorType() const { return getRawErrorForType(ErrorSource_t::GAIN_ERROR); } + /// \brief Get the error code of the obect in case the object is a link error + /// \return Error code (-1 in case the object is not a gain type error) + int getLinkErrorType() const { return getRawErrorForType(ErrorSource_t::LINK_ERROR); } + + /// \brief Get the error code of the obect in case the object is a STU decoder error + /// \return Error code (-1 in case the object is not a STU decoder error) + int getSTUDecoderErrorType() const { return getRawErrorForType(ErrorSource_t::STU_ERROR); } + + /// \brief Get the error code of the obect in case the object is a TRU decoder error + /// \return Error code (-1 in case the object is not a STU decoder error) + int getTRUDecoderErrorType() const { return getRawErrorForType(ErrorSource_t::TRU_ERROR); } + /// \brief Get subspecification of the error /// \return Subspecification of the error int getSubspecification() const { return mSubspecification; } @@ -173,7 +200,7 @@ class ErrorTypeFEE /// \brief Get the number of error types /// \return Number of error types (including undefined) - static constexpr int getNumberOfErrorTypes() { return 7; } + static constexpr int getNumberOfErrorTypes() { return 10; } /// \brief Get the name of the error type /// \param errorTypeID ID of the error type diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h index a7e1f72ca1baf..1529eff104400 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h @@ -61,8 +61,9 @@ class TriggerRecord /// \enum TriggerBitsCoded_t /// \brief Position of trigger classes in compressed format enum TriggerBitsCoded_t { - PHYSTRIGGER, ///< Physics trigger - CALIBTRIGGER ///< Calib trigger + PHYSTRIGGER, ///< Physics trigger + CALIBTRIGGER, ///< Calib trigger + REJECTINCOMPLETE ///< Rejected as incomplete }; BCData mBCData; /// Bunch crossing data of the trigger DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) diff --git a/DataFormats/Detectors/EMCAL/src/Cell.cxx b/DataFormats/Detectors/EMCAL/src/Cell.cxx index 4132d99c2a224..261384d53ca2a 100644 --- a/DataFormats/Detectors/EMCAL/src/Cell.cxx +++ b/DataFormats/Detectors/EMCAL/src/Cell.cxx @@ -17,58 +17,334 @@ using namespace o2::emcal; +namespace TimeEncoding +{ const float TIME_SHIFT = 600., TIME_RANGE = 1500., - TIME_RESOLUTION = TIME_RANGE / 2047., - ENERGY_TRUNCATION = 250., - ENERGY_RESOLUTION = ENERGY_TRUNCATION / 16383.; + TIME_RESOLUTION = TIME_RANGE / 2047.; + +} +namespace EnergyEncoding +{ +namespace v0 +{ +const float + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION = ENERGY_TRUNCATION / 16383.; +} + +namespace v1 +{ +const float + ENERGY_BITS = static_cast(0x3FFF), + HGLGTRANSITION = o2::emcal::constants::EMCAL_HGLGTRANSITION * o2::emcal::constants::EMCAL_ADCENERGY, + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION_LG = (ENERGY_TRUNCATION - HGLGTRANSITION) / ENERGY_BITS, + ENERGY_RESOLUTION_HG = HGLGTRANSITION / ENERGY_BITS, + ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, + ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; +} + +namespace v2 +{ +const float + ENERGY_BITS = static_cast(0x3FFF), + SAFETYMARGIN = 0.2, + HGLGTRANSITION = o2::emcal::constants::OVERFLOWCUT * o2::emcal::constants::EMCAL_ADCENERGY, + OFFSET_LG = HGLGTRANSITION - SAFETYMARGIN, + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION_LG = (ENERGY_TRUNCATION - OFFSET_LG) / ENERGY_BITS, + ENERGY_RESOLUTION_HG = HGLGTRANSITION / ENERGY_BITS, + ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, + ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; + +} +} // namespace EnergyEncoding + +namespace DecodingV0 +{ +struct __attribute__((packed)) CellDataPacked { + uint16_t mTowerID : 15; ///< bits 0-14 Tower ID + uint16_t mTime : 11; ///< bits 15-25: Time (signed, can become negative after calibration) + uint16_t mEnergy : 14; ///< bits 26-39: Energy + uint16_t mCellStatus : 2; ///< bits 40-41: Cell status + uint16_t mZerod : 6; ///< bits 42-47: Zerod +}; +} // namespace DecodingV0 + +Cell::Cell(short tower, float energy, float timestamp, ChannelType_t ctype) : mTowerID(tower), mEnergy(energy), mTimestamp(timestamp), mChannelType(ctype) +{ +} + +Cell::Cell(uint16_t towerBits, uint16_t energyBits, uint16_t timestampBits, uint16_t channelBits, EncoderVersion version) +{ + initialiseFromEncoded(towerBits, timestampBits, energyBits, channelBits, version); +} + +uint16_t Cell::getTowerIDEncoded() const +{ + return mTowerID; +} + +uint16_t Cell::getTimeStampEncoded() const +{ + return encodeTime(mTimestamp); +} -Cell::Cell() +uint16_t Cell::getEnergyEncoded(EncoderVersion version) const { - memset(mCellWords, 0, sizeof(uint16_t) * 3); + uint16_t energyBits = 0; + switch (version) { + case EncoderVersion::EncodingV0: + energyBits = encodeEnergyV0(mEnergy); + break; + + case EncoderVersion::EncodingV1: + energyBits = encodeEnergyV1(mEnergy, mChannelType); + break; + + case EncoderVersion::EncodingV2: + energyBits = encodeEnergyV2(mEnergy, mChannelType); + break; + } + return energyBits; } -Cell::Cell(short tower, float energy, float time, ChannelType_t ctype) +uint16_t Cell::getCellTypeEncoded() const { - memset(mCellWords, 0, sizeof(uint16_t) * 3); - setTower(tower); - setTimeStamp(time); - setEnergy(energy); - setType(ctype); + return static_cast(mChannelType); } -void Cell::setTimeStamp(float timestamp) +void Cell::setEnergyEncoded(uint16_t energyBits, uint16_t channelTypeBits, EncoderVersion version) { - // truncate: - const float TIME_MIN = -1. * TIME_SHIFT, - TIME_MAX = TIME_RANGE - TIME_SHIFT; - if (timestamp < TIME_MIN) { - timestamp = TIME_MIN; - } else if (timestamp > TIME_MAX) { - timestamp = TIME_MAX; + switch (version) { + case EncoderVersion::EncodingV0: + mEnergy = decodeEnergyV0(energyBits); + break; + case EncoderVersion::EncodingV1: + mEnergy = decodeEnergyV1(energyBits, static_cast(channelTypeBits)); + break; + case EncoderVersion::EncodingV2: + mEnergy = decodeEnergyV2(energyBits, static_cast(channelTypeBits)); + break; } - getDataRepresentation()->mTime = static_cast(std::round((timestamp + TIME_SHIFT) / TIME_RESOLUTION)); } -float Cell::getTimeStamp() const +void Cell::setTimestampEncoded(uint16_t timestampBits) +{ + mTimestamp = decodeTime(timestampBits); +} + +void Cell::setTowerIDEncoded(uint16_t towerIDBits) { - return (static_cast(getDataRepresentation()->mTime) * TIME_RESOLUTION) - TIME_SHIFT; + mTowerID = towerIDBits; } -void Cell::setEnergy(float energy) +void Cell::setChannelTypeEncoded(uint16_t channelTypeBits) +{ + mChannelType = static_cast(channelTypeBits); +} + +void Cell::initializeFromPackedBitfieldV0(const char* bitfield) +{ + auto bitrepresentation = reinterpret_cast(bitfield); + mEnergy = decodeEnergyV0(bitrepresentation->mEnergy); + mTimestamp = decodeTime(bitrepresentation->mTime); + mTowerID = bitrepresentation->mTowerID; + mChannelType = static_cast(bitrepresentation->mCellStatus); +} + +float Cell::getEnergyFromPackedBitfieldV0(const char* bitfield) +{ + return decodeEnergyV0(reinterpret_cast(bitfield)->mEnergy); +} + +float Cell::getTimeFromPackedBitfieldV0(const char* bitfield) +{ + return decodeTime(reinterpret_cast(bitfield)->mTime); +} + +ChannelType_t Cell::getCellTypeFromPackedBitfieldV0(const char* bitfield) +{ + return static_cast(reinterpret_cast(bitfield)->mCellStatus); +} + +short Cell::getTowerFromPackedBitfieldV0(const char* bitfield) +{ + return reinterpret_cast(bitfield)->mTowerID; +} + +void Cell::truncate(EncoderVersion version) +{ + setEnergyEncoded(getEnergyEncoded(version), getCellTypeEncoded(), version); + setTimestampEncoded(getTimeStampEncoded()); +} + +uint16_t Cell::encodeTime(float timestamp) +{ + // truncate + auto timestampTruncated = timestamp; + const float TIME_MIN = -1. * TimeEncoding::TIME_SHIFT, + TIME_MAX = TimeEncoding::TIME_RANGE - TimeEncoding::TIME_SHIFT; + if (timestampTruncated < TIME_MIN) { + timestampTruncated = TIME_MIN; + } else if (timestampTruncated > TIME_MAX) { + timestampTruncated = TIME_MAX; + } + return static_cast(std::round((timestampTruncated + TimeEncoding::TIME_SHIFT) / TimeEncoding::TIME_RESOLUTION)); +} + +uint16_t Cell::encodeEnergyV0(float energy) +{ + auto truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > EnergyEncoding::v0::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v0::ENERGY_TRUNCATION; + } + return static_cast(std::round(truncatedEnergy / EnergyEncoding::v0::ENERGY_RESOLUTION)); +} + +uint16_t Cell::encodeEnergyV1(float energy, ChannelType_t celltype) { double truncatedEnergy = energy; if (truncatedEnergy < 0.) { truncatedEnergy = 0.; - } else if (truncatedEnergy > ENERGY_TRUNCATION) { - truncatedEnergy = ENERGY_TRUNCATION; + } else if (truncatedEnergy > EnergyEncoding::v1::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v1::ENERGY_TRUNCATION; } - getDataRepresentation()->mEnergy = static_cast(std::round(truncatedEnergy / ENERGY_RESOLUTION)); + float resolutionApplied = 0., energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v1::HGLGTRANSITION; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); +}; + +uint16_t Cell::encodeEnergyV2(float energy, ChannelType_t celltype) +{ + double truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > EnergyEncoding::v2::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v2::ENERGY_TRUNCATION; + } + float resolutionApplied = 0., energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); +}; + +uint16_t Cell::V0toV1(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV0(energyBits); + return encodeEnergyV1(decodedEnergy, celltype); +} + +uint16_t Cell::V0toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV0(energyBits); + return encodeEnergyV2(decodedEnergy, celltype); +} + +uint16_t Cell::V1toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV1(energyBits, celltype); + return encodeEnergyV2(decodedEnergy, celltype); } -float Cell::getEnergy() const +float Cell::decodeTime(uint16_t timestampBits) { - return static_cast(getDataRepresentation()->mEnergy) * ENERGY_RESOLUTION; + return (static_cast(timestampBits) * TimeEncoding::TIME_RESOLUTION) - TimeEncoding::TIME_SHIFT; +} + +float Cell::decodeEnergyV0(uint16_t energyBits) +{ + return static_cast(energyBits) * EnergyEncoding::v0::ENERGY_RESOLUTION; +} + +float Cell::decodeEnergyV1(uint16_t energyBits, ChannelType_t celltype) +{ + float resolutionApplied = 0., + energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v1::HGLGTRANSITION; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v1::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return (static_cast(energyBits) * resolutionApplied) + energyOffset; +} + +float Cell::decodeEnergyV2(uint16_t energyBits, ChannelType_t celltype) +{ + float resolutionApplied = 0., + energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return (static_cast(energyBits) * resolutionApplied) + energyOffset; } void Cell::PrintStream(std::ostream& stream) const @@ -76,7 +352,7 @@ void Cell::PrintStream(std::ostream& stream) const stream << "EMCAL Cell: Type " << getType() << ", Energy " << getEnergy() << ", Time " << getTimeStamp() << ", Tower " << getTower(); } -std::ostream& operator<<(std::ostream& stream, const Cell& c) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const Cell& c) { c.PrintStream(stream); return stream; diff --git a/DataFormats/Detectors/EMCAL/src/CellLabel.cxx b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx new file mode 100644 index 0000000000000..70a1a642c5449 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CellLabel.cxx + +#include "DataFormatsEMCAL/CellLabel.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +#include + +using namespace o2::emcal; + +CellLabel::CellLabel(std::vector labels, std::vector amplitudeFractions) : mLabels(std::move(labels)), mAmplitudeFraction(std::move(amplitudeFractions)) +{ + if (labels.size() != amplitudeFractions.size()) { + LOG(error) << "Size of labels " << labels.size() << " does not match size of amplitude fraction " << amplitudeFractions.size() << " !"; + } +} + +CellLabel::CellLabel(gsl::span labels, gsl::span amplitudeFractions) : mLabels(labels.begin(), labels.end()), mAmplitudeFraction(amplitudeFractions.begin(), amplitudeFractions.end()) +{ + if (labels.size() != amplitudeFractions.size()) { + LOG(error) << "Size of labels " << labels.size() << " does not match size of amplitude fraction " << amplitudeFractions.size() << " !"; + } +} + +int32_t CellLabel::GetLeadingMCLabel() const +{ + size_t maxIndex = 0; + float maxFraction = mAmplitudeFraction[0]; + + for (size_t i = 1; i < mAmplitudeFraction.size(); ++i) { + if (mAmplitudeFraction[i] > maxFraction) { + maxFraction = mAmplitudeFraction[i]; + maxIndex = i; + } + } + return mLabels[maxIndex]; +} diff --git a/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx b/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx new file mode 100644 index 0000000000000..9ad1f9be5459f --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx @@ -0,0 +1,75 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ClusterLabel.cxx + +#include "DataFormatsEMCAL/ClusterLabel.h" + +using namespace o2::emcal; + +//_______________________________________________________________________ +void ClusterLabel::clear() +{ + mClusterLabels.clear(); +} + +//_______________________________________________________________________ +void ClusterLabel::addValue(int label, float energyFraction) +{ + auto it = std::find_if(mClusterLabels.begin(), mClusterLabels.end(), + [label](const labelWithE& lWE) { return lWE.label == label; }); + + if (it != mClusterLabels.end()) { + // label already exists, accumulate energy fraction + it->energyFraction += energyFraction; + } else { + // label does not exist, add new energy fraction + mClusterLabels.emplace_back(label, energyFraction); + } +} + +//_______________________________________________________________________ +void ClusterLabel::normalize(float factor) +{ + for (auto& clusterlabel : mClusterLabels) { + clusterlabel.energyFraction = clusterlabel.energyFraction / factor; + } +} + +//_______________________________________________________________________ +std::vector ClusterLabel::getLabels() +{ + std::vector vLabels; + vLabels.reserve(mClusterLabels.size()); + for (auto& clusterlabel : mClusterLabels) { + vLabels.push_back(clusterlabel.label); + } + return vLabels; +} + +//_______________________________________________________________________ +std::vector ClusterLabel::getEnergyFractions() +{ + std::vector vEnergyFractions; + vEnergyFractions.reserve(mClusterLabels.size()); + for (auto& clusterlabel : mClusterLabels) { + vEnergyFractions.push_back(clusterlabel.energyFraction); + } + return vEnergyFractions; +} + +//_______________________________________________________________________ +void ClusterLabel::orderLabels() +{ + // Sort the pairs based on values in descending order + std::sort(mClusterLabels.begin(), mClusterLabels.end(), + [](const labelWithE& a, const labelWithE& b) { return a.energyFraction > b.energyFraction; }); +} diff --git a/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx b/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx new file mode 100644 index 0000000000000..e60b58c958d03 --- /dev/null +++ b/DataFormats/Detectors/EMCAL/src/CompressedTriggerData.cxx @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "DataFormatsEMCAL/CompressedTriggerData.h" + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedTRU& tru) +{ + stream << "TRU " << tru.mTRUIndex << ": Fired " << (tru.mFired ? "yes" : "no") << ", time " << (tru.mFired ? std::to_string(static_cast(tru.mTriggerTime)) : "Undefined") << ", number of patches " << tru.mNumberOfPatches; + return stream; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedTriggerPatch& patch) +{ + stream << "Patch " << patch.mPatchIndexInTRU << " in TRU " << patch.mTRUIndex << ": Time " << patch.mTime << ", ADC " << patch.mADC; + return stream; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::CompressedL0TimeSum& timesum) +{ + stream << "FastOR " << timesum.mIndex << ": " << timesum.mTimesum << " ADC counts"; + return stream; +} \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h index 464b611d8b6f2..a648dd9fbbdc1 100644 --- a/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h +++ b/DataFormats/Detectors/EMCAL/src/DataFormatsEMCALLinkDef.h @@ -24,6 +24,11 @@ #pragma link C++ class o2::emcal::MCLabel + ; #pragma link C++ class o2::emcal::ErrorTypeFEE + ; +#pragma read \ + sourceClass = "o2::emcal::Cell" targetClass = "o2::emcal::Cell" source = "UShort_t mCellWords[3]" version = "[1]" include = "iostream" target = "mEnergy,mTimestamp,mTowerID,mChannelType" code = "{const char * oldwords = reinterpret_cast(onfile.mCellWords); mEnergy=o2::emcal::Cell::getEnergyFromPackedBitfieldV0(oldwords); mTimestamp=o2::emcal::Cell::getTimeFromPackedBitfieldV0(oldwords); mTowerID=o2::emcal::Cell::getTowerFromPackedBitfieldV0(oldwords); mChannelType=o2::emcal::Cell::getCellTypeFromPackedBitfieldV0(oldwords);}" +#pragma read \ + sourceClass = "o2::emcal::Cell" targetClass = "o2::emcal::Cell" source = "char mCellWords[6]" version = "[2]" include = "iostream" target = "mEnergy,mTimestamp,mTowerID,mChannelType" code = "{mEnergy=o2::emcal::Cell::getEnergyFromPackedBitfieldV0(onfile.mCellWords); mTimestamp=o2::emcal::Cell::getTimeFromPackedBitfieldV0(onfile.mCellWords); mTowerID=o2::emcal::Cell::getTowerFromPackedBitfieldV0(onfile.mCellWords); mChannelType=o2::emcal::Cell::getCellTypeFromPackedBitfieldV0(onfile.mCellWords);}" + #pragma link C++ class std::vector < o2::emcal::TriggerRecord> + ; #pragma link C++ class std::vector < o2::emcal::Cell> + ; #pragma link C++ class std::vector < o2::emcal::Digit> + ; @@ -37,8 +42,6 @@ // For channel type in digits and cells #pragma link C++ enum o2::emcal::ChannelType_t + ; -#pragma link C++ class std::vector < o2::emcal::Cluster> + ; - #pragma link C++ class o2::emcal::EventData < o2::emcal::Cell> + ; #pragma link C++ class o2::emcal::EventData < o2::emcal::Digit> + ; #pragma link C++ class o2::emcal::EventHandler < o2::emcal::Cell> + ; diff --git a/DataFormats/Detectors/EMCAL/src/Digit.cxx b/DataFormats/Detectors/EMCAL/src/Digit.cxx index 45ba800790e8f..a88cc652cdd6d 100644 --- a/DataFormats/Detectors/EMCAL/src/Digit.cxx +++ b/DataFormats/Detectors/EMCAL/src/Digit.cxx @@ -86,8 +86,8 @@ Int_t Digit::getAmplitudeADC(ChannelType_t ctype) const case ChannelType_t::TRU: { int ampADC = std::floor(mAmplitudeGeV / constants::EMCAL_TRU_ADCENERGY); // truncate energy in case dynamic range is saturated - if (ampADC >= constants::EMCAL_TRU_ADCENERGY) { - return constants::EMCAL_TRU_ADCENERGY; + if (ampADC >= constants::MAX_RANGE_ADC) { + return constants::MAX_RANGE_ADC; } return ampADC; }; diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx deleted file mode 100644 index 8affa29259f7a..0000000000000 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.cxx -/// \brief Class to store the data format for calibraton of the EMCal - -#include "DataFormatsEMCAL/EMCALChannelData.h" - -using namespace o2::dataformats; - -ClassImp(o2::dataformats::EMCALChannelData; diff --git a/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx b/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx index df79498bd97e7..21d985b2a832a 100644 --- a/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx +++ b/DataFormats/Detectors/EMCAL/src/ErrorTypeFEE.cxx @@ -34,9 +34,18 @@ void ErrorTypeFEE::PrintStream(std::ostream& stream) const case ErrorSource_t::GEOMETRY_ERROR: typestring = "geometry error"; break; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: typestring = "gain type error"; break; + case ErrorSource_t::TRU_ERROR: + typestring = "STU decoder error"; + break; + case ErrorSource_t::STU_ERROR: + typestring = "STU decoder error"; + break; + case ErrorSource_t::LINK_ERROR: + typestring = "Link missing error"; + break; case ErrorSource_t::UNDEFINED: typestring = "unknown error"; break; @@ -66,8 +75,14 @@ const char* ErrorTypeFEE::getErrorTypeName(unsigned int errorTypeID) return "Fit"; case ErrorSource_t::GEOMETRY_ERROR: return "Geometry"; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: return "GainType"; + case ErrorSource_t::TRU_ERROR: + return "TRUDecoding"; + case ErrorSource_t::STU_ERROR: + return "STUDecoding"; + case ErrorSource_t::LINK_ERROR: + return "LinkMissing"; case ErrorSource_t::UNDEFINED: return "Undefined"; default: @@ -88,8 +103,14 @@ const char* ErrorTypeFEE::getErrorTypeTitle(unsigned int errorTypeID) return "Fit"; case ErrorSource_t::GEOMETRY_ERROR: return "Geometry"; - case ErrorTypeFEE::GAIN_ERROR: + case ErrorSource_t::GAIN_ERROR: return "Gain"; + case ErrorSource_t::TRU_ERROR: + return "TRU Decoding"; + case ErrorSource_t::STU_ERROR: + return "STU Decoding"; + case ErrorSource_t::LINK_ERROR: + return "Link missing"; case ErrorSource_t::UNDEFINED: return "Unknown"; default: @@ -97,7 +118,7 @@ const char* ErrorTypeFEE::getErrorTypeTitle(unsigned int errorTypeID) }; } -std::ostream& operator<<(std::ostream& stream, const ErrorTypeFEE& error) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const ErrorTypeFEE& error) { error.PrintStream(stream); return stream; diff --git a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx index f380b944f807d..7ca716dbb4ea2 100644 --- a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx +++ b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx @@ -13,6 +13,7 @@ #include #include "DataFormatsEMCAL/TriggerRecord.h" #include "CommonConstants/Triggers.h" +#include "DataFormatsEMCAL/Constants.h" namespace o2 { @@ -29,6 +30,9 @@ uint16_t TriggerRecord::getTriggerBitsCompressed() const if (mTriggerBits & o2::trigger::Cal) { result |= 1 << TriggerBitsCoded_t::CALIBTRIGGER; } + if (mTriggerBits & o2::emcal::triggerbits::Inc) { + result |= 1 << TriggerBitsCoded_t::REJECTINCOMPLETE; + } return result; } @@ -41,6 +45,9 @@ void TriggerRecord::setTriggerBitsCompressed(uint16_t triggerbits) if (triggerbits & (1 << TriggerBitsCoded_t::CALIBTRIGGER)) { mTriggerBits |= o2::trigger::Cal; } + if (triggerbits & (1 << TriggerBitsCoded_t::REJECTINCOMPLETE)) { + mTriggerBits |= o2::emcal::triggerbits::Inc; + } } void TriggerRecord::printStream(std::ostream& stream) const diff --git a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt index fff07003f2700..140ba1165bff8 100644 --- a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt @@ -11,11 +11,14 @@ o2_add_library(DataFormatsFDD SOURCES src/RawEventData.cxx + src/RecPoint.cxx src/CTF.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::FDDBase O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFDD @@ -27,3 +30,4 @@ o2_target_root_dictionary(DataFormatsFDD include/DataFormatsFDD/RawEventData.h include/DataFormatsFDD/LookUpTable.h include/DataFormatsFDD/CTF.h) + diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h index 3c1df8d89ee4d..594f924e16598 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // BC data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels // channel data diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h index e138001e30508..ceac8e26fec7f 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/LookUpTable.h @@ -9,11 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FDD_LOOKUPTABLE_H_ #define ALICEO2_FDD_LOOKUPTABLE_H_ //////////////////////////////////////////////// @@ -21,63 +16,12 @@ ////////////////////////////////////////////// #include "DataFormatsFIT/LookUpTable.h" -#include -#include -#include -#include // std::setfill, std::setw - for stream formating -#include -#include "FDDBase/Constants.h" -#include "CommonUtils/NameConf.h" - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace fdd { -namespace new_lut -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FDD"; - static constexpr char sDefaultLUTpath[] = "FDD/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr, long timestamp = -1) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath, timestamp); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace fdd } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h index 6615dc322180b..f784d99145728 100644 --- a/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h +++ b/DataFormats/Detectors/FIT/FDD/include/DataFormatsFDD/RecPoint.h @@ -9,8 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file RecPoint.h +/// \file RecPoint.h /// \brief Definition of the FDD RecPoint class + #ifndef ALICEO2_FDD_RECPOINT_H #define ALICEO2_FDD_RECPOINT_H @@ -42,6 +43,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -80,6 +82,9 @@ class RecPoint int getFirstEntry() const { return mRef.getFirstEntry(); } int getEntriesInCurrentBC() const { return mRef.getEntries(); } + void print() const; + bool operator==(const RecPoint&) const = default; + private: o2::dataformats::RangeReference mRef; o2::InteractionRecord mIntRecord; diff --git a/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx new file mode 100644 index 0000000000000..7965b73d6aa95 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFDD/LookUpTable.h" + +using namespace o2::fdd; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx b/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx new file mode 100644 index 0000000000000..854a09088a2f4 --- /dev/null +++ b/DataFormats/Detectors/FIT/FDD/src/RecPoint.cxx @@ -0,0 +1,33 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RecPoint.cxx +/// \brief Implementation of the FDD RecPoint class +/// \author Andreas Molander andreas.molander@cern.ch + +#include "DataFormatsFDD/RecPoint.h" +#include "Framework/Logger.h" + +using namespace o2::fdd; + +void ChannelDataFloat::print() const +{ + LOG(info) << "ChannelDataFloat data:"; + LOG(info) << "Channel ID: " << mPMNumber << ", Time (ps): " << mTime << ", Charge (ADC): " << mChargeADC << ", QTC chain: " << adcId; +} + +void RecPoint::print() const +{ + LOG(info) << "RecPoint data:"; + LOG(info) << "Collision times: A: " << getCollisionTimeA() << ", C: " << getCollisionTimeC(); + LOG(info) << "Ref first: " << mRef.getFirstEntry() << ", Ref entries: " << mRef.getEntries(); + LOG(info) << "Triggers: " << mTriggers.print(); +} diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index 14136966509c5..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -17,12 +17,16 @@ o2_add_library(DataFormatsFT0 src/RecPoints.cxx src/RawEventData.cxx src/CTF.cxx + src/LookUpTable.cxx + src/SlewingCoef.cxx + PUBLIC_LINK_LIBRARIES O2::FT0Base O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat O2::Headers O2::CCDB + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFT0 @@ -42,4 +46,6 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/GlobalOffsetsInfoObject.h include/DataFormatsFT0/GlobalOffsetsCalibrationObject.h include/DataFormatsFT0/SpectraInfoObject.h + include/DataFormatsFT0/SlewingCoef.h + include/DataFormatsFT0/EventsPerBc.h ) diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h index fdc073edfd5c1..3ddea05158577 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // trigger data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels std::vector eventStatus; // special flags about event conditions: pile-up, not use for collision time, not use for event plane, etc. diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h index 074caf78f66b3..9b3d6ec805604 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/ChannelData.h @@ -13,8 +13,8 @@ /// \brief Class to describe fired and stored channels for the BC and to refer to channel data /// \author Alla.Maevskaya@cern.ch -#ifndef _FT0_CHANNELDATA_H_ -#define _FT0_CHANNELDATA_H_ +#ifndef O2_FT0_CHANNELDATA_H_ +#define O2_FT0_CHANNELDATA_H_ #include #include diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h index 2b7758eeac1f5..c4051fd5b1a7d 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/Digit.h @@ -50,6 +50,7 @@ struct DetTrigInput { (isSCnt << Triggers::bitSCen)) { } + bool isVertex() const { return mInputs.test(Triggers::bitVertex); } ClassDefNV(DetTrigInput, 1); }; diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h index 11ff50dfeb86b..a00abc013c27d 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/DigitFilterParam.h @@ -27,9 +27,9 @@ struct DigitFilterParam : o2::conf::ConfigurableParamHelper { uint8_t mPMbitsGood = (1 << ChannelData::EEventDataBit::kIsCFDinADCgate) | (1 << ChannelData::EEventDataBit::kIsEventInTVDC); uint8_t mPMbitsBad = (1 << ChannelData::EEventDataBit::kIsDoubleEvent) | (1 << ChannelData::EEventDataBit::kIsTimeInfoNOTvalid) | (1 << ChannelData::EEventDataBit::kIsTimeInfoLate) | (1 << ChannelData::EEventDataBit::kIsAmpHigh) | (1 << ChannelData::EEventDataBit::kIsTimeInfoLost); uint8_t mPMbitsToCheck = mPMbitsGood | mPMbitsBad; - uint8_t mTrgBitsGood = (1 << Triggers::bitVertex) | (1 << Triggers::bitDataIsValid); - uint8_t mTrgBitsBad = (1 << Triggers::bitOutputsAreBlocked); - uint8_t mTrgBitsToCheck = mTrgBitsGood | mTrgBitsBad; + uint64_t mTrgBitsGood = Triggers::word(Triggers::bitVertex, Triggers::bitDataIsValid); + uint64_t mTrgBitsBad = Triggers::word(Triggers::bitOutputsAreBlocked); + uint64_t mTrgBitsToCheck = mTrgBitsGood | mTrgBitsBad; O2ParamDef(DigitFilterParam, "FT0DigitFilterParam"); }; @@ -41,12 +41,13 @@ struct ChannelFilterParam : o2::conf::ConfigurableParamHelper + +namespace o2::ft0 +{ +struct EventsPerBc { + std::array histogram; + ClassDefNV(EventsPerBc, 1); +}; +} // namespace o2::ft0 +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h index 7ec7b997d61cb..1d31b079c2ac0 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/LookUpTable.h @@ -9,85 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FT0_LOOKUPTABLE_H_ #define ALICEO2_FT0_LOOKUPTABLE_H_ //////////////////////////////////////////////// // Look Up Table FT0 ////////////////////////////////////////////// - -#include "CCDB/BasicCCDBManager.h" -#include "FT0Base/Constants.h" #include "DataFormatsFIT/LookUpTable.h" -#include "CommonUtils/NameConf.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace ft0 { - -namespace new_lut -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FT0"; - static constexpr char sDefaultLUTpath[] = "FT0/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr, long timestamp = -1) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath, timestamp); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace ft0 } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h index 1178cc20a4da0..0503e4f39948f 100644 --- a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/RecPoints.h @@ -24,7 +24,9 @@ #include "Rtypes.h" #include #include - +#include +#include +#include namespace o2 { namespace ft0 @@ -32,10 +34,10 @@ namespace ft0 struct ChannelDataFloat { - int ChId = -1; //channel Id - int ChainQTC = -1; //QTC chain - float CFDTime = -20000; //time in ps, 0 at the LHC clk center - float QTCAmpl = -20000; // Amplitude mV + int ChId = -1; // channel Id + int ChainQTC = -1; // QTC chain + float CFDTime = -20000; // time in ps, 0 at the LHC clk center + float QTCAmpl = -20000; // Amplitude mV ChannelDataFloat() = default; ChannelDataFloat(int iPmt, float time, float charge, int chainQTC) @@ -47,6 +49,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -55,10 +58,39 @@ class RecPoints { public: - enum : int { TimeMean, - TimeA, - TimeC, - Vertex }; + enum ETimeType { kTimeMean, + kTimeA, + kTimeC, + kTimeVertex }; + + // Enum for trigger nits specified in rec-points and AOD data + enum ETriggerBits { kOrA = 0, // OrA time-trigger signal + kOrC = 1, // OrC time-trigger signal + kSemiCentral = 2, // Semi-central amplitude-trigger signal + kCentral = 3, // Central amplitude-trigger signal + kVertex = 4, // Vertex time-trigger signal + kIsActiveSideA = 5, // Side-A has at least one channel active + kIsActiveSideC = 6, // Side-C has at least one channel active + kIsFlangeEvent = 7 // Flange event at Side-C, at least one channel has time which corresponds to -82 cm area + }; + static const inline std::map sMapTriggerBits = { + {ETriggerBits::kOrA, "OrA"}, + {ETriggerBits::kOrC, "OrC"}, + {ETriggerBits::kSemiCentral, "Semicentral"}, + {ETriggerBits::kCentral, "Central"}, + {ETriggerBits::kVertex, "Vertex"}, + {ETriggerBits::kIsActiveSideA, "IsActiveSideA"}, + {ETriggerBits::kIsActiveSideC, "IsActiveSideC"}, + {ETriggerBits::kIsFlangeEvent, "IsFlangeEvent"}}; + + enum ETechnicalBits { kLaser = 0, // indicates the laser was triggered in this BC + kOutputsAreBlocked = 1, // indicates that laser-induced pulses should arrive from detector to FEE in this BC (and trigger outputs are blocked) + kDataIsValid = 2, // data is valid for processing + }; + static const inline std::map sMapTechnicalBits = { + {ETechnicalBits::kLaser, "Laser"}, + {ETechnicalBits::kOutputsAreBlocked, "OutputsAreBlocked"}, + {ETechnicalBits::kDataIsValid, "DataIsValid"}}; o2::dataformats::RangeReference ref; o2::InteractionRecord mIntRecord; // Interaction record (orbit, bc) @@ -72,38 +104,63 @@ class RecPoints mIntRecord = iRec; mTriggers = chTrig; } - ~RecPoints() = default; + RecPoints(int chDataFirstEntryPos, + int chDataNEntries, + const o2::InteractionRecord& ir, + const std::array& arrTimes, + const o2::fit::Triggers& digitTriggers, + uint8_t extraTriggerWord) : mIntRecord(ir), mCollisionTime(arrTimes), mTriggers(digitTriggers) + { + ref.setFirstEntry(chDataFirstEntryPos); + ref.setEntries(chDataNEntries); + initRecPointTriggers(digitTriggers, extraTriggerWord); + } - void print() const; + ~RecPoints() = default; short getCollisionTime(int side) const { return mCollisionTime[side]; } - short getCollisionTimeMean() const { return getCollisionTime(TimeMean); } - short getCollisionTimeA() const { return getCollisionTime(TimeA); } - short getCollisionTimeC() const { return getCollisionTime(TimeC); } + short getCollisionTimeMean() const { return getCollisionTime(kTimeMean); } + short getCollisionTimeA() const { return getCollisionTime(kTimeA); } + short getCollisionTimeC() const { return getCollisionTime(kTimeC); } bool isValidTime(int side) const { return getCollisionTime(side) < o2::InteractionRecord::DummyTime; } void setCollisionTime(short time, int side) { mCollisionTime[side] = time; } - short getVertex() const { return getCollisionTime(Vertex); } - void setVertex(short vertex) { mCollisionTime[Vertex] = vertex; } + short getVertex() const { return getCollisionTime(kTimeVertex); } + void setVertex(short vertex) { mCollisionTime[kTimeVertex] = vertex; } o2::fit::Triggers getTrigger() const { return mTriggers; } void setTriggers(o2::fit::Triggers trig) { mTriggers = trig; } + uint8_t getTechnicalWord() const { return mTechnicalWord; } + static constexpr uint8_t makeExtraTrgWord(bool isActiveA = true, bool isActiveC = true, bool isFlangeEvent = true) + { + return (static_cast(isActiveA) << kIsActiveSideA) | + (static_cast(isActiveC) << kIsActiveSideC) | + (static_cast(isFlangeEvent) << kIsFlangeEvent); + } o2::InteractionRecord getInteractionRecord() const { return mIntRecord; }; - - // void SetMgrEventTime(Double_t time) { mTimeStamp = time; } - gsl::span getBunchChannelData(const gsl::span tfdata) const; short static constexpr sDummyCollissionTime = 32767; + void print() const; + bool operator==(const RecPoints&) const = default; + private: + void initRecPointTriggers(const o2::fit::Triggers& digitTriggers, uint8_t extraTrgWord = 0) + { + const auto digitTriggerWord = digitTriggers.getTriggersignals(); + const auto trgAndTechWordPair = o2::fit::Triggers::parseDigitTriggerWord(digitTriggerWord, true); + mTriggers.setTriggers(trgAndTechWordPair.first | extraTrgWord); + mTechnicalWord = trgAndTechWordPair.second; + } + std::array mCollisionTime = {sDummyCollissionTime, sDummyCollissionTime, sDummyCollissionTime, sDummyCollissionTime}; o2::fit::Triggers mTriggers; // pattern of triggers in this BC - - ClassDefNV(RecPoints, 3); + uint8_t mTechnicalWord{0}; // field for keeping ETechnicalBits + ClassDefNV(RecPoints, 4); }; } // namespace ft0 } // namespace o2 diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h new file mode 100644 index 0000000000000..704f4e3fd2324 --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/SlewingCoef.h @@ -0,0 +1,47 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FT0_SLEWINGCOEF_H_ +#define ALICEO2_FT0_SLEWINGCOEF_H_ +//////////////////////////////////////////////// +// Slewing coefficients for FT0 +////////////////////////////////////////////// +#include "TGraph.h" +#include "Rtypes.h" + +#include +#include +#include + +namespace o2 +{ +namespace ft0 +{ + +struct SlewingCoef { + constexpr static int sNCHANNELS = 208; + constexpr static int sNAdc = 2; + using VecPoints_t = std::vector; // Set of points + using VecPlot_t = std::pair; // Plot as pair of two set of points + using VecSlewingCoefs_t = std::array, sNAdc>; // 0 - adc0, 1 - adc1 + typedef std::array, sNAdc> SlewingPlots_t; + VecSlewingCoefs_t mSlewingCoefs{}; + SlewingPlots_t makeSlewingPlots() const; + constexpr static const char* getObjectPath() + { + return "FT0/Calib/SlewingCoef"; + } + ClassDefNV(SlewingCoef, 1) +}; +} // namespace ft0 +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 22dd7a0a8aa8e..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -53,4 +53,9 @@ #pragma link C++ class o2::ft0::GlobalOffsetsCalibrationObject + ; #pragma link C++ class o2::ft0::RecoCalibInfoObject + ; #pragma link C++ class o2::ft0::GlobalOffsetsInfoObject + ; +#pragma link C++ class std::pair < std::vector < double>, std::vector < double>> + ; +#pragma link C++ class o2::ft0::SlewingCoef + ; + +#pragma link C++ class o2::ft0::EventsPerBc + ; + #endif diff --git a/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx new file mode 100644 index 0000000000000..aa29f589acdec --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFT0/LookUpTable.h" + +using namespace o2::ft0; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx b/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx index f580d0dd1ea8c..afd244f977f71 100644 --- a/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx +++ b/DataFormats/Detectors/FIT/FT0/src/RecPoints.cxx @@ -21,14 +21,22 @@ using namespace o2::ft0; +void ChannelDataFloat::print() const +{ + printf(" ChID% d | CFDtime=%f | QTCampl=%f QTC chain %d\n", ChId, CFDTime, QTCAmpl, ChainQTC); +} + gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const { // extract the span of channel data for this bunch from the whole TF data return ref.getEntries() ? gsl::span(tfdata).subspan(ref.getFirstEntry(), ref.getEntries()) : gsl::span(); } -void ChannelDataFloat::print() const +void RecPoints::print() const { - - printf(" ChID% d | CFDtime=%f | QTCampl=%f QTC chain %d\n", ChId, CFDTime, QTCAmpl, ChainQTC); + LOG(info) << "RecPoint data:"; + LOG(info) << "Collision times: mean: " << getCollisionTimeMean() << ", A: " << getCollisionTimeA() << ", C: " << getCollisionTimeC(); + LOG(info) << "Vertex: " << getVertex(); + LOG(info) << "Ref first: " << ref.getFirstEntry() << ", Ref entries: " << ref.getEntries(); + LOG(info) << "Triggers: " << mTriggers.print(); } diff --git a/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx b/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx new file mode 100644 index 0000000000000..e1d8bda5ac4ea --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/src/SlewingCoef.cxx @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFT0/SlewingCoef.h" +#include +using namespace o2::ft0; + +SlewingCoef::SlewingPlots_t SlewingCoef::makeSlewingPlots() const +{ + typename o2::ft0::SlewingCoef::SlewingPlots_t plots{}; + for (int iAdc = 0; iAdc < sNAdc; iAdc++) { + const auto& slewingCoefs = mSlewingCoefs[iAdc]; + auto& plotsAdc = plots[iAdc]; + for (int iCh = 0; iCh < sNCHANNELS; iCh++) { + const auto& points_x = slewingCoefs[iCh].first; + const auto& points_y = slewingCoefs[iCh].second; + assert(points_x.size() == points_y.size()); + const int nPoints = points_x.size(); + auto& plot = plotsAdc[iCh]; + plot = TGraph(nPoints, points_x.data(), points_y.data()); + } + } + return plots; +} diff --git a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt index 9eea4dd092d7a..35bc653a8234e 100644 --- a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt @@ -16,11 +16,13 @@ o2_add_library(DataFormatsFV0 src/RecPoints.cxx src/RawEventData.cxx src/CTF.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::FV0Base O2::DataFormatsFIT O2::SimulationDataFormat O2::CommonDataFormat Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFV0 @@ -33,5 +35,4 @@ o2_target_root_dictionary(DataFormatsFV0 include/DataFormatsFV0/RecPoints.h include/DataFormatsFV0/RawEventData.h include/DataFormatsFV0/LookUpTable.h - include/DataFormatsFV0/CTF.h -) + include/DataFormatsFV0/CTF.h) diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h index e71e42ca2f4ad..275b682a15276 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/CTF.h @@ -41,8 +41,8 @@ struct CompressedDigits { // trigger data std::vector trigger; // trigger bits - std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc - std::vector orbitInc; // increment in orbit + std::vector bcInc; // increment in BC if the same orbit, otherwise abs bc + std::vector orbitInc; // increment in orbit std::vector nChan; // number of fired channels // channel data diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h index 4f568feb92103..4c77b0143d1dc 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/LookUpTable.h @@ -9,71 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// file RawEventData.h class for RAW data format -// Alla.Maevskaya -// simple look-up table just to feed digits 2 raw procedure. -// Will be really set after module/electronics connections -// #ifndef ALICEO2_FV0_LOOKUPTABLE_H_ #define ALICEO2_FV0_LOOKUPTABLE_H_ //////////////////////////////////////////////// // Look Up Table FV0 ////////////////////////////////////////////// - #include "DataFormatsFIT/LookUpTable.h" -#include "CommonUtils/NameConf.h" - +#include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace fv0 { - -namespace new_lut - -{ -// Singleton for LookUpTable -template -class SingleLUT : public LUT -{ - private: - SingleLUT() = default; - SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} - SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} - SingleLUT(const SingleLUT&) = delete; - SingleLUT& operator=(SingleLUT&) = delete; - - public: - static constexpr char sDetectorName[] = "FV0"; - static constexpr char sDefaultLUTpath[] = "FV0/Config/LookupTable"; - static constexpr char sObjectName[] = "LookupTable"; - inline static std::string sCurrentCCDBpath = ""; - inline static std::string sCurrentLUTpath = sDefaultLUTpath; - // Before instance() call, setup url and path - static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } - static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } - - using Table_t = typename LUT::Table_t; - bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization - static SingleLUT& Instance(const Table_t* table = nullptr, long timestamp = -1) - { - if (sCurrentCCDBpath == "") { - sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); - } - static SingleLUT instanceLUT; - if (table != nullptr) { - instanceLUT.initFromTable(table); - instanceLUT.mFirstUpdate = false; - } else if (instanceLUT.mFirstUpdate) { - instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath, timestamp); - instanceLUT.mFirstUpdate = false; - } - return instanceLUT; - } -}; -} // namespace new_lut - -using SingleLUT = new_lut::SingleLUT>; - +using SingleLUT = o2::fit::SingleLUT>; } // namespace fv0 } // namespace o2 #endif diff --git a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h index c5e0a827de132..b3527fdd049d2 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h @@ -28,7 +28,7 @@ namespace fv0 struct ChannelDataFloat { int channel = -1; // channel Id - double time = -20000; // time in ps, 0 at the LHC clk center + double time = -20000; // time in ns, 0 at the LHC clk center double charge = -20000; // charge [channels] int adcId = -1; // QTC chain @@ -42,6 +42,7 @@ struct ChannelDataFloat { } void print() const; + bool operator==(const ChannelDataFloat&) const = default; ClassDefNV(ChannelDataFloat, 1); }; @@ -77,6 +78,9 @@ class RecPoints gsl::span getBunchChannelData(const gsl::span tfdata) const; short static constexpr sDummyCollissionTime = 32767; + void print() const; + bool operator==(const RecPoints&) const = default; + private: o2::dataformats::RangeReference mRef; o2::InteractionRecord mIntRecord; diff --git a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h index 3cd6a237f8e05..ab23b4aa727bc 100644 --- a/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h +++ b/DataFormats/Detectors/FIT/FV0/src/DataFormatsFV0LinkDef.h @@ -18,6 +18,8 @@ #pragma link C++ class o2::fv0::Hit + ; #pragma link C++ class vector < o2::fv0::Hit> + ; #pragma link C++ class o2::fv0::MCLabel + ; +#include "SimulationDataFormat/MCTruthContainer.h" +#pragma link C++ class o2::dataformats::MCTruthContainer < o2::fv0::MCLabel> + ; #pragma link C++ class o2::fv0::ChannelData + ; #pragma link C++ class o2::fv0::Digit + ; diff --git a/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx new file mode 100644 index 0000000000000..7a1d5969abb79 --- /dev/null +++ b/DataFormats/Detectors/FIT/FV0/src/LookUpTable.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFV0/LookUpTable.h" + +using namespace o2::fv0; +template class o2::fit::SingleLUT>; diff --git a/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx b/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx index 18f2effc281e8..ef1554acf5419 100644 --- a/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx +++ b/DataFormats/Detectors/FIT/FV0/src/RecPoints.cxx @@ -14,13 +14,22 @@ using namespace o2::fv0; -gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const +void ChannelDataFloat::print() const { - // extract the span of channel data for this bunch from the whole TF data - return mRef.getEntries() ? gsl::span(tfdata).subspan(mRef.getFirstEntry(), mRef.getEntries()) : gsl::span(); + printf(" Channel=%d | time=%f | charge=%f | adcId=%d\n", channel, time, charge, adcId); } -void ChannelDataFloat::print() const +void RecPoints::print() const { - printf(" Channel=%d | time=%f | charge=%f | adcId=%d\n", channel, time, charge, adcId); + printf("RecPoint data:"); + printf("Collision times: first: %f, global mean: %f, selected mean: %f\n", getCollisionFirstTime(), getCollisionGlobalMeanTime(), getCollisionSelectedMeanTime()); + printf("Ref first: %d, Ref entries: %d\n", mRef.getFirstEntry(), mRef.getEntries()); + printf("Triggers: "); + mTriggers.print(); +} + +gsl::span RecPoints::getBunchChannelData(const gsl::span tfdata) const +{ + // extract the span of channel data for this bunch from the whole TF data + return mRef.getEntries() ? gsl::span(tfdata).subspan(mRef.getFirstEntry(), mRef.getEntries()) : gsl::span(); } diff --git a/DataFormats/Detectors/FIT/common/CMakeLists.txt b/DataFormats/Detectors/FIT/common/CMakeLists.txt index a42142fb55fb0..61dbcabc7f087 100644 --- a/DataFormats/Detectors/FIT/common/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/common/CMakeLists.txt @@ -12,10 +12,15 @@ o2_add_library(DataFormatsFIT SOURCES src/RawEventData.cxx src/Triggers.cxx + src/RawDataMetric.cxx + src/LookUpTable.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::DetectorsCommonDataFormats O2::CCDB) o2_target_root_dictionary(DataFormatsFIT HEADERS include/DataFormatsFIT/DCSDPValues.h include/DataFormatsFIT/LookUpTable.h - include/DataFormatsFIT/Triggers.h) + include/DataFormatsFIT/Triggers.h + include/DataFormatsFIT/ChannelData.h + include/DataFormatsFIT/Digit.h) diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h new file mode 100644 index 0000000000000..452307d05f200 --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/ChannelData.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ChannelData.h +/// \brief Class represents enity with info per channel in given event +/// \author Artur Furs afurs@cern.ch + +#ifndef _FT0_CHANNELDATA_H_ +#define _FT0_CHANNELDATA_H_ + +#include +#include +#include +#include "DetectorsCommonDataFormats/DetID.h" + +namespace o2 +{ +namespace fit +{ + +template +struct ChannelData { + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + static constexpr uint8_t sDUMMY_CHANNEL_ID = 0xff; + static constexpr uint8_t sDUMMY_PM_WORD = 0xff; + static constexpr int16_t sDUMMY_TIME = -5000; + static constexpr int16_t sDUMMY_AMP = -5000; + uint8_t mChannelID = sDUMMY_CHANNEL_ID; // channel id + uint8_t mWordPM = sDUMMY_PM_WORD; // PM word, based on EBitsPM + int16_t mTime = sDUMMY_TIME; // time in TDC units + int16_t mAmp = sDUMMY_AMP; // amplitude in ADC units + enum EBitsPM { + kNumberADC, + kIsDoubleEvent, + kIsTimeInfoNotValid, + kIsCFDinADCgate, + kIsTimeInfoLate, + kIsAmpNotValid, + kIsVertexEvent, + kIsTimeInfoLost + }; + static const inline std::map sMapBitsPM = { + {EBitsPM::kNumberADC, "NumberADC"}, + {EBitsPM::kIsDoubleEvent, "IsDoubleEvent"}, + {EBitsPM::kIsTimeInfoNotValid, "IsTimeInfoNotValid"}, + {EBitsPM::kIsCFDinADCgate, "IsCFDinADCgate"}, + {EBitsPM::kIsTimeInfoLate, "IsTimeInfoLate"}, + {EBitsPM::kIsAmpNotValid, "IsAmpNotValid"}, + {EBitsPM::kIsVertexEvent, "IsVertexEvent"}, + {EBitsPM::kIsTimeInfoLost, "IsTimeInfoLost"}}; + ChannelData() = default; + ChannelData(uint8_t channelID, uint8_t wordPM, int16_t time, int16_t amp) : mChannelID(channelID), mWordPM(wordPM), mTime(time), mAmp(amp) + { + } + // void print() const; + bool operator<=>(ChannelData const& other) const = default; + bool operator==(ChannelData const& other) const = default; + ClassDefNV(ChannelData, 1); +}; +} // namespace fit +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h index 26e1969fca968..97bbe982e6aac 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/DCSDPValues.h @@ -14,22 +14,23 @@ #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" namespace o2 { namespace fit { struct DCSDPValues { - std::vector> values; + std::vector> values; DCSDPValues() { - values = std::vector>(); + values = std::vector>(); } void add(uint64_t timestamp, int64_t value) { - values.push_back(std::pair(timestamp, value)); + values.push_back(std::pair(timestamp, value)); } bool empty() @@ -63,4 +64,4 @@ struct DCSDPValues { } // namespace fit } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h new file mode 100644 index 0000000000000..adaef4ec8349a --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Digit.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \file Digit.h +/// \brief FIT event entity +/// \author Artur Furs afurs@cern.ch + +#ifndef O2_FIT_DIGIT_H_ +#define O2_FIT_DIGIT_H_ + +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsFIT/ChannelData.h" +#include "DataFormatsFIT/Triggers.h" + +#include +#include + +namespace o2 +{ +namespace fit +{ +using Triggers = o2::fit::Triggers; + +template +struct DigitBase { + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + typedef ChannelData ChannelData_t; // related ChannelData entity + o2::InteractionRecord mIR{}; // Interaction record (orbit, bc) + o2::dataformats::RangeReference mReference{}; + DigitBase() = default; + DigitBase(int first, int nEntries, const o2::InteractionRecord& ir) : mIR(ir) + { + mReference.setFirstEntry(first); + mReference.setEntries(nEntries); + } + uint32_t getOrbit() const { return mIR.orbit; } + uint16_t getBC() const { return mIR.bc; } + const o2::InteractionRecord& getIntRecord() const { return mIR; }; + gsl::span getBunchChannelData(const gsl::span channelData) const + { + return mReference.getEntries() ? gsl::span(&channelData[mReference.getFirstEntry()], mReference.getEntries()) : gsl::span(); + } + ClassDefNV(DigitBase, 1); +}; + +template +struct Digit : public DigitBase { + uint8_t mTriggerWord{}; + Digit() = default; + Digit(int first, int nEntries, const o2::InteractionRecord& ir, uint8_t trgWord) : DigitBase(first, nEntries, ir), mTriggerWord(trgWord) + { + } + ClassDefNV(Digit, 1); +}; + +template +struct DigitExt : public DigitBase { + Triggers mTriggers{}; + DigitExt() = default; + DigitExt(int first, int nEntries, const o2::InteractionRecord& ir, const Triggers& triggers) : DigitBase(first, nEntries, ir), mTriggers(triggers) + { + } + ClassDefNV(DigitExt, 1); +}; + +} // namespace fit +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h index dcd99bf402504..aa4bb1fba8d41 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h @@ -15,7 +15,9 @@ // Look Up Table FIT ////////////////////////////////////////////// -#include "CCDB/BasicCCDBManager.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/NameConf.h" +#include "Framework/Logger.h" #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include #include @@ -157,8 +159,7 @@ enum class EModuleType : int { kUnknown, kTCM }; template , - typename MapEntryPM2ChannelID = std::unordered_map, - typename = typename std::enable_if_t::value>> + typename MapEntryPM2ChannelID = std::unordered_map> class LookupTableBase { public: @@ -173,7 +174,7 @@ class LookupTableBase typedef EntryPM_t Topo_t; // temporary for common interface LookupTableBase() = default; - LookupTableBase(const Table_t& vecEntryFEE) { initFromTable(vecEntryFEE); } + LookupTableBase(const Table_t* vecEntryFEE) { initFromTable(vecEntryFEE); } LookupTableBase(const std::string& pathToFile) { initFromFile(pathToFile); } LookupTableBase(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp = -1) { initCCDB(urlCCDB, pathToStorageInCCDB, timestamp); } // Map of str module names -> enum types @@ -183,6 +184,12 @@ class LookupTableBase { return mEntryCRU_TCM.mLinkID == linkID && mEntryCRU_TCM.mEndPointID == epID; } + + bool isPM(int linkID, int epID) const + { + return isPM(EntryCRU_t{linkID, epID}); + } + bool isTCM(const EntryCRU_t& entryCRU) const { if (getModuleType(entryCRU) == EModuleType::kTCM) { @@ -236,13 +243,7 @@ class LookupTableBase prepareEntriesFEE(filepath); prepareLUT(); } - void initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp = -1) - { - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - mgr.setURL(urlCCDB); - mVecEntryFEE = *(mgr.getForTimeStamp(pathToStorageInCCDB, timestamp)); - prepareLUT(); - } + void initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp = -1); void initFromTable(const Table_t* vecEntryFEE) { mVecEntryFEE = *vecEntryFEE; @@ -412,7 +413,68 @@ class LookupTableBase Table_t mVecEntryFEE; MapEntryCRU2ModuleType_t mMapEntryCRU2ModuleType; MapEntryPM2ChannelID_t mMapEntryPM2ChannelID; + typedef std::enable_if_t::value> CheckChannelIDtype; // should be integral }; + +// Singleton for LookUpTable, coomon for all three FIT detectors +template +class SingleLUT : public LUT +{ + private: + SingleLUT() = default; + SingleLUT(const std::string& ccdbPath, const std::string& ccdbPathToLUT) : LUT(ccdbPath, ccdbPathToLUT) {} + SingleLUT(const std::string& pathToFile) : LUT(pathToFile) {} + SingleLUT(const SingleLUT&) = delete; + SingleLUT& operator=(SingleLUT&) = delete; + constexpr static bool isValidDet() + { + return (DetID == o2::detectors::DetID::FDD) || (DetID == o2::detectors::DetID::FT0) || (DetID == o2::detectors::DetID::FV0); + } + + public: + typedef LUT LookupTable_t; + typedef typename LookupTable_t::Table_t Table_t; + + constexpr static const char* getObjectPath() + { + static_assert(isValidDet(), "Invalid detector type(o2::detectors::DetID::ID)! Should be one of the FIT detector!"); + if constexpr (DetID == o2::detectors::DetID::FDD) { + return "FDD/Config/LookupTable"; + } else if constexpr (DetID == o2::detectors::DetID::FT0) { + return "FT0/Config/LookupTable"; + } else if constexpr (DetID == o2::detectors::DetID::FV0) { + return "FV0/Config/LookupTable"; + } + return ""; + } + static constexpr o2::detectors::DetID sDetID = o2::detectors::DetID(DetID); + static constexpr const char* sDetectorName = o2::detectors::DetID::getName(DetID); + static constexpr const char* sDefaultLUTpath = getObjectPath(); + static constexpr const char sObjectName[] = "LookupTable"; + inline static std::string sCurrentCCDBpath = ""; + inline static std::string sCurrentLUTpath = sDefaultLUTpath; + // Before instance() call, setup url and path + static void setCCDBurl(const std::string& url) { sCurrentCCDBpath = url; } + static void setLUTpath(const std::string& path) { sCurrentLUTpath = path; } + bool mFirstUpdate{true}; // option in case if LUT should be updated during workflow initialization + static SingleLUT& Instance(const Table_t* table = nullptr, long timestamp = -1) + { + if (sCurrentCCDBpath == "") { + sCurrentCCDBpath = o2::base::NameConf::getCCDBServer(); + } + static SingleLUT instanceLUT; + if (table != nullptr) { + instanceLUT.initFromTable(table); + instanceLUT.mFirstUpdate = false; + } else if (instanceLUT.mFirstUpdate) { + instanceLUT.initCCDB(sCurrentCCDBpath, sCurrentLUTpath, timestamp); + instanceLUT.mFirstUpdate = false; + } + return instanceLUT; + } +}; + } // namespace fit } // namespace o2 + #endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h new file mode 100644 index 0000000000000..79aba23841abc --- /dev/null +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawDataMetric.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// file RawDataMetric.h class Struct for collecting metrics during decoding +// with Artur.Furs@cern.ch +// +#ifndef ALICEO2_FIT_RAWDATAMETRIC_H_ +#define ALICEO2_FIT_RAWDATAMETRIC_H_ + +#include +#include +#include +#include + +namespace o2 +{ +namespace fit +{ +struct RawDataMetric { + RawDataMetric(uint8_t linkID, uint8_t EPID, uint16_t FEEID, bool isRegisteredFEE = true) : mLinkID(linkID), mEPID(EPID), mFEEID(FEEID), mIsRegisteredFEE(isRegisteredFEE) {} + ~RawDataMetric() = default; + + enum EStatusBits { + kIncompletePayload, // Incomplete payload + kWrongDescriptor, // Wrong descriptors in header + kWrongChannelMapping, // Wrong channel mapping + kEmptyDataBlock, // Only header in data block + kDecodedDataBlock // Decoded w/o any issue data block + }; + typedef uint8_t Status_t; + constexpr static uint8_t sNbits = 5; + inline bool checkBadDataBlock(Status_t metric) + { + bool result = checkStatusBit(metric, EStatusBits::kIncompletePayload); + if (!result) { // Incomplete payload has high priority among errors + result |= checkStatusBit(metric, EStatusBits::kEmptyDataBlock); + // Lets just check this for a while, w/o any decision for data block + // result |= checkStatusBit(metric,EStatusBits::kWrongDescriptor); + checkStatusBit(metric, EStatusBits::kWrongDescriptor); + } + return result; + } + inline void addStatusBit(EStatusBits statusBit, bool val = true) + { + mBitStats[statusBit] += static_cast(val); + } + + inline bool checkStatusBit(Status_t metric, EStatusBits statusBit) + { + const bool result = (metric & (1 << statusBit)) > 0; + mBitStats[statusBit] += static_cast(result); + return result; + } + + inline static bool isBitActive(Status_t metric, EStatusBits statusBit) + { + return (metric & (1 << statusBit)) > 0; + } + + inline static void setStatusBit(Status_t& metric, EStatusBits statusBit, bool val = true) + { + metric |= (static_cast(val) << statusBit); + } + void print() const; + static Status_t getAllBitsActivated(); + uint8_t mLinkID; + uint8_t mEPID; + uint16_t mFEEID; + bool mIsRegisteredFEE; + std::array mBitStats{}; + const static std::map sMapBitsToNames; +}; +} // namespace fit +} // namespace o2 +#endif diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h index a7edf6785e69e..ec04f770f8061 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/RawEventData.h @@ -28,6 +28,7 @@ struct EventHeader { static constexpr size_t PayloadPerGBTword = 10; static constexpr size_t MinNelements = 1; static constexpr size_t MaxNelements = 1; + static constexpr uint64_t sDescriptor = 0xf; uint64_t bc : 12; uint64_t orbit : 32; uint64_t phase : 3; @@ -44,6 +45,7 @@ struct EventHeader { bc = intRec.bc; orbit = intRec.orbit; } + inline bool isBadDescriptor() const { return startDescriptor != sDescriptor; } void print() const; } __attribute__((__packed__)); @@ -54,7 +56,7 @@ struct EventData { static constexpr size_t MaxNelements = 12; int16_t time : 12; int16_t charge : 13; - uint8_t pmBits : 8; + uint16_t pmBits : 8; uint8_t reservedField : 3; uint8_t channelID : 4; void generateFlags() @@ -76,7 +78,7 @@ struct EventData { struct TCMdata { static constexpr size_t PayloadSize = 10; static constexpr size_t PayloadPerGBTword = 10; - static constexpr size_t MinNelements = 1; + static constexpr size_t MinNelements = 0; static constexpr size_t MaxNelements = 1; uint64_t orA : 1, // 0 bit (0 byte) diff --git a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h index dbdf4759c1dce..a660d77820207 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/Triggers.h @@ -25,10 +25,15 @@ namespace o2 { namespace fit { - class Triggers { public: + template + constexpr static uint64_t word(TrgBits&&... trgBits) + { + return ((1ull << std::forward(trgBits)) | ...); + } + enum { bitA = 0, bitC = 1, // alias of bitAOut (FT0/FDD) bitAOut = 1, // alias of bitC (FV0) @@ -40,7 +45,8 @@ class Triggers bitAIn = 4, // alias of bitVertex (FV0) bitLaser = 5, // indicates the laser was triggered in this BC bitOutputsAreBlocked = 6, // indicates that laser-induced pulses should arrive from detector to FEE in this BC (and trigger outputs are blocked) - bitDataIsValid = 7 }; // data is valid for processing + bitDataIsValid = 7, // data is valid for processing + bitMinBias = 8 }; // extra calculated bit, vrt & (cern || semicent) static const int16_t DEFAULT_TIME = -5000; // for average of one side (A or C) static const int16_t DEFAULT_AMP = 0; static const int16_t DEFAULT_ZERO = 0; @@ -56,6 +62,21 @@ class Triggers timeA = atimeA; timeC = atimeC; } + inline static bool checkMinBiasFT0(uint64_t trgWord) + { + return static_cast(trgWord & word(bitVertex)) && static_cast(trgWord & word(bitSCen, bitCen)); + } + static uint64_t makeExtendedTrgWord(uint64_t trgWord) + { + return trgWord | (static_cast(checkMinBiasFT0(trgWord)) << bitMinBias); + } + static constexpr std::pair parseDigitTriggerWord(uint8_t digitWord, bool shiftTechBitsToBegin = false) + { + const uint8_t techWordMask = word(bitLaser, bitOutputsAreBlocked, bitDataIsValid); + const uint8_t shiftTechWordPos = shiftTechBitsToBegin ? bitLaser : 0; + return {(digitWord & (~techWordMask)), (digitWord & techWordMask) >> shiftTechWordPos}; + } + bool getOrA() const { return (triggersignals & (1 << bitA)) != 0; } bool getOrC() const { return (triggersignals & (1 << bitC)) != 0; } // only used by FT0/FDD (same bit as OrAOut in FV0) bool getOrAOut() const { return (triggersignals & (1 << bitAOut)) != 0; } // only used by FV0 (same bit as OrC in FT0/FDD) @@ -68,6 +89,8 @@ class Triggers bool getLaser() const { return (triggersignals & (1 << bitLaser)) != 0; } bool getOutputsAreBlocked() const { return (triggersignals & (1 << bitOutputsAreBlocked)) != 0; } bool getDataIsValid() const { return (triggersignals & (1 << bitDataIsValid)) != 0; } + bool getMinBiasFT0() const { return checkMinBiasFT0(static_cast(triggersignals)); } + uint64_t getExtendedTrgWordFT0() const { return makeExtendedTrgWord(static_cast(triggersignals)); } uint8_t getTriggersignals() const { return triggersignals; } uint8_t getNChanA() const { return nChanA; } @@ -87,6 +110,10 @@ class Triggers timeA = atimeA; timeC = atimeC; } + void setTriggers(uint8_t trgsig) + { + triggersignals = trgsig; + } void setTriggers(Bool_t isA, Bool_t isC, Bool_t isVrtx, Bool_t isCnt, Bool_t isSCnt, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC, Bool_t isLaser, Bool_t isOutputsAreBlocked, Bool_t isDataValid) diff --git a/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h b/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h index 6f41ab93eed79..e005315e2d6a0 100644 --- a/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h +++ b/DataFormats/Detectors/FIT/common/src/DataFormatsFITLinkDef.h @@ -25,6 +25,19 @@ #pragma link C++ std::vector < std::pair < uint64_t, int>> + ; #pragma link C++ struct o2::fit::DCSDPValues + ; +/* +#include "DetectorsCommonDataFormats/DetID.h" +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; + +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; + +#pragma link C++ struct o2::fit::ChannelData + ; +#pragma link C++ struct o2::fit::Digit + ; +*/ +// #pragma link C++ struct o2::fit::ChannelData; +// #pragma link C++ struct o2::fit::Digit; // TODO AM: Set this here when unused class warning is solved. // #pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::fit::DCSDPValues> + ; diff --git a/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx b/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx new file mode 100644 index 0000000000000..73c0b1bf1bb9e --- /dev/null +++ b/DataFormats/Detectors/FIT/common/src/LookUpTable.cxx @@ -0,0 +1,26 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFIT/LookUpTable.h" +#include "CCDB/BasicCCDBManager.h" +#include +using namespace o2::fit; +template +void LookupTableBase::initCCDB(const std::string& urlCCDB, const std::string& pathToStorageInCCDB, long timestamp) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(urlCCDB); + mVecEntryFEE = *(mgr.getForTimeStamp::Table_t>(pathToStorageInCCDB, timestamp)); + prepareLUT(); +} +template class o2::fit::LookupTableBase, + std::unordered_map>; diff --git a/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx b/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx new file mode 100644 index 0000000000000..2b686afc6cfcd --- /dev/null +++ b/DataFormats/Detectors/FIT/common/src/RawDataMetric.cxx @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsFIT/RawDataMetric.h" +#include +using namespace o2::fit; +const std::map RawDataMetric::sMapBitsToNames = { + {RawDataMetric::EStatusBits::kIncompletePayload, "IncompletePayload"}, + {RawDataMetric::EStatusBits::kWrongDescriptor, "WrongDescriptor"}, + {RawDataMetric::EStatusBits::kWrongChannelMapping, "WrongChannelMapping"}, + {RawDataMetric::EStatusBits::kEmptyDataBlock, "EmptyDataBlock"}, + {RawDataMetric::EStatusBits::kDecodedDataBlock, "DecodedDataBlock"}}; +void RawDataMetric::print() const +{ + LOG(info) << "=============================================================="; + LOG(info) << "Raw data metric: linkID " << static_cast(mLinkID) << " mEPID " << static_cast(mEPID) << " FEEID " << static_cast(mFEEID); + LOG(info) << "Is registered FEE: " << mIsRegisteredFEE; + for (const auto& entry : sMapBitsToNames) { + LOG(info) << entry.second << ": " << mBitStats[entry.first]; + } + LOG(info) << "=============================================================="; +} +RawDataMetric::Status_t RawDataMetric::getAllBitsActivated() +{ + Status_t metricStatus{}; + for (const auto& entry : sMapBitsToNames) { + metricStatus |= (1 << entry.first); + } + return metricStatus; +} diff --git a/DataFormats/Detectors/GlobalTracking/CMakeLists.txt b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt index 41f44e02afe30..b219de73f5b47 100644 --- a/DataFormats/Detectors/GlobalTracking/CMakeLists.txt +++ b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library( DataFormatsGlobalTracking SOURCES src/RecoContainer.cxx @@ -34,7 +36,8 @@ o2_add_library( O2::DataFormatsCPV O2::DataFormatsPHOS O2::DataFormatsEMCAL - O2::GPUDataTypeHeaders + O2::GPUDataTypes + $<$:O2::ITS3Reconstruction> PRIVATE_LINK_LIBRARIES O2::Framework) diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h index b6c28f97ba48a..31d531ef19265 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h @@ -19,7 +19,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "ReconstructionDataFormats/GlobalTrackAccessor.h" #include "CommonDataFormat/RangeReference.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/MatchingType.h" #include "CommonDataFormat/AbstractRefAccessor.h" @@ -37,6 +37,7 @@ namespace o2::tpc class TrackTPC; using TPCClRefElem = uint32_t; struct ClusterNativeAccess; +struct TriggerInfoDLBZS; namespace internal { struct getWorkflowTPCInput_ret; @@ -163,7 +164,11 @@ class PrimaryVertex; class VtxTrackIndex; class VtxTrackRef; class V0; +class V0Index; class Cascade; +class CascadeIndex; +class Decay3Body; +class Decay3BodyIndex; class StrangeTrack; class TrackCosmics; class GlobalFwdTrack; @@ -220,12 +225,14 @@ struct DataRequest { void requestITSClusters(bool mc); void requestMFTClusters(bool mc); void requestTPCClusters(bool mc); + void requestTPCOccMap(); + void requestTPCTriggers(); void requestTOFClusters(bool mc); void requestTRDTracklets(bool mc); void requestMCHClusters(bool mc); void requestMIDClusters(bool mc); void requestHMPClusters(bool mc); - // void requestHMPMatches(bool mc); // no input available at the moment + void requestHMPMatches(bool mc); // no input available at the moment void requestCTPDigits(bool mc); @@ -235,12 +242,16 @@ struct DataRequest { void requestCoscmicTracks(bool mc); - void requestPrimaryVertertices(bool mc); - void requestPrimaryVerterticesTMP(bool mc); - void requestSecondaryVertertices(bool mc); + void requestPrimaryVertices(bool mc); + void requestPrimaryVerticesTMP(bool mc); + void requestSecondaryVertices(bool mc); void requestStrangeTracks(bool mc); void requestIRFramesITS(); + +#ifdef ENABLE_UPGRADES + void requestIT3Clusters(bool mc); +#endif }; // Helper class to requested data. @@ -280,10 +291,13 @@ struct RecoContainer { NPVTXSLOTS }; // slots to register secondary vertex data - enum SVTXSlots { V0S, // V0 objects + enum SVTXSlots { V0SIDX, // V0s indices + V0S, // V0 objects PVTX_V0REFS, // PV -> V0 references + CASCSIDX, // Cascade indices CASCS, // Cascade objects PVTX_CASCREFS, // PV -> Cascade reference + DECAY3BODYIDX, // 3-body decay indices DECAY3BODY, // 3-body decay objects PVTX_3BODYREFS, // PV -> 3-body decay references NSVTXSLOTS }; @@ -308,6 +322,8 @@ struct RecoContainer { using GTrackID = o2::dataformats::GlobalTrackID; using GlobalIDSet = std::array; + static constexpr float PS2MUS = 1e-6; + o2::InteractionRecord startIR; // TF start IR std::array commonPool; @@ -329,6 +345,7 @@ struct RecoContainer { o2::ctp::LumiInfo mCTPLumi; gsl::span clusterShMapTPC; ///< externally set TPC clusters sharing map + gsl::span occupancyMapTPC; ///< externally set TPC clusters occupancy map std::unique_ptr inputsTPCclusters; // special struct for TPC clusters access std::unique_ptr inputsTRD; // special struct for TRD tracklets, trigger records @@ -354,14 +371,15 @@ struct RecoContainer { void addTOFMatchesTPCTRD(o2::framework::ProcessingContext& pc, bool mc); void addTOFMatchesITSTPCTRD(o2::framework::ProcessingContext& pc, bool mc); - // void addHMPMatches(o2::framework::ProcessingContext& pc, bool mc); // no input available for the moment - + void addHMPMatches(o2::framework::ProcessingContext& pc, bool mc); void addMFTMCHMatches(o2::framework::ProcessingContext& pc, bool mc); void addMCHMIDMatches(o2::framework::ProcessingContext& pc, bool mc); void addITSClusters(o2::framework::ProcessingContext& pc, bool mc); void addMFTClusters(o2::framework::ProcessingContext& pc, bool mc); - void addTPCClusters(o2::framework::ProcessingContext& pc, bool mc, bool shmap); + void addTPCClusters(o2::framework::ProcessingContext& pc, bool mc, bool shmap, bool occmap); + void addTPCOccMap(o2::framework::ProcessingContext& pc); + void addTPCTriggers(o2::framework::ProcessingContext& pc); void addTOFClusters(o2::framework::ProcessingContext& pc, bool mc); void addHMPClusters(o2::framework::ProcessingContext& pc, bool mc); void addTRDTracklets(o2::framework::ProcessingContext& pc, bool mc); @@ -390,6 +408,10 @@ struct RecoContainer { void addIRFramesITS(o2::framework::ProcessingContext& pc); +#ifdef ENABLE_UPGRADES + void addIT3Clusters(o2::framework::ProcessingContext& pc, bool mc); +#endif + // custom getters // get contributors from single detectors: return array with sources set to all contributing GTrackIDs @@ -527,6 +549,7 @@ struct RecoContainer { auto getTPCTrackMCLabel(GTrackID id) const { return getObject(id, MCLABELS); } const o2::tpc::ClusterNativeAccess& getTPCClusters() const; const o2::dataformats::ConstMCTruthContainerView* getTPCClustersMCLabels() const; + auto getTPCTriggers() const { return getSpan(GTrackID::TPC, MATCHES); } // ITS-TPC const o2::dataformats::TrackTPCITS& getTPCITSTrack(GTrackID gid) const { return getTrack(gid); } @@ -612,13 +635,9 @@ struct RecoContainer { auto getITSTPCTRDTOFMatches() const { return getSpan(GTrackID::ITSTPCTRDTOF, MATCHES); } auto getITSTPCTRDTOFMatchesMCLabels() const { return getSpan(GTrackID::ITSTPCTRDTOF, MCLABELS); } - /* no input available for the moment // HMPID matches - auto getHMPMatchTriggers() const { return getSpan(GTrackID::HMP, TRACKREFS); } auto getHMPMatches() const { return getSpan(GTrackID::HMP, MATCHES); } - auto getHMPPhotsClusterCharges() const { return getSpan(GTrackID::HMP, PATTERNS); } auto getHMPMatchesMCLabels() const { return getSpan(GTrackID::HMP, MCLABELS); } - */ // TOF clusters auto getTOFClusters() const { return getSpan(GTrackID::TOF, CLUSTERS); } @@ -677,13 +696,21 @@ struct RecoContainer { auto getPrimaryVertexMCLabels() const { return pvtxPool.getSpan(PVTX_MCTR); } // Secondary vertices + const o2::dataformats::V0Index& getV0Idx(int i) const { return svtxPool.get_as(V0SIDX, i); } const o2::dataformats::V0& getV0(int i) const { return svtxPool.get_as(V0S, i); } + const o2::dataformats::CascadeIndex& getCascadeIdx(int i) const { return svtxPool.get_as(CASCSIDX, i); } const o2::dataformats::Cascade& getCascade(int i) const { return svtxPool.get_as(CASCS, i); } + + auto getV0sIdx() const { return svtxPool.getSpan(V0SIDX); } auto getV0s() const { return svtxPool.getSpan(V0S); } auto getPV2V0Refs() { return svtxPool.getSpan>(PVTX_V0REFS); } + + auto getCascadesIdx() const { return svtxPool.getSpan(CASCSIDX); } auto getCascades() const { return svtxPool.getSpan(CASCS); } auto getPV2CascadesRefs() { return svtxPool.getSpan>(PVTX_CASCREFS); } - auto getDecays3Body() const { return svtxPool.getSpan(DECAY3BODY); } + + auto getDecays3BodyIdx() const { return svtxPool.getSpan(DECAY3BODYIDX); } + auto getDecays3Body() const { return svtxPool.getSpan(DECAY3BODY); } auto getPV2Decays3BodyRefs() { return svtxPool.getSpan>(PVTX_3BODYREFS); } // Strangeness track @@ -699,6 +726,18 @@ struct RecoContainer { // IRFrames where ITS was reconstructed and tracks were seen (e.g. sync.w-flow mult. selection) auto getIRFramesITS() const { return getSpan(GTrackID::ITS, VARIA); } + + void getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPCTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITSTPC(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPCTOF(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeITS(GTrackID gid, float& t, float& tErr) const; + void getTrackTimeTPC(GTrackID gid, float& t, float& tErr) const; + + void getTrackTime(GTrackID gid, float& t, float& tErr) const; }; } // namespace globaltracking diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h index 9730f8c7ac506..ecce5a4d58b2f 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h @@ -17,6 +17,7 @@ #include "Framework/InputSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsTPC/WorkflowHelper.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTRD/RecoInputContainer.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" @@ -149,8 +150,8 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID const auto& match = matchesITSTPCTRDTOF[i]; auto gidx = match.getTrackRef(); // this should be corresponding ITS-TPC-TRD track // no need to check isUsed: by construction this ITS-TPC-TRD was not used elsewhere - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksITSTPCTRD[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used ITS-TPC-TRD tracks @@ -169,9 +170,9 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID } for (unsigned i = 0; i < matchesTPCTRDTOF.size(); i++) { const auto& match = matchesTPCTRDTOF[i]; - auto gidx = match.getTrackRef(); // this should be corresponding TPC-TRD track - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + auto gidx = match.getTrackRef(); // this should be corresponding TPC-TRD track + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksTPCTRD[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used TPC-TRD tracks @@ -195,12 +196,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track } } @@ -223,8 +223,8 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID continue; } // no need to check isUsed: by construction this ITS-TPC was not used elsewhere - const auto& tofCl = tofClusters[match.getTOFClIndex()]; - float timeTOFMUS = (tofCl.getTime() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + // const auto& tofCl = tofClusters[match.getTOFClIndex()]; + float timeTOFMUS = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF const float timeErr = 0.010f; // assume 10 ns error FIXME if (creator(tracksTPCITS[gidx.getIndex()], {i, currentSource}, timeTOFMUS, timeErr)) { flagUsed(gidx); // flag used ITS-TPC tracks @@ -248,12 +248,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track } } @@ -286,7 +285,7 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID if (srcSel[currentSource]) { if (matchesTPCTOF.size() && !tracksTPCTOF.size()) { throw std::runtime_error(fmt::format("TPC-TOF matched tracks ({}) require TPCTOF matches ({}) and TPCTOF tracks ({})", - matchesTPCTOF.size(), tracksTPCTOF.size())); + -1, matchesTPCTOF.size(), tracksTPCTOF.size())); } for (unsigned i = 0; i < matchesTPCTOF.size(); i++) { const auto& match = matchesTPCTOF[i]; @@ -322,7 +321,7 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID if (srcSel[currentSource]) { if (matchesMCHMID.size() && !tracksMCH.size()) { throw std::runtime_error(fmt::format("MCH-MID matched tracks ({}) require MCHMID matches ({}) and MCH tracks ({})", - matchesMCHMID.size(), tracksMCH.size())); + -1, matchesMCHMID.size(), tracksMCH.size())); } for (unsigned i = 0; i < matchesMCHMID.size(); i++) { const auto& match = matchesMCHMID[i]; diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h index be156f44f8cc3..3ca1a33325b96 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/TrackTuneParams.h @@ -16,6 +16,7 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include namespace o2 { @@ -32,13 +33,19 @@ struct TrackTuneParams : public o2::conf::ConfigurableParamHelper getCovInnerTotal(float scale) const; + std::array getCovOuterTotal(float scale) const; O2ParamDef(TrackTuneParams, "trackTuneParams"); }; diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index b789486b4f82c..dd206ffe3b70d 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -23,9 +23,10 @@ #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "SimulationDataFormat/MCEventLabel.h" +#include "ReconstructionDataFormats/DecayNBodyIndex.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "ReconstructionDataFormats/StrangeTrack.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" @@ -41,6 +42,10 @@ #include "Framework/DataRefUtils.h" #include "Framework/CCDBParamSpec.h" +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif + using namespace o2::globaltracking; using namespace o2::framework; namespace o2d = o2::dataformats; @@ -118,6 +123,7 @@ void DataRequest::requestTPCTracks(bool mc) addInput({"trackTPCClRefs", "TPC", "CLUSREFS", 0, Lifetime::Timeframe}); if (requestMap.find("clusTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"trackTPCMCTR", "TPC", "TRACKSMCLBL", 0, Lifetime::Timeframe}); @@ -225,14 +231,29 @@ void DataRequest::requestITSClusters(bool mc) addInput({"clusITS", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}); addInput({"clusITSPatt", "ITS", "PATTERNS", 0, Lifetime::Timeframe}); addInput({"clusITSROF", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}); - addInput({"cldictITS", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")}); addInput({"alpparITS", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")}); if (mc) { addInput({"clusITSMC", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe}); } + addInput({"cldictITS", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")}); requestMap["clusITS"] = mc; } +#ifdef ENABLE_UPGRADES +void DataRequest::requestIT3Clusters(bool mc) +{ + addInput({"clusITS", "ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}); + addInput({"clusITSPatt", "ITS", "PATTERNS", 0, Lifetime::Timeframe}); + addInput({"clusITSROF", "ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}); + addInput({"alpparITS", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")}); + if (mc) { + addInput({"clusITSMC", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe}); + } + addInput({"cldictIT3", "IT3", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/ClusterDictionary")}); + requestMap["clusIT3"] = mc; +} +#endif + void DataRequest::requestMFTClusters(bool mc) { addInput({"clusMFT", "MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe}); @@ -246,11 +267,21 @@ void DataRequest::requestMFTClusters(bool mc) requestMap["clusMFT"] = mc; } +void DataRequest::requestTPCOccMap() +{ + addInput({"clusTPCoccmap", "TPC", "TPCOCCUPANCYMAP", 0, Lifetime::Timeframe}); + requestMap["TPCOcc"] = false; +} + void DataRequest::requestTPCClusters(bool mc) { addInput({"clusTPC", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe}); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + requestTPCTriggers(); + } if (requestMap.find("trackTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"clusTPCMC", ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}, Lifetime::Timeframe}); @@ -258,6 +289,12 @@ void DataRequest::requestTPCClusters(bool mc) requestMap["clusTPC"] = mc; } +void DataRequest::requestTPCTriggers() +{ + addInput({"trigTPC", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}); + requestMap["trigTPC"] = false; +} + void DataRequest::requestTOFClusters(bool mc) { addInput({"tofcluster", "TOF", "CLUSTERS", 0, Lifetime::Timeframe}); @@ -368,7 +405,7 @@ void DataRequest::requestCoscmicTracks(bool mc) requestMap["Cosmics"] = mc; } -void DataRequest::requestPrimaryVertertices(bool mc) +void DataRequest::requestPrimaryVertices(bool mc) { addInput({"pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe}); addInput({"pvtx_trmtc", "GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}); // global ids of associated tracks @@ -379,7 +416,7 @@ void DataRequest::requestPrimaryVertertices(bool mc) requestMap["PVertex"] = mc; } -void DataRequest::requestPrimaryVerterticesTMP(bool mc) // primary vertices before global vertex-track matching +void DataRequest::requestPrimaryVerticesTMP(bool mc) // primary vertices before global vertex-track matching { addInput({"pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe}); addInput({"pvtx_cont", "GLO", "PVTX_CONTID", 0, Lifetime::Timeframe}); // global ids of contributors @@ -390,12 +427,15 @@ void DataRequest::requestPrimaryVerterticesTMP(bool mc) // primary vertices befo requestMap["PVertexTMP"] = mc; } -void DataRequest::requestSecondaryVertertices(bool) +void DataRequest::requestSecondaryVertices(bool) { + addInput({"v0sIdx", "GLO", "V0S_IDX", 0, Lifetime::Timeframe}); addInput({"v0s", "GLO", "V0S", 0, Lifetime::Timeframe}); addInput({"p2v0s", "GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}); + addInput({"cascsIdx", "GLO", "CASCS_IDX", 0, Lifetime::Timeframe}); addInput({"cascs", "GLO", "CASCS", 0, Lifetime::Timeframe}); addInput({"p2cascs", "GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}); + addInput({"decay3bodyIdx", "GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe}); addInput({"decay3body", "GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}); addInput({"p2decay3body", "GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}); requestMap["SVertex"] = false; // no MC provided for secondary vertices @@ -451,18 +491,14 @@ void DataRequest::requestEMCALCells(bool mc) requestMap["EMCCells"] = mc; } -/* void DataRequest::requestHMPMatches(bool mc) { addInput({"matchHMP", "HMP", "MATCHES", 0, Lifetime::Timeframe}); - addInput({"matchTriggerHMP", "HMP", "TRACKREFS", 0, Lifetime::Timeframe}); - addInput({"matchPhotsCharge", "HMP", "PATTERNS", 0, Lifetime::Timeframe}); if (mc) { addInput({"clsHMP_GLO_MCTR", "HMP", "MCLABELS", 0, Lifetime::Timeframe}); } requestMap["matchHMP"] = mc; } -*/ void DataRequest::requestTracks(GTrackID::mask_t src, bool useMC) { @@ -531,9 +567,9 @@ void DataRequest::requestTracks(GTrackID::mask_t src, bool useMC) if (GTrackID::includesDet(DetID::HMP, src)) { requestHMPClusters(useMC); } - // if (src[GTrackID::HMP]) { - // requestHMPMatches(useMC); - // } + if (src[GTrackID::HMP]) { + requestHMPMatches(useMC); + } } void DataRequest::requestClusters(GTrackID::mask_t src, bool useMC, DetID::mask_t skipDetClusters) @@ -662,14 +698,34 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques addITSClusters(pc, req->second); } +#ifdef ENABLE_UPGRADES + req = reqMap.find("clusIT3"); + if (req != reqMap.end()) { + addIT3Clusters(pc, req->second); + } +#endif + req = reqMap.find("clusMFT"); if (req != reqMap.end()) { addMFTClusters(pc, req->second); } + req = reqMap.find("TPCOcc"); + bool TPCOccDone = false; + if (req != reqMap.end()) { + TPCOccDone = true; + addTPCOccMap(pc); + } + req = reqMap.find("clusTPC"); if (req != reqMap.end()) { - addTPCClusters(pc, req->second, reqMap.find("trackTPC") != reqMap.end()); + auto tracksON = reqMap.find("trackTPC") != reqMap.end(); + addTPCClusters(pc, req->second, tracksON, tracksON && (!TPCOccDone)); + } + + req = reqMap.find("trigTPC"); + if (req != reqMap.end()) { + addTPCTriggers(pc); } req = reqMap.find("clusTOF"); @@ -766,20 +822,23 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques if (req != reqMap.end()) { addIRFramesITS(pc); } - // req = reqMap.find("matchHMP"); - // if (req != reqMap.end()) { - // addHMPMatches(pc, req->second); - // } + req = reqMap.find("matchHMP"); + if (req != reqMap.end()) { + addHMPMatches(pc, req->second); + } } //____________________________________________________________ void RecoContainer::addSVertices(ProcessingContext& pc, bool) { + svtxPool.registerContainer(pc.inputs().get>("v0sIdx"), V0SIDX); svtxPool.registerContainer(pc.inputs().get>("v0s"), V0S); svtxPool.registerContainer(pc.inputs().get>>("p2v0s"), PVTX_V0REFS); + svtxPool.registerContainer(pc.inputs().get>("cascsIdx"), CASCSIDX); svtxPool.registerContainer(pc.inputs().get>("cascs"), CASCS); svtxPool.registerContainer(pc.inputs().get>>("p2cascs"), PVTX_CASCREFS); - svtxPool.registerContainer(pc.inputs().get>("decay3body"), DECAY3BODY); + svtxPool.registerContainer(pc.inputs().get>("decay3bodyIdx"), DECAY3BODYIDX); + svtxPool.registerContainer(pc.inputs().get>("decay3body"), DECAY3BODY); svtxPool.registerContainer(pc.inputs().get>>("p2decay3body"), PVTX_3BODYREFS); // no mc } @@ -787,13 +846,11 @@ void RecoContainer::addSVertices(ProcessingContext& pc, bool) //____________________________________________________________ void RecoContainer::addPVertices(ProcessingContext& pc, bool mc) { - if (!pvtxPool.isLoaded(PVTX)) { // in case was loaded via addPVerticesTMP - pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); - } + pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); pvtxPool.registerContainer(pc.inputs().get>("pvtx_trmtc"), PVTX_TRMTC); pvtxPool.registerContainer(pc.inputs().get>("pvtx_tref"), PVTX_TRMTCREFS); - if (mc && !pvtxPool.isLoaded(PVTX_MCTR)) { // in case was loaded via addPVerticesTMP + if (mc) { // in case was loaded via addPVerticesTMP pvtxPool.registerContainer(pc.inputs().get>("pvtx_mc"), PVTX_MCTR); } } @@ -810,13 +867,11 @@ void RecoContainer::addStrangeTracks(ProcessingContext& pc, bool mc) //____________________________________________________________ void RecoContainer::addPVerticesTMP(ProcessingContext& pc, bool mc) { - if (!pvtxPool.isLoaded(PVTX)) { // in case was loaded via addPVertices - pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); - } + pvtxPool.registerContainer(pc.inputs().get>("pvtx"), PVTX); pvtxPool.registerContainer(pc.inputs().get>("pvtx_cont"), PVTX_CONTID); pvtxPool.registerContainer(pc.inputs().get>("pvtx_contref"), PVTX_CONTIDREFS); - if (mc && !pvtxPool.isLoaded(PVTX_MCTR)) { // in case was loaded via addPVertices + if (mc) { // in case was loaded via addPVertices pvtxPool.registerContainer(pc.inputs().get>("pvtx_mc"), PVTX_MCTR); } } @@ -1003,18 +1058,14 @@ void RecoContainer::addTOFMatchesITSTPCTRD(ProcessingContext& pc, bool mc) } } -/* //__________________________________________________________ void RecoContainer::addHMPMatches(ProcessingContext& pc, bool mc) { - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchHMP"), MATCHES); // HMPID match info, no real tracks - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchTriggerHMP"), TRACKREFS); // HMPID triggers - commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchPhotsCharge"), PATTERNS); // HMPID photon cluster charges + commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("matchHMP"), MATCHES); // HMPID match info, no real tracks if (mc) { commonPool[GTrackID::HMP].registerContainer(pc.inputs().get>("clsHMP_GLO_MCTR"), MCLABELS); } } -*/ //__________________________________________________________ void RecoContainer::addITSClusters(ProcessingContext& pc, bool mc) @@ -1031,6 +1082,22 @@ void RecoContainer::addITSClusters(ProcessingContext& pc, bool mc) } } +#ifdef ENABLE_UPGRADES +void RecoContainer::addIT3Clusters(ProcessingContext& pc, bool mc) +{ + if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once + pc.inputs().get*>("alpparITS"); // note: configurable param does not need finaliseCCDB + pc.inputs().get("cldictIT3"); // just to trigger the finaliseCCDB + } + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITSROF"), CLUSREFS); + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITS"), CLUSTERS); + commonPool[GTrackID::ITS].registerContainer(pc.inputs().get>("clusITSPatt"), PATTERNS); + if (mc) { + mcITSClusters = pc.inputs().get*>("clusITSMC"); + } +} +#endif + //__________________________________________________________ void RecoContainer::addMFTClusters(ProcessingContext& pc, bool mc) { @@ -1047,12 +1114,27 @@ void RecoContainer::addMFTClusters(ProcessingContext& pc, bool mc) } //__________________________________________________________ -void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap) +void RecoContainer::addTPCOccMap(ProcessingContext& pc) +{ + occupancyMapTPC = pc.inputs().get>("clusTPCoccmap"); +} + +//__________________________________________________________ +void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap, bool occmap) { inputsTPCclusters = o2::tpc::getWorkflowTPCInput(pc, 0, mc); if (shmap) { clusterShMapTPC = pc.inputs().get>("clusTPCshmap"); } + if (occmap) { + occupancyMapTPC = pc.inputs().get>("clusTPCoccmap"); + } +} + +//__________________________________________________________ +void RecoContainer::addTPCTriggers(ProcessingContext& pc) +{ + commonPool[GTrackID::TPC].registerContainer(pc.inputs().get>("trigTPC"), MATCHES); } //__________________________________________________________ @@ -1103,7 +1185,9 @@ void RecoContainer::addMIDClusters(ProcessingContext& pc, bool mc) void RecoContainer::addCTPDigits(ProcessingContext& pc, bool mc) { commonPool[GTrackID::CTP].registerContainer(pc.inputs().get>("CTPDigits"), CLUSTERS); - mCTPLumi = pc.inputs().get("CTPLumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { + mCTPLumi = pc.inputs().get("CTPLumi"); + } if (mc) { // pc.inputs().get*>("CTPDigitsMC"); } @@ -1356,8 +1440,7 @@ RecoContainer::GlobalIDSet RecoContainer::getSingleDetectorRefs(GTrackID gidx) c table[GTrackID::TRD] = parent0.getTrackRef(); // there is no standalone TRD track, so use the index for the ITSTPCTRD track array } else if (src == GTrackID::TPCTRDTOF) { const auto& parent0 = getTOFMatch(gidx); // TPCTRD : TOF - const auto& parent1 = getITSTPCTRDTrack(parent0.getTrackRef()); - const auto& parent2 = getTPCITSTrack(parent1.getRefGlobalTrackId()); + const auto& parent1 = getTPCTRDTrack(parent0.getTrackRef()); table[GTrackID::TPCTRD] = parent0.getTrackRef(); table[GTrackID::TPC] = parent1.getRefGlobalTrackId(); table[GTrackID::TOF] = {unsigned(parent0.getIdxTOFCl()), GTrackID::TOF}; @@ -1382,7 +1465,7 @@ RecoContainer::GlobalIDSet RecoContainer::getSingleDetectorRefs(GTrackID gidx) c table[GTrackID::MCH] = parent0.getMCHRef(); table[GTrackID::MID] = parent0.getMIDRef(); } - return std::move(table); + return table; } //________________________________________________________ @@ -1458,3 +1541,133 @@ const o2::dataformats::MCTruthContainer* RecoContainer::getE { return mcEMCCells.get(); } + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getITSTPCTRDTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getTPCTRDTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& match = getITSTPCTOFMatches()[gid]; + t = (match.getSignal() - match.getLTIntegralOut().getTOF(o2::track::PID::Pion)) * PS2MUS; // tof time in \mus, FIXME: account for time of flight to R TOF + tErr = 0.010f; +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) const +{ + const auto trigITSTPCTRD = getITSTPCTRDTriggers(); + // very slow: find the trigger this track belongs to + for (const auto& trig : trigITSTPCTRD) { + if (trig.getTrackRefs().getEntriesBound() > gid.getIndex()) { + t = trig.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 5.e-3; + const auto& trc = getITSTPCTRDTracks()[gid]; + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time + tErr += trc.getPileUpTimeErrorMUS(); + } + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) const +{ + const auto trigTPCTRD = getTPCTRDTriggers(); + // very slow: find the trigger this track belongs to + for (const auto& trig : trigTPCTRD) { + if (trig.getTrackRefs().getEntriesBound() > gid.getIndex()) { + t = trig.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 5.e-3; + const auto& trc = getTPCTRDTracks()[gid]; + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time + tErr += trc.getPileUpTimeErrorMUS(); + } + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITSTPC(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCITSTrack(gid); + t = trc.getTimeMUS().getTimeStamp(); + tErr = trc.getTimeMUS().getTimeStampError(); +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPCTOF(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCTOFTrack(gid); + t = trc.getTimeMUS().getTimeStamp(); + tErr = trc.getTimeMUS().getTimeStampError(); +} + +//________________________________________________________ +void RecoContainer::getTrackTimeITS(GTrackID gid, float& t, float& tErr) const +{ + const auto& rofrs = getITSTracksROFRecords(); + for (const auto& rof : rofrs) { // slow + if (gid.getIndex() < rof.getFirstEntry() + rof.getNEntries()) { + t = rof.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; + tErr = 0.5; + return; + } + } +} + +//________________________________________________________ +void RecoContainer::getTrackTimeTPC(GTrackID gid, float& t, float& tErr) const +{ + const auto& trc = getTPCTrack(gid); + t = trc.getTime0() + 0.5 * (trc.getDeltaTFwd() - trc.getDeltaTBwd()); + tErr = 0.5 * (trc.getDeltaTFwd() + trc.getDeltaTBwd()); +} + +//________________________________________________________ +void RecoContainer::getTrackTime(GTrackID gid, float& t, float& tErr) const +{ + auto src = gid.getSource(); + if (src == GTrackID::ITSTPCTRDTOF) { + return getTrackTimeITSTPCTRDTOF(gid, t, tErr); + } + if (src == GTrackID::TPCTRDTOF) { + return getTrackTimeTPCTRDTOF(gid, t, tErr); + } + if (src == GTrackID::ITSTPCTOF) { + return getTrackTimeITSTPCTOF(gid, t, tErr); + } + if (src == GTrackID::ITSTPCTRD) { + return getTrackTimeITSTPCTRD(gid, t, tErr); + } + if (src == GTrackID::TPCTRD) { + return getTrackTimeTPCTRD(gid, t, tErr); + } + if (src == GTrackID::ITSTPC) { + return getTrackTimeITSTPC(gid, t, tErr); + } + if (src == GTrackID::TPCTOF) { + return getTrackTimeTPCTOF(gid, t, tErr); + } + if (src == GTrackID::ITS) { + return getTrackTimeITS(gid, t, tErr); + } + if (src == GTrackID::TPC) { + return getTrackTimeTPC(gid, t, tErr); + } +} diff --git a/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx b/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx index ee5dfc954ad7e..138f91937b250 100644 --- a/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/TrackTuneParams.cxx @@ -15,3 +15,25 @@ #include "DataFormatsGlobalTracking/TrackTuneParams.h" O2ParamImpl(o2::globaltracking::TrackTuneParams); + +using namespace o2::globaltracking; + +std::array TrackTuneParams::getCovInnerTotal(float scale) const +{ + std::array cov{}; + for (int i = 0; i < 5; i++) { + cov[i] = tpcCovInner[i] + scale * tpcCovInnerSlope[i]; + cov[i] *= cov[i]; + } + return cov; +} + +std::array TrackTuneParams::getCovOuterTotal(float scale) const +{ + std::array cov{}; + for (int i = 0; i < 5; i++) { + cov[i] = tpcCovOuter[i] + scale * tpcCovOuterSlope[i]; + cov[i] *= cov[i]; + } + return cov; +} diff --git a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h index 3649c13fffb8d..9c777d5afb718 100644 --- a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h +++ b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Cluster.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -40,18 +40,22 @@ class Cluster kEmp = -1 }; // status flags public: - Cluster() : mCh(-1), mSi(-1), mSt(kEmp), mBox(-1), mNlocMax(-1), mMaxQpad(-1), mMaxQ(-1), mQRaw(0), mQ(0), mErrQ(-1), mXX(0), mErrX(-1), mYY(0), mErrY(-1), mChi2(-1), mParam(o2::hmpid::Param::instanceNoGeo()) { mDigs.clear(); }; - // Cluster(int chamber, int size, int NlocMax, float QRaw, float Q, float X, float Y) - // : mCh(chamber), mSi(size), mNlocMax(NlocMax), mQRaw(QRaw), mQ(Q), mXX(X), mYY(Y) { }; + Cluster() : mCh(-1), mSi(-1), mSt(kEmp), mBox(-1), mNlocMax(-1), mMaxQpad(-1), mMaxQ(-1), mQRaw(0), mQ(0), mErrQ(-1), mXX(0), mErrX(-1), mYY(0), mErrY(-1), mChi2(-1) {} // Methods // void draw(Option_t *opt=""); //overloaded TObject::Print() to draw cluster in current canvas void print(Option_t* opt = "") const; // overloaded TObject::Print() to print cluster info static void fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int iflag); // fit function to be used by MINUIT + void cleanPointers() + { + mDigs = nullptr; + } void coG(); // calculates center of gravity void corrSin(); // sinoidal correction void digAdd(const o2::hmpid::Digit* pDig); // add new digit to the cluster - const o2::hmpid::Digit* dig(int i) const { return mDigs[i]; } // pointer to i-th digi + const o2::hmpid::Digit* dig(int i) const { return mDigs ? (*mDigs)[i] : nullptr; } // pointer to i-th digi + const std::vector* getDigits() const { return mDigs; } + void setDigits(std::vector* v = nullptr) { mDigs = v; } inline bool isInPc(); // check if is in the current PC void reset(); // cleans the cluster // void setClusterParams(float xL, float yL, int iCh); //Set AliCluster3D part @@ -95,26 +99,25 @@ class Cluster // public: protected: - int mCh; // chamber number - int mSi; // size of the formed cluster from which this cluster deduced - int mSt; // flag to mark the quality of the cluster - int mBox; // box contaning this cluster - int mNlocMax; // number of local maxima in formed cluster - int mMaxQpad; // abs pad number of a pad with the highest charge - double mMaxQ; // that max charge value - double mQRaw; // QDC value of the raw cluster - double mQ; // QDC value of the actual cluster - double mErrQ; // error on Q - double mXX; // local x postion, [cm] - double mErrX; // error on x postion, [cm] - double mYY; // local y postion, [cm] - double mErrY; // error on y postion, [cm] - double mChi2; // some estimator of the fit quality - std::vector mDigs; //! list of digits forming this cluster + int mCh; // chamber number + int mSi; // size of the formed cluster from which this cluster deduced + int mSt; // flag to mark the quality of the cluster + int mBox; // box contaning this cluster + int mNlocMax; // number of local maxima in formed cluster + int mMaxQpad; // abs pad number of a pad with the highest charge + double mMaxQ; // that max charge value + double mQRaw; // QDC value of the raw cluster + double mQ; // QDC value of the actual cluster + double mErrQ; // error on Q + double mXX; // local x postion, [cm] + double mErrX; // error on x postion, [cm] + double mYY; // local y postion, [cm] + double mErrY; // error on y postion, [cm] + double mChi2; // some estimator of the fit quality + std::vector* mDigs = nullptr; //! list of digits forming this cluster public: static bool fgDoCorrSin; // flag to switch on/off correction for Sinusoidal to cluster reco - Param* mParam; //! Pointer to AliHMPIDParam ClassDefNV(Cluster, 3); }; diff --git a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h index b58d12ff6a259..63b04d454e66a 100644 --- a/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h +++ b/DataFormats/Detectors/HMPID/include/DataFormatsHMP/Digit.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,7 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// /// \file Digit.h /// \author Antonio Franco - INFN Bari /// \version 1.0 diff --git a/DataFormats/Detectors/HMPID/src/Cluster.cxx b/DataFormats/Detectors/HMPID/src/Cluster.cxx index 56017ef1cd114..e14075c16c842 100644 --- a/DataFormats/Detectors/HMPID/src/Cluster.cxx +++ b/DataFormats/Detectors/HMPID/src/Cluster.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -12,6 +12,7 @@ #include #include #include "DataFormatsHMP/Cluster.h" +#include "Framework/Logger.h" #include #include #include @@ -24,84 +25,20 @@ namespace hmpid bool o2::hmpid::Cluster::fgDoCorrSin = true; -/*Cluster::Cluster() -{ - mCh = -1; - mSi = -1; - mSt = kEmp; - mBox = -1; - mNlocMax = -1; - mMaxQpad = -1; - mMaxQ = -1; - mQRaw = 0; - mQ = 0; - mErrQ = -1; - mXX = 0; - mErrX = -1; - mYY = 0; - mErrY = -1; - mChi2 = -1; - mDigs.clear(); - - // delete [] &mDigs; - mParam = (o2::hmpid::Param::instanceNoGeo()); - // mParam = (o2::hmpid::Param()); -}; -*/ -/*Cluster::~Cluster() -{ - delete mDigs; -} -*/ -/*void Cluster::setClusterParams(float xL,float yL,int iCh) -{ - //------------------------------------------------------------------------ - //Set the cluster properties for the AliCluster3D part - //------------------------------------------------------------------------ - mParam = o2::hmpid::Param::instance(); - if(!mParam->getInstType()) { //if there is no geometry we cannot retrieve the volId (only for monitoring) - // new(this) AliCluster3D(); return; - } - //Get the volume ID from the previously set PNEntry - uint16_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kHMPID, iCh); - - //get L->T cs matrix for a given chamber - const TGeoHMatrix *t2l = AliGeomManager::GetTracking2LocalMatrix(volId); - mParam = o2::hmpid::Param::instance(); - //transformation from the pad cs to local - xL -= 0.5 * mParam->sizeAllX(); //size of all pads with dead zones included - yL -= 0.5 * mParam->sizeAllY(); - - // Get the position in the tracking cs - float posL[3]={xL, yL, 0.}; //this is the LORS of HMPID - float posT[3]; - t2l->MasterToLocal(posL,posT); - - //Get the cluster covariance matrix in the tracking cs - float covL[9] = { 0.8 * 0.8 / 12.0, 0.0, 0.0, //pad size X - 0.0, 0.84 * 0.84 / 12.0, 0.0, //pad size Y - 0.0, 0.0, 0.1 }; //just 1 , no Z dimension ??? - - TGeoHMatrix m; - m.SetRotation(covL); - m.Multiply(t2l); - const TGeoHMatrix& t2li = t2l->Inverse(); - m.MultiplyLeft(&t2li); - float *covT = m.GetRotationMatrix(); - - // ===> new(this) AliCluster3D(volId,posT[0],posT[1],posT[2],covT[0],covT[1],covT[2],covT[4],covT[5],covT[8],0x0); // No MC labels ? -} -*/ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Calculates naive cluster position as a center of gravity of its digits. void Cluster::coG() { + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } + int minPadX = 999; int minPadY = 999; int maxPadX = -1; int maxPadY = -1; // for box finding - if (mDigs.size() == 0) { + if (mDigs->size() == 0) { return; } // no digits in this cluster mXX = mYY = mQRaw = 0; // init summable parameters @@ -110,9 +47,9 @@ void Cluster::coG() int maxQ = -1; // to calculate the pad with the highest charge o2::hmpid::Digit* pDig = nullptr; - for (int iDig = 0; iDig < mDigs.size(); iDig++) { // digits loop + for (int iDig = 0; iDig < mDigs->size(); iDig++) { // digits loop int x, y, mod; - int padId = mDigs[iDig]->getPadID(); + int padId = (*mDigs)[iDig]->getPadID(); o2::hmpid::Digit::pad2Absolute(padId, &mod, &x, &y); if (x > maxPadX) { maxPadX = x; @@ -127,7 +64,7 @@ void Cluster::coG() minPadY = y; } // MinY - float q = mDigs[iDig]->mQ; // get QDC + float q = (*mDigs)[iDig]->mQ; // get QDC mXX += o2::hmpid::Digit::lorsX(padId) * q; mYY += o2::hmpid::Digit::lorsY(padId) * q; // add digit center weighted by QDC mQRaw += q; // increment total charge @@ -144,7 +81,7 @@ void Cluster::coG() mYY /= mQRaw; } // final center of gravity - if (mDigs.size() > 1 && fgDoCorrSin) { + if (mDigs->size() > 1 && fgDoCorrSin) { corrSin(); } // correct it by sinoid @@ -162,12 +99,14 @@ void Cluster::coG() // Correction of cluster x position due to sinoid, see HMPID TDR page 30 void Cluster::corrSin() { + const auto param = o2::hmpid::Param::instanceNoGeo(); int pc; int px; int py; o2::hmpid::Param::lors2Pad(mXX, mYY, pc, px, py); // tmp digit to get it center - double x = mXX - mParam->lorsX(pc, px); // diff between cluster x and center of the pad contaning this cluster - mXX += 3.31267e-2 * sin(2 * M_PI / 0.8 * x) - 2.66575e-3 * sin(4 * M_PI / 0.8 * x) + 2.80553e-3 * sin(6 * M_PI / 0.8 * x) + 0.0070; + double x = mXX - param->lorsX(pc, px); // diff between cluster x and center of the pad contaning this cluster + double xpi8on10 = M_PI / 0.8 * x; + mXX += 3.31267e-2 * sin(2.0 * xpi8on10) - 2.66575e-3 * sin(4.0 * xpi8on10) + 2.80553e-3 * sin(6.0 * xpi8on10) + 0.0070; return; } @@ -197,8 +136,11 @@ void Cluster::fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster double dQpadMath = 0; for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - double fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - dQpadMath += par[3 * j + 2] * fracMathi; // par[3*j+2] is charge par[3*j] is x par[3*j+1] is y of current Mathieson + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], pClu->dig(i)->getPadID()); + dQpadMath += par[baseOff2] * fracMathi; // par[3*j+2] is charge par[3*j] is x par[3*j+1] is y of current Mathieson } if (dQpadMath > 0 && pClu->dig(i)->mQ > 0) { chi2 += std::pow((pClu->dig(i)->mQ - dQpadMath), 2.0) / pClu->dig(i)->mQ; // chi2 function to be minimized @@ -206,45 +148,46 @@ void Cluster::fitFunc(int& iNpars, double* deriv, double& chi2, double* par, int } //---calculate gradients... if (iflag == 2) { - float** derivPart; - derivPart = new float*[iNpars]; - for (int j = 0; j < iNpars; j++) { - deriv[j] = 0; - derivPart[j] = new float[nPads]; - for (int i = 0; i < nPads; i++) { - derivPart[j][i] = 0; - } - } - for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + std::vector> derivPart(iNpars, std::vector(nPads, 0.0f)); + double dHalfPadX = o2::hmpid::Param::sizeHalfPadX(); + double dHalfPadY = o2::hmpid::Param::sizeHalfPadY(); + + for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + int iPadId = pClu->dig(i)->getPadID(); + double lx = o2::hmpid::Digit::lorsX(iPadId); + double ly = o2::hmpid::Digit::lorsY(iPadId); for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - double fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - double lx = o2::hmpid::Digit::lorsX(pClu->dig(i)->getPadID()); - double ly = o2::hmpid::Digit::lorsY(pClu->dig(i)->getPadID()); - derivPart[3 * j][i] += par[3 * j + 2] * (o2::hmpid::Digit::mathiesonX(par[3 * j] - lx - 0.5 * o2::hmpid::Param::sizePadX()) - o2::hmpid::Digit::mathiesonX(par[3 * j] - lx + 0.5 * o2::hmpid::Param::sizePadX())) * - o2::hmpid::Digit::intPartMathiY(par[3 * j + 1], pClu->dig(i)->getPadID()); - derivPart[3 * j + 1][i] += par[3 * j + 2] * (o2::hmpid::Digit::mathiesonY(par[3 * j + 1] - ly - 0.5 * o2::hmpid::Param::sizePadY()) - o2::hmpid::Digit::mathiesonY(par[3 * j + 1] - ly + 0.5 * o2::hmpid::Param::sizePadY())) * - o2::hmpid::Digit::intPartMathiX(par[3 * j], pClu->dig(i)->getPadID()); - derivPart[3 * j + 2][i] += fracMathi; + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], iPadId); + derivPart[baseOff][i] += par[baseOff2] * (o2::hmpid::Digit::mathiesonX(par[baseOff] - lx - dHalfPadX) - o2::hmpid::Digit::mathiesonX(par[baseOff] - lx + dHalfPadX)) * + o2::hmpid::Digit::intPartMathiY(par[baseOff1], iPadId); + derivPart[baseOff1][i] += par[baseOff2] * (o2::hmpid::Digit::mathiesonY(par[baseOff1] - ly - dHalfPadY) - o2::hmpid::Digit::mathiesonY(par[baseOff1] - ly + dHalfPadY)) * + o2::hmpid::Digit::intPartMathiX(par[baseOff], iPadId); + derivPart[baseOff2][i] += fracMathi; } } // loop on all pads of the cluster - for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster - double dQpadMath = 0; // pad charge collector + for (int i = 0; i < nPads; i++) { // loop on all pads of the cluster + int iPadId = pClu->dig(i)->getPadID(); + double dPadmQ = pClu->dig(i)->mQ; + double dQpadMath = 0.0; // pad charge collector + double twoOverMq = 2.0 / dPadmQ; for (int j = 0; j < iNshape; j++) { // Mathiesons loop as all of them may contribute to this pad - float fracMathi = o2::hmpid::Digit::intMathieson(par[3 * j], par[3 * j + 1], pClu->dig(i)->getPadID()); - dQpadMath += par[3 * j + 2] * fracMathi; - if (dQpadMath > 0 && pClu->dig(i)->mQ > 0) { - deriv[3 * j] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j][i]; - deriv[3 * j + 1] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j + 1][i]; - deriv[3 * j + 2] += 2 / pClu->dig(i)->mQ * (pClu->dig(i)->mQ - dQpadMath) * derivPart[3 * j + 2][i]; + int baseOff = 3 * j; + int baseOff1 = baseOff + 1; + int baseOff2 = baseOff + 2; + double fracMathi = o2::hmpid::Digit::intMathieson(par[baseOff], par[baseOff1], iPadId); + dQpadMath += par[baseOff2] * fracMathi; + if (dQpadMath > 0 && dPadmQ > 0) { + double appoggio = twoOverMq * (dPadmQ - dQpadMath); + deriv[baseOff] += appoggio * derivPart[baseOff][i]; + deriv[baseOff1] += appoggio * derivPart[baseOff1][i]; + deriv[baseOff2] += appoggio * derivPart[baseOff2][i]; } } } - // delete array... - for (int i = 0; i < iNpars; i++) { - delete[] derivPart[i]; - } - delete[] derivPart; } //---gradient calculations ended // fit ended. Final calculations } // FitFunction() @@ -301,10 +244,10 @@ void Cluster::print(Option_t* opt) const } printf("%sCLU: ch=%i (X = %7.3f, Y = %7.3f) Q=%8.3f Qraw=%8.3f(%3.0f%%) Size=%2i DimBox=%i LocMax=%i Chi2=%7.3f %s\n", opt, mCh, mXX, mYY, mQ, mQRaw, ratio, mSi, mBox, mNlocMax, mChi2, status); - if (mDigs.size() > 0) { + if (mDigs && mDigs->size() > 0) { std::cout << "Digits of Cluster" << std::endl; - for (int i; i < mDigs.size(); i++) { - std::cout << mDigs[i] << std::endl; + for (int i; i < mDigs->size(); i++) { + std::cout << (*mDigs)[i] << std::endl; } } return; @@ -321,6 +264,10 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // Arguments: pCluLst - cluster list pointer where to add new cluster(s) // isTryUnfold - flag to switch on/off unfolding // Returns: number of local maxima of original cluster + const auto param = o2::hmpid::Param::instanceNoGeo(); + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } const int kMaxLocMax = 6; // max allowed number of loc max for fitting coG(); // First calculate CoG for the given cluster int iCluCnt = pCluLst->size(); // get current number of clusters already stored in the list by previous operations @@ -336,13 +283,18 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // setClusterParams(mXX, mYY, mCh); // 2 - flag is set to FALSE // new ((*pCluLst)[iCluCnt++]) Cluster(*this); // 3 - size = 1 pCluLst->push_back(o2::hmpid::Cluster(*this)); + pCluLst->back().cleanPointers(); return 1; // add this raw cluster } // Phase 0. Initialise Fitter - double arglist[10]; + double arglist[10]{0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; float ierflg = 0.; TVirtualFitter* fitter = TVirtualFitter::Fitter((TObject*)this, 3 * 6); // initialize Fitter + if (fitter == nullptr) { + LOG(fatal) << "TVirtualFitter could not be created"; + return 1; + } arglist[0] = -1; ierflg = fitter->ExecuteCommand("SET PRI", arglist, 1); // no printout ierflg = fitter->ExecuteCommand("SET NOW", arglist, 0); // no warning messages @@ -352,13 +304,13 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // Phase 1. Find number of local maxima. Strategy is to check if the current pad has QDC more then all neigbours. Also find the box contaning the cluster mNlocMax = 0; for (int iDig1 = 0; iDig1 < rawSize; iDig1++) { // first digits loop - auto pDig1 = mDigs.at(iDig1); // take next digit + auto pDig1 = (*mDigs)[iDig1]; // take next digit int iCnt = 0; // counts how many neighbouring pads has QDC more then current one for (int iDig2 = 0; iDig2 < rawSize; iDig2++) { // loop on all digits again if (iDig1 == iDig2) { continue; } // the same digit, no need to compare - auto pDig2 = mDigs.at(iDig2); // take second digit to compare with the first one + auto pDig2 = (*mDigs)[iDig2]; // take second digit to compare with the first one int dist = TMath::Sign(int(pDig1->mX - pDig2->mX), 1) + TMath::Sign(int(pDig1->mY - pDig2->mY), 1); // distance between pads if (dist == 1) { // means dig2 is a neighbour of dig1 if (pDig2->mQ >= pDig1->mQ) { @@ -369,10 +321,10 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b if (iCnt == 0 && mNlocMax < kMaxLocMax) { // this pad has Q more then any neighbour so it's local maximum float xStart = o2::hmpid::Digit::lorsX(pDig1->getPadID()); float yStart = o2::hmpid::Digit::lorsY(pDig1->getPadID()); - float xMin = xStart - mParam->sizePadX(); - float xMax = xStart + mParam->sizePadX(); - float yMin = yStart - mParam->sizePadY(); - float yMax = yStart + mParam->sizePadY(); + float xMin = xStart - param->sizePadX(); + float xMax = xStart + param->sizePadX(); + float yMin = yStart - param->sizePadY(); + float yMax = yStart + param->sizePadY(); ierflg = fitter->SetParameter(3 * mNlocMax, Form("x%i", mNlocMax), xStart, 0.1, xMin, xMax); // X,Y,Q initial values of the loc max pad ierflg = fitter->SetParameter(3 * mNlocMax + 1, Form("y%i", mNlocMax), yStart, 0.1, yMin, yMax); // X, Y constrained to be near the loc max ierflg = fitter->SetParameter(3 * mNlocMax + 2, Form("q%i", mNlocMax), pDig1->mQ, 0.1, 0, 10000); // Q constrained to be positive @@ -387,6 +339,7 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b mSt = kNoLoc; // setClusterParams(mXX, mYY, mCh); //need to fill the AliCluster3D part pCluLst->push_back(o2::hmpid::Cluster(*this)); // add new unfolded cluster pCluLst->push_back(o2::hmpid::Cluster(*this)); + pCluLst->back().cleanPointers(); return mNlocMax; } @@ -395,10 +348,12 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b // setClusterParams(mXX, mYY, mCh); // if # of local maxima exceeds kMaxLocMax... mSt = kMax; pCluLst->push_back(o2::hmpid::Cluster(*this)); //...add this raw cluster - } else { // or resonable number of local maxima to fit and user requested it + pCluLst->back().cleanPointers(); + } else { // or resonable number of local maxima to fit and user requested it // Now ready for minimization step - arglist[0] = 500; // number of steps and sigma on pads charges - arglist[1] = 1.; // + arglist[0] = 500; // number of steps and sigma on pads charges + arglist[1] = 1.; // + /* ierflg = fitter->ExecuteCommand("SIMPLEX", arglist, 2); // start fitting with Simplex if (!ierflg) { fitter->ExecuteCommand("MIGRAD", arglist, 2); // fitting improved by Migrad @@ -413,6 +368,14 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b } } } + */ + + double strategy = 2.; + ierflg = fitter->ExecuteCommand("SET STR", &strategy, 1); // change level of strategy + if (!ierflg) { + fitter->ExecuteCommand("MIGRAD", arglist, 2); // fitting improved by Migrad + } + if (ierflg) { mSt = kAbn; // no convergence of the fit... } @@ -427,6 +390,7 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b fitter->GetParameter(3 * i + 1, sName, mYY, mErrY, dummy, dummy); // Y fitter->GetParameter(3 * i + 2, sName, mQ, mErrQ, dummy, dummy); // Q fitter->GetStats(mChi2, edm, errdef, nvpar, nparx); // get fit infos + // Printf("********************loc. max. = %i, X= %f, Y = %f, Q = %f**************************",i,mXX,mYY,mQ); if (mNlocMax > 1) { findClusterSize(i, pSigmaCut); // find clustersize for deconvoluted clusters // after this call, fSi temporarly is the calculated size. Later is set again @@ -447,7 +411,9 @@ int Cluster::solve(std::vector* pCluLst, float* pSigmaCut, b } } // setClusterParams(mXX, mYY, mCh); //need to fill the AliCluster3D part + // Printf("********************loc. max. = %i, X= %f, Y = %f, Q = %f**************************",i,mXX,mYY,mQ); pCluLst->push_back(o2::hmpid::Cluster(*this)); // add new unfolded cluster + pCluLst->back().cleanPointers(); if (mNlocMax > 1) { setSize(rawSize); // Original raw size is set again to its proper value } @@ -483,9 +449,13 @@ Bool_t Cluster::isInPc() // Check if (X,Y) position is inside the PC limits // Arguments: // Returns: True or False - int pc = mDigs[0]->getPh(); // (o2::hmpid::Digit*)&mDigs.at(iDig) + const auto param = o2::hmpid::Param::instanceNoGeo(); + if (!mDigs) { + LOGP(fatal, "digits are missing in the cluster"); + } + int pc = (*mDigs)[0]->getPh(); // (o2::hmpid::Digit*)&mDigs.at(iDig) - if (mXX < Param::minPcX(pc) || mXX > Param::maxPcX(pc) || mYY < Param::minPcY(pc) || mYY > Param::maxPcY(pc)) { + if (mXX < param->minPcX(pc) || mXX > param->maxPcX(pc) || mYY < param->minPcY(pc) || mYY > param->maxPcY(pc)) { return false; } @@ -497,12 +467,14 @@ void Cluster::digAdd(const Digit* pDig) // Adds a given digit to the list of digits belonging to this cluster, cluster is not owner of digits // Arguments: pDig - pointer to digit to be added // Returns: none - if (mDigs.size() == 0) { // create list of digits in the first invocation + if (!mDigs) { + LOGP(fatal, "digits are not set to the cluster"); + } + if (mDigs->size() == 0) { // create list of digits in the first invocation mSi = 0; - // std::vector fDigs; } // fDigs->Add(pDig); - mDigs.push_back(pDig); + mDigs->push_back(pDig); mSt = kFrm; mSi++; return; @@ -511,11 +483,7 @@ void Cluster::digAdd(const Digit* pDig) void Cluster::reset() { // - // - // - if (mDigs.size() > 0) { - mDigs.clear(); - } + cleanPointers(); // mDigs={0x0}; mSt = kEmp; mQRaw = mQ = 0; diff --git a/DataFormats/Detectors/HMPID/src/Digit.cxx b/DataFormats/Detectors/HMPID/src/Digit.cxx index 397d1a3cd5d0d..89726a5d28c95 100644 --- a/DataFormats/Detectors/HMPID/src/Digit.cxx +++ b/DataFormats/Detectors/HMPID/src/Digit.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -325,8 +325,8 @@ Double_t Digit::qdcTot(Double_t e, Double_t time, Int_t pc, Int_t px, Int_t py, /// @return : a charge fraction [0-1] imposed into the pad double Digit::intPartMathiX(double x, int pad) { - double shift1 = -lorsX(pad) + 0.5 * o2::hmpid::Param::sizePadX(); - double shift2 = -lorsX(pad) - 0.5 * o2::hmpid::Param::sizePadX(); + double shift1 = -lorsX(pad) + o2::hmpid::Param::sizeHalfPadX(); + double shift2 = -lorsX(pad) - o2::hmpid::Param::sizeHalfPadX(); double ux1 = o2::hmpid::Param::sqrtK3x() * tanh(o2::hmpid::Param::k2x() * (x + shift1) / o2::hmpid::Param::pitchAnodeCathode()); double ux2 = o2::hmpid::Param::sqrtK3x() * tanh(o2::hmpid::Param::k2x() * (x + shift2) / o2::hmpid::Param::pitchAnodeCathode()); @@ -343,8 +343,8 @@ double Digit::intPartMathiX(double x, int pad) /// @return : a charge fraction [0-1] imposed into the pad double Digit::intPartMathiY(double y, int pad) { - double shift1 = -lorsY(pad) + 0.5 * o2::hmpid::Param::sizePadY(); - double shift2 = -lorsY(pad) - 0.5 * o2::hmpid::Param::sizePadY(); + double shift1 = -lorsY(pad) + o2::hmpid::Param::sizeHalfPadY(); + double shift2 = -lorsY(pad) - o2::hmpid::Param::sizeHalfPadY(); double uy1 = o2::hmpid::Param::sqrtK3y() * tanh(o2::hmpid::Param::k2y() * (y + shift1) / o2::hmpid::Param::pitchAnodeCathode()); double uy2 = o2::hmpid::Param::sqrtK3y() * tanh(o2::hmpid::Param::k2y() * (y + shift2) / o2::hmpid::Param::pitchAnodeCathode()); diff --git a/DataFormats/Detectors/ITSMFT/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/CMakeLists.txt index 791339ac226d5..5d5e6da13067b 100644 --- a/DataFormats/Detectors/ITSMFT/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/CMakeLists.txt @@ -12,6 +12,3 @@ add_subdirectory(common) add_subdirectory(ITS) add_subdirectory(MFT) -if(ENABLE_UPGRADES) - add_subdirectory(ITS3) -endif() diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 7c2dd982564c6..06d4fba51bd54 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -35,7 +35,8 @@ namespace its class TrackITS : public o2::track::TrackParCov { enum UserBits { - kNextROF = 1 + kNextROF = 1 << 28, + kSharedClusters = 1 << 29 }; using Cluster = o2::itsmft::Cluster; @@ -45,22 +46,23 @@ class TrackITS : public o2::track::TrackParCov using o2::track::TrackParCov::TrackParCov; // inherit base constructors static constexpr int MaxClusters = 16; - GPUdDefault() TrackITS() = default; - GPUdDefault() TrackITS(const TrackITS& t) = default; - GPUd() TrackITS(const o2::track::TrackParCov& parcov) : o2::track::TrackParCov{parcov} {} - GPUd() TrackITS(const o2::track::TrackParCov& parCov, float chi2, const o2::track::TrackParCov& outer) + GPUhdDefault() TrackITS() = default; + GPUhdDefault() TrackITS(const TrackITS& t) = default; + GPUhd() TrackITS(const o2::track::TrackParCov& parcov) : o2::track::TrackParCov{parcov} {} + GPUhd() TrackITS(const o2::track::TrackParCov& parCov, float chi2, const o2::track::TrackParCov& outer) : o2::track::TrackParCov{parCov}, mParamOut{outer}, mChi2{chi2} {} - GPUdDefault() TrackITS& operator=(const TrackITS& tr) = default; - GPUdDefault() ~TrackITS() = default; + GPUhdDefault() TrackITS& operator=(const TrackITS& tr) = default; + GPUhdDefault() TrackITS& operator=(TrackITS&& tr) = default; + GPUhdDefault() ~TrackITS() = default; // These functions must be provided bool propagate(float alpha, float x, float bz); bool update(const Cluster& c, float chi2); // Other functions - float getChi2() const { return mChi2; } - int getNClusters() const { return mClusRef.getEntries(); } - int getNumberOfClusters() const { return getNClusters(); } + GPUhdi() float getChi2() const { return mChi2; } + GPUhdi() int getNClusters() const { return mClusRef.getEntries(); } + GPUhdi() int getNumberOfClusters() const { return getNClusters(); } int getFirstClusterEntry() const { return mClusRef.getFirstEntry(); } int getClusterEntry(int i) const { return getFirstClusterEntry() + i; } void shiftFirstClusterEntry(int bias) @@ -85,22 +87,23 @@ class TrackITS : public o2::track::TrackParCov } const ClusRefs& getClusterRefs() const { return mClusRef; } - ClusRefs& getClusterRefs() { return mClusRef; } + GPUhdi() ClusRefs& getClusterRefs() { return mClusRef; } - void setChi2(float chi2) { mChi2 = chi2; } + GPUhdi() void setChi2(float chi2) { mChi2 = chi2; } bool isBetter(const TrackITS& best, float maxChi2) const; - o2::track::TrackParCov& getParamIn() { return *this; } - const o2::track::TrackParCov& getParamIn() const { return *this; } + GPUhdi() o2::track::TrackParCov& getParamIn() { return *this; } + GPUhdi() const o2::track::TrackParCov& getParamIn() const { return *this; } - o2::track::TrackParCov& getParamOut() { return mParamOut; } - const o2::track::TrackParCov& getParamOut() const { return mParamOut; } + GPUhdi() o2::track::TrackParCov& getParamOut() { return mParamOut; } + GPUhdi() const o2::track::TrackParCov& getParamOut() const { return mParamOut; } - void setPattern(uint32_t p) { mPattern = p; } - uint32_t getPattern() const { return mPattern; } - bool hasHitOnLayer(int i) { return mPattern & (0x1 << i); } - bool isFakeOnLayer(int i) { return !(mPattern & (0x1 << (16 + i))); } + GPUhdi() void setPattern(uint32_t p) { mPattern = p; } + GPUhdi() uint32_t getPattern() const { return mPattern; } + bool hasHitOnLayer(uint32_t i) const { return mPattern & (0x1 << i); } + bool isFakeOnLayer(uint32_t i) const { return !(mPattern & (0x1 << (16 + i))); } + bool isExtendedOnLayer(uint32_t i) const { return (mPattern & (0x1 << (24 + i))); } // only correct if getNClusters <= 8 on layers <= 8 uint32_t getLastClusterLayer() const { uint32_t r{0}, v{mPattern & ((1 << 16) - 1)}; @@ -117,18 +120,46 @@ class TrackITS : public o2::track::TrackParCov } return s; } - int getNFakeClusters(); + int getNFakeClusters() const; - void setNextROFbit(bool toggle = true) { setUserField((getUserField() & ~kNextROF) | (-toggle & kNextROF)); } - bool hasHitInNextROF() const { return getUserField() & kNextROF; } + void setNextROFbit(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kNextROF) : (mClusterSizes & ~kNextROF); } + bool hasHitInNextROF() const { return mClusterSizes & kNextROF; } + void setSharedClusters(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); } + bool hasSharedClusters() const { return mClusterSizes & kSharedClusters; } + + void setClusterSize(int l, int size) + { + if (l >= 8) { + return; + } + if (size > 15) { + size = 15; + } + mClusterSizes &= ~(0xf << (l * 4)); + mClusterSizes |= (size << (l * 4)); + } + + int getClusterSize(int l) + { + if (l >= 7) { + return 0; + } + return (mClusterSizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return mClusterSizes; + } private: o2::track::TrackParCov mParamOut; ///< parameter at largest radius ClusRefs mClusRef; ///< references on clusters float mChi2 = 0.; ///< Chi2 for this track uint32_t mPattern = 0; ///< layers pattern + unsigned int mClusterSizes = 0u; - ClassDefNV(TrackITS, 5); + ClassDefNV(TrackITS, 6); }; class TrackITSExt : public TrackITS @@ -138,21 +169,21 @@ class TrackITSExt : public TrackITS static constexpr int MaxClusters = 16; /// Prepare for overlaps and new detector configurations using TrackITS::TrackITS; // inherit base constructors - GPUd() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, + GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, o2::track::TrackParCov&& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUd() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, + GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, o2::track::TrackParCov& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUdDefault() TrackITSExt(const TrackITSExt& t) = default; + GPUhdDefault() TrackITSExt(const TrackITSExt& t) = default; void setClusterIndex(int l, int i) { @@ -161,9 +192,9 @@ class TrackITSExt : public TrackITS getClusterRefs().setEntries(ncl); } - int getClusterIndex(int lr) const { return mIndex[lr]; } + GPUhdi() const int& getClusterIndex(int lr) const { return mIndex[lr]; } - void setExternalClusterIndex(int layer, int idx, bool newCluster = false) + GPUhdi() void setExternalClusterIndex(int layer, int idx, bool newCluster = false) { if (newCluster) { getClusterRefs().setEntries(getNumberOfClusters() + 1); @@ -174,7 +205,7 @@ class TrackITSExt : public TrackITS mIndex[layer] = idx; } - std::array& getClusterIndexes() + GPUh() std::array& getClusterIndexes() { return mIndex; } diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx index 4f0a0a5a2ae7f..0244756647120 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx @@ -29,31 +29,31 @@ bool TrackITS::operator<(const TrackITS& o) const //----------------------------------------------------------------- // This function compares tracks according to the their curvature //----------------------------------------------------------------- - Float_t co = TMath::Abs(o.getPt()); - Float_t c = TMath::Abs(getPt()); - // Float_t co=o.GetSigmaY2()*o.GetSigmaZ2(); - // Float_t c =GetSigmaY2()*GetSigmaZ2(); + float co = TMath::Abs(o.getPt()); + float c = TMath::Abs(getPt()); + // float co=o.GetSigmaY2()*o.GetSigmaZ2(); + // float c =GetSigmaY2()*GetSigmaZ2(); return (c > co); } -void TrackITS::getImpactParams(Float_t x, Float_t y, Float_t z, Float_t bz, Float_t ip[2]) const +void TrackITS::getImpactParams(float x, float y, float z, float bz, float ip[2]) const { //------------------------------------------------------------------ // This function calculates the transverse and longitudinal impact parameters // with respect to a point with global coordinates (x,y,0) // in the magnetic field "bz" (kG) //------------------------------------------------------------------ - Float_t f1 = getSnp(), r1 = TMath::Sqrt((1. - f1) * (1. + f1)); - Float_t xt = getX(), yt = getY(); - Float_t sn = TMath::Sin(getAlpha()), cs = TMath::Cos(getAlpha()); - Float_t a = x * cs + y * sn; + float f1 = getSnp(), r1 = TMath::Sqrt((1. - f1) * (1. + f1)); + float xt = getX(), yt = getY(); + float sn = TMath::Sin(getAlpha()), cs = TMath::Cos(getAlpha()); + float a = x * cs + y * sn; y = -x * sn + y * cs; x = a; xt -= x; yt -= y; - Float_t rp4 = getCurvature(bz); + float rp4 = getCurvature(bz); if ((TMath::Abs(bz) < Almost0) || (TMath::Abs(rp4) < Almost0)) { ip[0] = -(xt * f1 - yt * r1); ip[1] = getZ() + (ip[0] * f1 - xt) / r1 * getTgl() - z; @@ -63,59 +63,59 @@ void TrackITS::getImpactParams(Float_t x, Float_t y, Float_t z, Float_t bz, Floa sn = rp4 * xt - f1; cs = rp4 * yt + r1; a = 2 * (xt * f1 - yt * r1) - rp4 * (xt * xt + yt * yt); - Float_t rr = TMath::Sqrt(sn * sn + cs * cs); + float rr = TMath::Sqrt(sn * sn + cs * cs); ip[0] = -a / (1 + rr); - Float_t f2 = -sn / rr, r2 = TMath::Sqrt((1. - f2) * (1. + f2)); + float f2 = -sn / rr, r2 = TMath::Sqrt((1. - f2) * (1. + f2)); ip[1] = getZ() + getTgl() / rp4 * TMath::ASin(f2 * r1 - f1 * r2) - z; } -Bool_t TrackITS::propagate(Float_t alpha, Float_t x, Float_t bz) +bool TrackITS::propagate(float alpha, float x, float bz) { if (rotate(alpha)) { if (propagateTo(x, bz)) { - return kTRUE; + return true; } } - return kFALSE; + return false; } -Bool_t TrackITS::update(const Cluster& c, Float_t chi2) +bool TrackITS::update(const Cluster& c, float chi2) { //-------------------------------------------------------------------- // Update track params //-------------------------------------------------------------------- if (!o2::track::TrackParCov::update(static_cast&>(c))) { - return kFALSE; + return false; } mChi2 += chi2; - return kTRUE; + return true; } -Bool_t TrackITS::isBetter(const TrackITS& best, Float_t maxChi2) const +bool TrackITS::isBetter(const TrackITS& best, float maxChi2) const { - Int_t ncl = getNumberOfClusters(); - Int_t nclb = best.getNumberOfClusters(); + int ncl = getNumberOfClusters(); + int nclb = best.getNumberOfClusters(); if (ncl >= nclb) { - Float_t chi2 = getChi2(); + float chi2 = getChi2(); if (chi2 < maxChi2) { if (ncl > nclb || chi2 < best.getChi2()) { - return kTRUE; + return true; } } } - return kFALSE; + return false; } -int TrackITS::getNFakeClusters() +int TrackITS::getNFakeClusters() const { int nFake{0}; - int firstClus = getFirstClusterLayer(); - int lastClus = firstClus + getNClusters(); - for (int iCl{firstClus}; iCl < lastClus; ++iCl) { + auto firstClus = getFirstClusterLayer(); + auto lastClus = firstClus + getNClusters(); + for (auto iCl{firstClus}; iCl < lastClus; ++iCl) { if (hasHitOnLayer(iCl) && isFakeOnLayer(iCl)) { ++nFake; } } return nFake; -} \ No newline at end of file +} diff --git a/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt deleted file mode 100644 index 944ca48df1191..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -o2_add_library(DataFormatsITS3 - SOURCES src/CompCluster.cxx) - -o2_target_root_dictionary(DataFormatsITS3 - HEADERS include/DataFormatsITS3/CompCluster.h - LINKDEF src/ITS3DataFormatsLinkDef.h) diff --git a/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h b/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h deleted file mode 100644 index 03da492fd56c4..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/include/DataFormatsITS3/CompCluster.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CompCluster.h -/// \brief Definition of the ITS3 compact cluster -#ifndef ALICEO2_ITS3_COMPCLUSTER_H -#define ALICEO2_ITS3_COMPCLUSTER_H - -#include - -namespace o2 -{ -namespace its3 -{ - -/// This is a version of the ALPIDE cluster represented by the pattern ID and the address of the -/// top-left (min row,col) pixel of the topololy bounding box - -class CompCluster -{ - public: - static constexpr int NBitsPattID = 11; // number of bits for the pattern ID - private: - static constexpr UShort_t PattIDMask = (0x1 << NBitsPattID) - 1; - static constexpr UShort_t FlagBit = 0x1 << (NBitsPattID); - // - ///< compactified data: bits [0:8] - row, [9-18] - col, [19-30] - pattern ID, bit 31 - special flag - UShort_t mRow; - UShort_t mCol; - UShort_t mPat; - - public: - static constexpr unsigned short InvalidPatternID = (0x1 << NBitsPattID) - 1; // All 11 bits of pattern ID are 1 - CompCluster(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0) - { - set(row, col, patt); - } - - void set(UShort_t row, UShort_t col, UShort_t patt) - { - setPatternID(patt); - mRow = row; - mCol = col; - } - - UShort_t getRow() const { return mRow; } - UShort_t getCol() const { return mCol; } - UShort_t getPatternID() const { return (mPat & PattIDMask); } - bool getFlag() const { return (mPat & FlagBit) == FlagBit; } - - void setRow(UShort_t r) - { - mRow = r; - } - void setCol(UShort_t c) - { - mCol = c; - }; - void setPatternID(UShort_t p) - { - mPat &= ~(PattIDMask); - mPat |= p; - } - void setFlag(bool v) - { - mPat &= ~FlagBit; - if (v) { - mPat |= FlagBit; - } - } - - void print() const; - - ClassDefNV(CompCluster, 1); -}; - -/// Extension of the compact cluster, augmented by the chipID -/// This is a TEMPORARY class, until we converge to more economical container -class CompClusterExt : public CompCluster -{ - private: - UShort_t mChipID; ///< chip id - - public: - CompClusterExt(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0, UShort_t chipID = 0) : CompCluster(row, col, patt), mChipID(chipID) - { - } - - void set(UShort_t row, UShort_t col, UShort_t patt, UShort_t chipID) - { - CompCluster::set(row, col, patt); - mChipID = chipID; - } - - UShort_t getChipID() const { return mChipID; } - UShort_t getSensorID() const { return mChipID; } // to have the same signature as BaseCluster - - void setChipID(UShort_t c) { mChipID = c; } - - void print() const; - - ClassDefNV(CompClusterExt, 1); -}; - -} // namespace its3 -} // namespace o2 - -std::ostream& operator<<(std::ostream& stream, const o2::its3::CompCluster& cl); -std::ostream& operator<<(std::ostream& stream, const o2::its3::CompClusterExt& cl); - -#endif /* ALICEO2_ITS3_COMPACTCLUSTER_H */ diff --git a/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx b/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx deleted file mode 100644 index 046a27be92919..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/src/CompCluster.cxx +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file Cluster.cxx -/// \brief Implementation of the ITSMFT cluster - -#include "DataFormatsITS3/CompCluster.h" -#include -#include - -using namespace o2::its3; - -std::ostream& operator<<(std::ostream& stream, const CompCluster& cl) -{ - stream << " row: " << cl.getRow() << " col: " << cl.getCol() - << " pattID " << cl.getPatternID() << " [flag: " << cl.getFlag() << "] "; - return stream; -} - -std::ostream& operator<<(std::ostream& stream, const CompClusterExt& cl) -{ - stream << " chip: " << cl.getChipID() << ((const CompCluster&)cl); - return stream; -} - -//______________________________________________________________________________ -void CompCluster::print() const -{ - // print itself - std::cout << *this << "\n"; -} - -//______________________________________________________________________________ -void CompClusterExt::print() const -{ - // print itself - std::cout << *this << "\n"; -} diff --git a/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h deleted file mode 100644 index c139171540f18..0000000000000 --- a/DataFormats/Detectors/ITSMFT/ITS3/src/ITS3DataFormatsLinkDef.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::its3::CompCluster + ; -#pragma link C++ class o2::its3::CompClusterExt + ; -#pragma link C++ class std::vector < o2::its3::CompCluster> + ; -#pragma link C++ class std::vector < o2::its3::CompClusterExt> + ; - -#endif diff --git a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h index 04ac4fc54263c..04cca15264047 100644 --- a/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h +++ b/DataFormats/Detectors/ITSMFT/MFT/include/DataFormatsMFT/TrackMFT.h @@ -70,6 +70,24 @@ class TrackMFT : public o2::track::TrackParCovFwd const o2::track::TrackParCovFwd& getOutParam() const { return mOutParameters; } ///< Returns track parameters fitted outwards void setOutParam(const o2::track::TrackParCovFwd parcov) { mOutParameters = parcov; } ///< Set track out parameters + void setClusterSize(int l, int size) + { + if (l >= 10) { + return; + } + if (size > 63) { + size = 63; + } + + mClusterSizes &= ~(0x3fULL << (l * 6)); + mClusterSizes |= (static_cast(size) << (l * 6)); + } + + uint64_t getClusterSizes() const + { + return mClusterSizes; + } + private: Bool_t mIsCA = false; ///< Track finding method CA vs. LTF @@ -79,8 +97,9 @@ class TrackMFT : public o2::track::TrackParCovFwd Double_t mSeedinvQPtFitChi2 = 0.; ///< Seed InvQPt Chi2 from FCF clusters X,Y positions Double_t mInvQPtSeed; ///< Seed InvQPt from FCF clusters X,Y positions + uint64_t mClusterSizes = 0; ///< MFT cluster sizes per track - ClassDefNV(TrackMFT, 2); + ClassDefNV(TrackMFT, 3); }; class TrackMFTExt : public TrackMFT @@ -94,14 +113,28 @@ class TrackMFTExt : public TrackMFT using TrackMFT::TrackMFT; // inherit base constructors int getExternalClusterIndex(int i) const { return mExtClsIndex[i]; } + int getExternalClusterSize(int i) const { return mExtClsSize[i]; } + int getExternalClusterLayer(int i) const { return mExtClsLayer[i]; } void setExternalClusterIndex(int np, int idx) { mExtClsIndex[np] = idx; } + void setExternalClusterSize(int np, int clsSize) + { + mExtClsSize[np] = clsSize; + } + + void setExternalClusterLayer(int np, int clsLayer) + { + mExtClsLayer[np] = clsLayer; + } + protected: std::array mExtClsIndex = {-1}; ///< External indices of associated clusters + std::array mExtClsSize = {-1}; ///< Cluster size + std::array mExtClsLayer = {-1}; ///< Cluster layer ClassDefNV(TrackMFTExt, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 2adaa9bcf24f2..96d376526a1a4 100644 --- a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(DataFormatsITSMFT src/ClusterPattern.cxx src/ClusterTopology.cxx src/TopologyDictionary.cxx + src/TimeDeadMap.cxx src/CTF.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTBase O2::ReconstructionDataFormats @@ -25,9 +26,10 @@ o2_add_library(DataFormatsITSMFT o2_target_root_dictionary(DataFormatsITSMFT HEADERS include/DataFormatsITSMFT/ROFRecord.h - include/DataFormatsITSMFT/Digit.h - include/DataFormatsITSMFT/GBTCalibData.h - include/DataFormatsITSMFT/NoiseMap.h + include/DataFormatsITSMFT/Digit.h + include/DataFormatsITSMFT/GBTCalibData.h + include/DataFormatsITSMFT/NoiseMap.h + include/DataFormatsITSMFT/TimeDeadMap.h include/DataFormatsITSMFT/Cluster.h include/DataFormatsITSMFT/CompCluster.h include/DataFormatsITSMFT/ClusterPattern.h diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h index b465aa5b3d66b..314523aa878ba 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h @@ -46,12 +46,12 @@ struct CompressedClusters { // ROF header data std::vector firstChipROF; /// 1st chip ID in the ROF - std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF - std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF + std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF + std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF std::vector nclusROF; /// number of clusters in ROF // Chip data - std::vector chipInc; /// increment of chipID wrt that of prev. chip + std::vector chipInc; /// increment of chipID wrt that of prev. chip std::vector chipMul; /// clusters in chip std::vector row; /// row of fired pixel std::vector colInc; /// increment of pixel column wrt that of prev. pixel (sometimes can be slightly negative) diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h index 772a9ae12a81a..361544798dc80 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h @@ -15,6 +15,7 @@ #define ALICEO2_ITSMFT_COMPCLUSTER_H #include +#include namespace o2 { @@ -81,7 +82,13 @@ class CompCluster } } + bool operator==(const CompCluster& cl) const + { + return mData == cl.mData; + } + void print() const; + std::string asString() const; ClassDefNV(CompCluster, 2); }; @@ -91,7 +98,7 @@ class CompCluster class CompClusterExt : public CompCluster { private: - UShort_t mChipID; ///< chip id + UShort_t mChipID; ///< chip id public: CompClusterExt(UShort_t row = 0, UShort_t col = 0, UShort_t patt = 0, UShort_t chipID = 0) : CompCluster(row, col, patt), mChipID(chipID) @@ -110,6 +117,7 @@ class CompClusterExt : public CompCluster void setChipID(UShort_t c) { mChipID = c; } void print() const; + std::string asString() const; ClassDefNV(CompClusterExt, 1); }; diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h index 4815b688ee7c1..25b7f451b6452 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h @@ -169,7 +169,7 @@ class NoiseMap // Methods required by the calibration framework void print(); void fill(const gsl::span data); - void merge(const NoiseMap* prev) {} + const std::map* getChipMap(int chip) const { return chip < (int)mNoisyPixels.size() ? &mNoisyPixels[chip] : nullptr; } std::map& getChip(int chip) { return mNoisyPixels[chip]; } @@ -202,6 +202,20 @@ class NoiseMap return std::ceil((1. + 1. / t) / (relErr * relErr)); } + NoiseMap merge(const NoiseMap* prev) + { + int incre = 0; + for (size_t i = 0; i < mNoisyPixels.size(); ++i) { + for (const auto& prev_np : prev->mNoisyPixels[i]) { // only enters this for loop if the "i" chip exists. + if (mNoisyPixels[i].find(prev_np.first) == mNoisyPixels[i].end()) { + mNoisyPixels[i][prev_np.first] = prev_np.second; + incre++; + } + } // end of for loop on elements of previous noise map + } // end of for loop on i (chip ID) + return (mNoisyPixels); + } // end of void merge + size_t size() const { return mNoisyPixels.size(); } void setNumOfStrobes(long n) { mNumOfStrobes = n; } void addStrobes(long n) { mNumOfStrobes += n; } diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h index 1f7ac73d0a131..9a6cd0891b6be 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/ROFRecord.h @@ -31,6 +31,8 @@ class ROFRecord { public: + enum { VtxStdMode = 0, + VtxUPCMode = 1 }; using EvIdx = o2::dataformats::RangeReference; using BCData = o2::InteractionRecord; using ROFtype = unsigned int; @@ -45,6 +47,14 @@ class ROFRecord void setFirstEntry(int idx) { mROFEntry.setFirstEntry(idx); } void setNEntries(int n) { mROFEntry.setEntries(n); } + uint32_t getFlags() const { return mBits; } + void setFlags(uint32_t flags) { mBits = flags; } + void setFlag(uint8_t flagIndex) { mBits |= (1 << flagIndex); } + void resetFlag(uint8_t flagIndex) { mBits &= ~(1 << flagIndex); } + bool getFlag(uint8_t flagIndex) const { return mBits & (1 << flagIndex); } + void clearAllFlags() { mBits = 0; } + void setAllFlags() { mBits = ~0; } + const BCData& getBCData() const { return mBCData; } BCData& getBCData() { return mBCData; } EvIdx getEntry() const { return mROFEntry; } @@ -58,7 +68,6 @@ class ROFRecord mROFEntry.clear(); mBCData.clear(); } - void print() const; template gsl::span getROFData(const gsl::span tfdata) const @@ -84,12 +93,16 @@ class ROFRecord return i < getNEntries() ? &tfdata[getFirstEntry() + i] : nullptr; } + std::string asString() const; + void print() const; + friend std::ostream& operator<<(std::ostream& output, const ROFRecord& rec); + private: o2::InteractionRecord mBCData; // BC data for given trigger EvIdx mROFEntry; //< reference on the 1st object of the ROF in data ROFtype mROFrame = 0; //< frame ID - - ClassDefNV(ROFRecord, 2); + uint32_t mBits = 0; + ClassDefNV(ROFRecord, 3); }; /// this is a simple reference connecting (composed) MC event ID (from the EventRecord of the RunContext) @@ -105,7 +118,10 @@ struct MC2ROFRecord { MC2ROFRecord() = default; MC2ROFRecord(int evID, int rofRecID, ROFtype mnrof, ROFtype mxrof) : eventRecordID(evID), rofRecordID(rofRecID), minROF(mnrof), maxROF(mxrof) {} int getNROFs() const { return (rofRecordID < 0 || minROF > maxROF) ? 0 : (maxROF - minROF); } + std::string asString() const; void print() const; + friend std::ostream& operator<<(std::ostream& output, const MC2ROFRecord& rec); + ClassDefNV(MC2ROFRecord, 1); }; } // namespace itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h new file mode 100644 index 0000000000000..6c7c01dc888b7 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TimeDeadMap.h +/// \brief Definition of the ITSMFT time-dependend dead map +#ifndef ALICEO2_ITSMFT_TIMEDEADMAP_H +#define ALICEO2_ITSMFT_TIMEDEADMAP_H + +#include + +#include +#include +#include +#include + +namespace o2 +{ + +namespace itsmft +{ + +class NoiseMap; + +class TimeDeadMap +{ + public: + // Constructor + TimeDeadMap(std::map>& deadmap) + { + mEvolvingDeadMap.swap(deadmap); + } + + /// Constructor + TimeDeadMap() = default; + /// Destructor + ~TimeDeadMap() = default; + + void fillMap(unsigned long firstOrbit, const std::vector& deadVect) + { + mEvolvingDeadMap[firstOrbit] = deadVect; + }; + + void fillMap(const std::vector& deadVect) + { + mStaticDeadMap = deadVect; + } + + void clear() + { + mEvolvingDeadMap.clear(); + mStaticDeadMap.clear(); + } + + void decodeMap(NoiseMap& noisemap) const; + void decodeMap(unsigned long orbit, o2::itsmft::NoiseMap& noisemap, bool includeStaticMap = true, long orbitGapAllowed = 330000) const; + std::string getMapVersion() const { return mMAP_VERSION; }; + + unsigned long getEvolvingMapSize() const { return mEvolvingDeadMap.size(); }; + std::vector getEvolvingMapKeys() const; + void getStaticMap(std::vector& mmap) const { mmap = mStaticDeadMap; }; + long getMapAtOrbit(unsigned long orbit, std::vector& mmap) const; + void setMapVersion(std::string version) { mMAP_VERSION = version; }; + + bool isDefault() const { return mIsDefaultObject; }; + void setAsDefault(bool isdef = true) { mIsDefaultObject = isdef; }; + + private: + bool mIsDefaultObject = false; + std::string mMAP_VERSION = "3"; + std::map> mEvolvingDeadMap; ///< Internal dead chip map representation. key = orbit + std::vector mStaticDeadMap; ///< To store map valid for every orbit. Filled starting from version = 4. + + ClassDefNV(TimeDeadMap, 2); +}; + +} // namespace itsmft +} // namespace o2 + +#endif /* ALICEO2_ITSMFT_TIMEDEADMAP_H */ diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h index 00ae135d8610f..132601030933e 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TopologyDictionary.h @@ -54,8 +54,8 @@ struct GroupStruct { float mErrZ; ///< Error associated to the hit point in the z direction. float mErr2X; ///< Squared Error associated to the hit point in the x direction. float mErr2Z; ///< Squared Error associated to the hit point in the z direction. - float mXCOG; ///< x position of te COG wrt the boottom left corner of the bounding box - float mZCOG; ///< z position of te COG wrt the boottom left corner of the bounding box + float mXCOG; ///< x position of the COG wrt the bottom left corner of the bounding box + float mZCOG; ///< z position of the COG wrt the bottom left corner of the bounding box int mNpixels; ///< Number of fired pixels ClusterPattern mPattern; ///< Bitmask of pixels. For groups the biggest bounding box for the group is taken, with all /// the bits set to 1. diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h index 164fd850fd539..27b059cc61ecf 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h @@ -25,12 +25,33 @@ namespace itsmft // can refer to max 15 indices in the vector of total length <268435456, i.e. 17895697 tracks in worst case struct TrkClusRef : public o2::dataformats::RangeRefComp<4> { using o2::dataformats::RangeRefComp<4>::RangeRefComp; + uint32_t clsizes = 0; ///< cluster sizes for each layer uint16_t pattern = 0; ///< layers pattern GPUd() int getNClusters() const { return getEntries(); } - bool hasHitOnLayer(int i) { return pattern & (0x1 << i); } - - ClassDefNV(TrkClusRef, 1); + bool hasHitOnLayer(int i) const { return pattern & (0x1 << i); } + + void setClusterSize(int l, int size) + { + if (l >= 8) + return; + if (size > 15) + size = 15; + clsizes &= ~(0xf << (l * 4)); + clsizes |= (size << (l * 4)); + } + + int getClusterSize(int l) const + { + return (l >= 8) ? 0 : (clsizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return clsizes; + } + + ClassDefNV(TrkClusRef, 2); }; } // namespace itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx b/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx index 95ecd73f6e9d5..1f4be3163b3d2 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/CompCluster.cxx @@ -15,22 +15,32 @@ #include "DataFormatsITSMFT/CompCluster.h" #include #include +#include using namespace o2::itsmft; std::ostream& operator<<(std::ostream& stream, const CompCluster& cl) { - stream << " row: " << cl.getRow() << " col: " << cl.getCol() - << " pattID " << cl.getPatternID() << " [flag: " << cl.getFlag() << "] "; + stream << cl.asString(); return stream; } std::ostream& operator<<(std::ostream& stream, const CompClusterExt& cl) { - stream << " chip: " << cl.getChipID() << ((const CompCluster&)cl); + stream << cl.asString(); return stream; } +std::string CompCluster::asString() const +{ + return std::format(" row: {:4d} col: {:4d} pattID: {:4d} [flag: {:1d}]", getRow(), getCol(), getPatternID(), getFlag()); +} + +std::string CompClusterExt::asString() const +{ + return std::format(" chip: {:5d} row: {:4d} col: {:4d} pattID: {:4d} [flag: {:1d}]", getChipID(), getRow(), getCol(), getPatternID(), getFlag()); +} + //______________________________________________________________________________ void CompCluster::print() const { diff --git a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h index 5edd964ce2cd0..fc67fdf028436 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h @@ -17,6 +17,7 @@ #pragma link C++ class o2::itsmft::Digit + ; #pragma link C++ class o2::itsmft::NoiseMap + ; +#pragma link C++ class o2::itsmft::TimeDeadMap + ; #pragma link C++ class std::vector < o2::itsmft::Digit> + ; #pragma link C++ class o2::itsmft::GBTCalibData + ; @@ -49,6 +50,8 @@ #pragma link C++ class o2::itsmft::CTF + ; #pragma link C++ class o2::ctf::EncodedBlocks < o2::itsmft::CTFHeader, 10, uint32_t> + ; +#pragma link C++ class std::map < unsigned long, std::vector < uint16_t>> + ; + #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; #pragma link C++ function o2::itsmft::getROFData(const gsl::span tfdata) const; diff --git a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx index fcc86ac7e7dca..8dbde0d580efc 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx @@ -9,20 +9,42 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "DataFormatsITSMFT/ROFRecord.h" #include +#include + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "Framework/Logger.h" using namespace o2::itsmft; +std::string ROFRecord::asString() const +{ + return std::format("ROF: {} | {} entries starting from {} | IR: {}", mROFrame, getNEntries(), getFirstEntry(), mBCData.asString()); +} + void ROFRecord::print() const { - std::cout << "ROF: " << mROFrame << " | " << getNEntries() << " entries starting from " - << getFirstEntry() << std::endl; - mBCData.print(); + LOG(info) << asString(); +} + +std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) +{ + stream << rec.asString(); + return stream; +} + +std::string MC2ROFRecord::asString() const +{ + return std::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); } void MC2ROFRecord::print() const { - std::cout << "MCEventID: " << eventRecordID << " ROFs: " << minROF << '-' << maxROF - << " Entry in ROFRecords: " << rofRecordID << std::endl; + LOG(info) << asString(); +} + +std::ostream& operator<<(std::ostream& stream, MC2ROFRecord const& rec) +{ + stream << rec.asString(); + return stream; } diff --git a/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx b/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx new file mode 100644 index 0000000000000..e3df8e7f91f86 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/TimeDeadMap.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TimeDeadMap.cxx +/// \brief Implementation of the time-dependent map + +#include "DataFormatsITSMFT/TimeDeadMap.h" +#include "DataFormatsITSMFT/NoiseMap.h" +#include "Framework/Logger.h" + +using namespace o2::itsmft; + +void TimeDeadMap::decodeMap(o2::itsmft::NoiseMap& noisemap) const +{ // for static part only + if (mMAP_VERSION == "3") { + LOG(error) << "Trying to decode static part of deadmap version " << mMAP_VERSION << ". Not implemented, doing nothing."; + return; + } + for (int iel = 0; iel < mStaticDeadMap.size(); iel++) { + uint16_t w = mStaticDeadMap[iel]; + noisemap.maskFullChip(w & 0x7FFF); + if (w & 0x8000) { + for (int w2 = (w & 0x7FFF) + 1; w2 < mStaticDeadMap.at(iel + 1); w2++) { + noisemap.maskFullChip(w2); + } + } + } +} + +void TimeDeadMap::decodeMap(unsigned long orbit, o2::itsmft::NoiseMap& noisemap, bool includeStaticMap, long orbitGapAllowed) const +{ // for time-dependent and (optionally) static part. Use orbitGapAllowed = -1 to ignore check on orbit difference + + if (mMAP_VERSION != "3" && mMAP_VERSION != "4") { + LOG(error) << "Trying to decode time-dependent deadmap version " << mMAP_VERSION << ". Not implemented, doing nothing."; + return; + } + + if (mEvolvingDeadMap.empty()) { + LOG(warning) << "Time-dependent dead map is empty. Doing nothing."; + return; + } + + std::vector closestVec; + long dT = getMapAtOrbit(orbit, closestVec); + + if (orbitGapAllowed >= 0 && std::abs(dT) > orbitGapAllowed) { + LOG(warning) << "Requested orbit " << orbit << ", found " << orbit - dT << ". Orbit gap is too high, skipping time-dependent map."; + closestVec.clear(); + } + + // add static part if requested. something may be masked twice + if (includeStaticMap && mMAP_VERSION != "3") { + closestVec.insert(closestVec.end(), mStaticDeadMap.begin(), mStaticDeadMap.end()); + } + + // vector encoding: if 1<<15 = 0x8000 is set, the word encodes the first element of a range, with mask (1<<15)-1 = 0x7FFF. The last element of the range is the next in the vector. + + for (int iel = 0; iel < closestVec.size(); iel++) { + uint16_t w = closestVec.at(iel); + noisemap.maskFullChip(w & 0x7FFF); + if (w & 0x8000) { + for (int w2 = (w & 0x7FFF) + 1; w2 < closestVec.at(iel + 1); w2++) { + noisemap.maskFullChip(w2); + } + } + } +} + +std::vector TimeDeadMap::getEvolvingMapKeys() const +{ + std::vector keys; + std::transform(mEvolvingDeadMap.begin(), mEvolvingDeadMap.end(), std::back_inserter(keys), + [](const auto& O) { return O.first; }); + return keys; +} + +long TimeDeadMap::getMapAtOrbit(unsigned long orbit, std::vector& mmap) const +{ // fills mmap and returns requested_orbit - found_orbit. Found orbit is the highest key lower or equal to the requested one + if (mEvolvingDeadMap.empty()) { + LOG(warning) << "Requested orbit " << orbit << "from an empty time-dependent map. Doing nothing"; + return (long)orbit; + } + auto closest = mEvolvingDeadMap.upper_bound(orbit); + if (closest != mEvolvingDeadMap.begin()) { + --closest; + mmap = closest->second; + return (long)orbit - closest->first; + } else { + mmap = mEvolvingDeadMap.begin()->second; + return (long)(orbit)-mEvolvingDeadMap.begin()->first; + } +} diff --git a/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h index 6485186b66463..cd11d4b71389c 100644 --- a/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h +++ b/DataFormats/Detectors/MUON/MCH/include/DataFormatsMCH/DsChannelId.h @@ -61,5 +61,13 @@ class DsChannelId ClassDefNV(DsChannelId, 1); // class for MCH readout channel }; + +inline bool operator==(const DsChannelId& a, const DsChannelId& b) { return a.value() == b.value(); } +inline bool operator!=(const DsChannelId& a, const DsChannelId& b) { return !(a == b); } +inline bool operator<(const DsChannelId& a, const DsChannelId& b) { return a.value() < b.value(); } +inline bool operator>(const DsChannelId& a, const DsChannelId& b) { return b < a; } +inline bool operator<=(const DsChannelId& a, const DsChannelId& b) { return !(a > b); } +inline bool operator>=(const DsChannelId& a, const DsChannelId& b) { return !(a < b); } + } // namespace o2::mch #endif diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx deleted file mode 100644 index bcf10d74c95ff..0000000000000 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsMCH/DsChannelGroup.h" - -std::string o2::mch::DsChannelId() const -{ -} diff --git a/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h index 01c649a1a579a..060538fc91654 100644 --- a/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h +++ b/DataFormats/Detectors/MUON/MID/include/DataFormatsMID/Track.h @@ -188,6 +188,9 @@ class Track /// Overload ostream operator for MID track friend std::ostream& operator<<(std::ostream& stream, const Track& track); + /// set efficiency word (public function) + void setEfficiencyWord(uint32_t efficiencyWord) { mEfficiencyWord = efficiencyWord; } + private: /// Set portions of the efficiency word /// \param pos Position in the word diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h index 4f5d243028177..e503af30d8e7f 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventData.h @@ -13,10 +13,11 @@ #define ALICEO2_PHOS_EVENTDATA_H_ #include +#include #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsPHOS/Cell.h" #include "DataFormatsPHOS/Cluster.h" - +#include "DataFormatsPHOS/MCLabel.h" namespace o2 { @@ -25,10 +26,11 @@ namespace phos template struct EventData { - InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event - gsl::span mClusters; ///< PHOS clusters - gsl::span mCells; ///< PHOS cells / digits - gsl::span mCellIndices; ///< Cell indices in cluster + InteractionRecord mInteractionRecord; ///< Interaction record for the trigger corresponding to this event + gsl::span mClusters; ///< PHOS clusters + gsl::span mCells; ///< PHOS cells / digits + gsl::span mCellIndices; ///< Cell indices in cluster + std::vector> mMCCellLabels; ///< span of MC labels for each cell /// \brief Reset event structure with empty interaction record and ranges void reset() @@ -37,13 +39,14 @@ struct EventData { mClusters = gsl::span(); mCells = gsl::span(); mCellIndices = gsl::span(); + mMCCellLabels = std::vector>(); } - ClassDefNV(EventData, 1); + ClassDefNV(EventData, 2); }; } // namespace phos } // namespace o2 -#endif // ALICEO2_PHOS_EVENTDATA_H_ \ No newline at end of file +#endif // ALICEO2_PHOS_EVENTDATA_H_ diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h index 149bcc5505bf0..0d3285248d7b2 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/EventHandler.h @@ -15,13 +15,16 @@ #include #include #include +#include #include "Rtypes.h" #include "fmt/format.h" #include "DataFormatsPHOS/Cell.h" #include "DataFormatsPHOS/Cluster.h" #include "DataFormatsPHOS/Digit.h" #include "DataFormatsPHOS/EventData.h" +#include "DataFormatsPHOS/MCLabel.h" #include "DataFormatsPHOS/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" namespace o2 { @@ -259,6 +262,13 @@ class EventHandler /// \throw NotInitializedException in case the event handler is not initialized for cell const CellRange getCellsForEvent(int eventID) const; + /// \brief Get vector of MC labels belonging to the given event + /// \param eventID ID of the event + /// \return vector of MC labels for the event + /// \throw RangeException in case the required event ID exceeds the maximum number of events + /// \throw NotInitializedException in case the event handler is not initialized for cell + std::vector> getCellMCLabelForEvent(int eventID) const; + /// \brief Get range of cluster cell indices belonging to the given event /// \param eventID ID of the event /// \return Cluster cell index range for the event @@ -300,6 +310,13 @@ class EventHandler mTriggerRecordsCells = triggers; } + /// \brief Setting the pointer for the MCTruthContainer for cells + /// \param mclabels Pointer to the MCTruthContainer for cells in timeframe + void setCellMCTruthContainer(const o2::dataformats::MCTruthContainer* mclabels) + { + mCellLabels = mclabels; + } + /// \brief Reset containers with empty ranges void reset(); @@ -329,14 +346,15 @@ class EventHandler TriggerRange mTriggerRecordsCellIndices; ///< trigger record for cluster cell index type TriggerRange mTriggerRecordsCells; ///< Trigger record for cell type - ClusterRange mClusters; /// container for clusters in timeframe - CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe - CellRange mCells; /// container for cells in timeframe + ClusterRange mClusters; /// container for clusters in timeframe + CellIndexRange mClusterCellIndices; /// container for cell indices in timeframe + CellRange mCells; /// container for cells in timeframe + const o2::dataformats::MCTruthContainer* mCellLabels = nullptr; /// pointer to the MCTruthContainer for cells in timeframe - ClassDefNV(EventHandler, 1); + ClassDefNV(EventHandler, 2); }; } // namespace phos } // namespace o2 -#endif // ALICEO2_PHOS_EVENTHANDLER_H__ \ No newline at end of file +#endif // ALICEO2_PHOS_EVENTHANDLER_H__ diff --git a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h index a9aaa30243c0a..aa171e9825870 100644 --- a/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h +++ b/DataFormats/Detectors/PHOS/include/DataFormatsPHOS/MCLabel.h @@ -23,7 +23,7 @@ namespace phos class MCLabel : public o2::MCCompLabel { private: - float mEdep = 0; //deposited energy + float mEdep = 0; // deposited energy public: MCLabel() = default; @@ -44,7 +44,9 @@ class MCLabel : public o2::MCCompLabel float getEdep() const { return mEdep; } - ClassDefNV(MCLabel, 1); + double getAmplitudeFraction() const { return mEdep; } + + ClassDefNV(MCLabel, 2); }; } // namespace phos } // namespace o2 diff --git a/DataFormats/Detectors/PHOS/src/EventHandler.cxx b/DataFormats/Detectors/PHOS/src/EventHandler.cxx index 1e74bf132687b..26e5fe15490b5 100644 --- a/DataFormats/Detectors/PHOS/src/EventHandler.cxx +++ b/DataFormats/Detectors/PHOS/src/EventHandler.cxx @@ -108,6 +108,23 @@ const typename EventHandler::CellRange EventHandler +std::vector> EventHandler::getCellMCLabelForEvent(int eventID) const +{ + if (mCellLabels && mTriggerRecordsCells.size()) { + if (eventID >= mTriggerRecordsCells.size()) { + throw RangeException(eventID, mTriggerRecordsCells.size()); + } + auto& trgrecord = mTriggerRecordsCells[eventID]; + std::vector> eventlabels(trgrecord.getNumberOfObjects()); + for (int index = 0; index < trgrecord.getNumberOfObjects(); index++) { + eventlabels[index] = mCellLabels->getLabels(trgrecord.getFirstEntry() + index); + } + return eventlabels; + } + throw NotInitializedException(); +} + template const typename EventHandler::CellIndexRange EventHandler::getClusterCellIndicesForEvent(int eventID) const { @@ -130,6 +147,7 @@ void EventHandler::reset() mClusters = ClusterRange(); mClusterCellIndices = CellIndexRange(); mCells = CellRange(); + mCellLabels = nullptr; } template @@ -146,6 +164,9 @@ EventData EventHandler::buildEvent(int eventID) co if (hasCells()) { outputEvent.mCells = getCellsForEvent(eventID); } + if (mCellLabels) { + outputEvent.mMCCellLabels = getCellMCLabelForEvent(eventID); + } return outputEvent; } diff --git a/DataFormats/Detectors/TOF/CMakeLists.txt b/DataFormats/Detectors/TOF/CMakeLists.txt index 03dbd9275edf9..4d41167f7bf1d 100644 --- a/DataFormats/Detectors/TOF/CMakeLists.txt +++ b/DataFormats/Detectors/TOF/CMakeLists.txt @@ -9,6 +9,14 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +o2_add_library(DataFormatsParamTOF + SOURCES src/ParameterContainers.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkLogger) + + +o2_target_root_dictionary(DataFormatsParamTOF + HEADERS include/DataFormatsTOF/ParameterContainers.h) + o2_add_library(DataFormatsTOF SOURCES src/Cluster.cxx src/CalibInfoTOFshort.cxx @@ -16,13 +24,13 @@ o2_add_library(DataFormatsTOF src/CalibLHCphaseTOF.cxx src/CalibTimeSlewingParamTOF.cxx src/CTF.cxx - src/ParameterContainers.cxx src/CalibInfoCluster.cxx src/CosmicInfo.cxx src/Diagnostic.cxx src/TOFFEElightInfo.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats O2::GPUCommon + O2::DataFormatsParamTOF Boost::serialization) o2_target_root_dictionary(DataFormatsTOF @@ -34,7 +42,6 @@ o2_target_root_dictionary(DataFormatsTOF include/DataFormatsTOF/RawDataFormat.h include/DataFormatsTOF/CompressedDataFormat.h include/DataFormatsTOF/CTF.h - include/DataFormatsTOF/ParameterContainers.h include/DataFormatsTOF/CalibInfoCluster.h include/DataFormatsTOF/CosmicInfo.h include/DataFormatsTOF/TOFFEElightInfo.h diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h index 57d5784dc3b66..881f87ff88971 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h @@ -56,14 +56,14 @@ struct CompressedInfos { */ // ROF header data - std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF - std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF + std::vector bcIncROF; /// increment of ROF BC wrt BC of previous ROF + std::vector orbitIncROF; /// increment of ROF orbit wrt orbit of previous ROF std::vector ndigROF; /// number of digits in ROF std::vector ndiaROF; /// number of diagnostic/pattern words in ROF std::vector ndiaCrate; /// number of diagnostic/pattern words per crate in ROF // Hit data - std::vector timeFrameInc; /// time increment with respect of previous digit in TimeFrame units + std::vector timeFrameInc; /// time increment with respect of previous digit in TimeFrame units std::vector timeTDCInc; /// time increment with respect of previous digit in TDC channel (about 24.4 ps) within timeframe std::vector stripID; /// increment of stripID wrt that of prev. strip std::vector chanInStrip; /// channel in strip 0-95 (ordered in time) diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h index 589afc8a2cde9..37d3ca23ddb35 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Cluster.h @@ -49,14 +49,18 @@ class Cluster : public o2::BaseCluster kDownRight = 4, // 2^4, 5th bit kDown = 5, // 2^5, 6th bit kDownLeft = 6, // 2^6, 7th bit - kLeft = 7 }; // 2^7, 8th bit + kLeft = 7 // 2^7, 8th bit + }; Cluster() = default; - Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC); + Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1latency, int deltaBC, float geanttime = 0.0, double t0 = 0.0); ~Cluster() = default; + bool isInNominalSector() const { return mInNominalSector; } + void setInNominalSector(bool v = true) { mInNominalSector = v; } + std::int8_t getSector() const { return getCount(); } void setSector(std::int8_t value) { setCount(value); } @@ -134,6 +138,10 @@ class Cluster : public o2::BaseCluster int getDigitInfoCH(int idig) const { return mDigitInfoCh[idig]; } double getDigitInfoT(int idig) const { return mDigitInfoT[idig]; } float getDigitInfoTOT(int idig) const { return mDigitInfoTOT[idig]; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } private: #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -153,8 +161,11 @@ class Cluster : public o2::BaseCluster int mDigitInfoCh[6] = {0, 0, 0, 0, 0, 0}; double mDigitInfoT[6] = {0., 0., 0., 0., 0., 0.}; float mDigitInfoTOT[6] = {0., 0., 0., 0., 0., 0.}; + float mTgeant = 0.0; + bool mInNominalSector = false; + double mT0true = 0.0; - ClassDefNV(Cluster, 4); + ClassDefNV(Cluster, 6); }; #ifndef GPUCA_GPUCODE diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h index 9029c06d503c8..224906e43b8c6 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h @@ -18,10 +18,10 @@ #ifndef O2_TOF_PARAMCONTAINER_H #define O2_TOF_PARAMCONTAINER_H -#include "TNamed.h" -#include "TFile.h" -#include "Framework/Logger.h" -#include "map" +#include +#include +#include +#include namespace o2 { @@ -210,7 +210,13 @@ class ParameterCollection : public TNamed } /// @brief getter for the parameters stored in the container matching to a pass - const auto& getPars(const std::string& pass) const { return mParameters.at(pass); } + const std::unordered_map& getPars(const std::string& pass) const + { + if (!hasKey(pass)) { + LOG(fatal) << "Parameters for pass " << pass << " not found!"; + } + return mParameters.at(pass); + } /// @brief printing function for the content of the pass /// @param pass pass to print @@ -221,7 +227,7 @@ class ParameterCollection : public TNamed /// @brief Getter of the full map of parameters stored in the container /// @return returns the full map of parameters - const auto& getFullMap() { return mParameters; } + const std::unordered_map>& getFullMap() const { return mParameters; } /// Loader from file /// \param FileName name of the input file diff --git a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx index 350af55dfc0ee..01df4f2323edd 100644 --- a/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx +++ b/DataFormats/Detectors/TOF/src/CalibTimeSlewingParamTOF.cxx @@ -160,6 +160,8 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const } int n = (*(mChannelStart[sector]))[channel]; + int nFirst = n; + if (n < 0) { return (*(mGlobalOffset[sector]))[channel]; } @@ -197,10 +199,14 @@ float CalibTimeSlewingParamTOF::evalTimeSlewing(int channel, float totIn) const return (float)((*(mTimeSlewing[sector]))[n].second + (*(mGlobalOffset[sector]))[channel]); // use the last value stored for that channel } + if (n < nFirst) { // this is before the first point (probably tot = 0 calibration is missing) + return (*(mGlobalOffset[sector]))[channel]; + } + float w1 = (float)(tot - (*(mTimeSlewing[sector]))[n].first); float w2 = (float)((*(mTimeSlewing[sector]))[n + 1].first - tot); - return (float)((*(mGlobalOffset[sector]))[channel] + (((*(mTimeSlewing[sector]))[n].second * w2 + (*(mTimeSlewing[sector]))[n + 1].second * w1) / ((*(mTimeSlewing[sector]))[n + 1].first - (*(mTimeSlewing[sector]))[n].first))); + return (float)((*(mGlobalOffset[sector]))[channel] + (((*(mTimeSlewing[sector]))[n].second * w2 + (*(mTimeSlewing[sector]))[n + 1].second * w1) / (w1 + w2))); } //______________________________________________ void CalibTimeSlewingParamTOF::setTimeSlewingInfo(int channel, float offsetold, int nold, const unsigned short* oldtot, const short* olddt, int nnew, const unsigned short* newtot, const short* newdt) @@ -212,56 +218,76 @@ void CalibTimeSlewingParamTOF::setTimeSlewingInfo(int channel, float offsetold, // work with float to avoid to loose precision due to short (in case offset will be adjust before to move deltat to short) std::vector deltat; - float minVal = 0; - float maxVal = 0; - - if (nnew >= nold) { // use new binning - int j = 0; - for (int i = 0; i < nnew; i++) { - while (j < nold && oldtot[j + 1] < newtot[i]) { - j++; + float minVal = 100000; + float maxVal = -100000; + + // new scheme + int k1 = 0; + int k2 = 0; + + int ntotFin = 0; + std::vector totFin; + + while (k1 < nnew && k2 < nold) { + if (newtot[k1] == oldtot[k2]) { + k1++; + } else if (newtot[k1] < oldtot[k2]) { + totFin.push_back(newtot[k1]); + if (k2 > 0) { + deltat.push_back(float(newdt[k1]) + olddt[k2 - 1]); + } else { + deltat.push_back(float(newdt[k1] + olddt[k2])); } - float val = float(newdt[i]) + olddt[j]; - // printf("%d %d -> %f\n",i,j,val); - deltat.push_back(val); - if (val < minVal) { - minVal = val; + k1++; + } else { + totFin.push_back(oldtot[k2]); + if (k1 > 0) { + deltat.push_back(float(olddt[k2]) + newdt[k1 - 1]); + } else { + deltat.push_back(float(olddt[k2] + newdt[k1])); } - if (val > maxVal) { - maxVal = val; - } - } - float recentering = (minVal + maxVal) * 0.5; - (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; - // printf("update channel offset for sec=%d/ch=%d -> %f\n",sector,channel,(*(mGlobalOffset[sector]))[channel]); - (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); - - for (int i = 0; i < nnew; i++) { - (*(mTimeSlewing[sector])).emplace_back(newtot[i], (short)(deltat[i] - recentering)); - // printf("%d) %d\n",i,(*(mTimeSlewing[sector]))[i]); + k2++; } - } else { // use old binning - int j = 0; - for (int i = 0; i < nold; i++) { - while (j < nnew && newtot[j + 1] < oldtot[i]) { - j++; - } - float val = float(olddt[i]) + newdt[j]; - deltat.push_back(val); - if (val < minVal) { - minVal = val; + } + + if (k1 < nnew) { + while (k1 < nnew) { + totFin.push_back(newtot[k1]); + if (nold) { + deltat.push_back(float(newdt[k1]) + olddt[nold - 1]); + } else { + deltat.push_back(float(newdt[k1])); } - if (val > maxVal) { - maxVal = val; + k1++; + } + } else if (k2 < nold) { + while (k2 < nold) { + totFin.push_back(oldtot[k2]); + if (nnew) { + deltat.push_back(float(olddt[k2]) + newdt[nnew - 1]); + } else { + deltat.push_back(float(olddt[k2])); } + k2++; + } + } + + for (int i = 0; i < deltat.size(); i++) { + if (deltat[i] < minVal) { + minVal = deltat[i]; } - float recentering = (minVal + maxVal) * 0.5; - (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; - (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); - for (int i = 0; i < nold; i++) { - (*(mTimeSlewing[sector])).emplace_back(oldtot[i], (short)(deltat[i] - recentering)); + if (deltat[i] > maxVal) { + maxVal = deltat[i]; } } + + float recentering = (minVal + maxVal) * 0.5; + (*(mGlobalOffset[sector]))[channel] = offsetold + recentering; + (*(mChannelStart[sector]))[channel] = mTimeSlewing[sector]->size(); + + for (int i = 0; i < deltat.size(); i++) { + (*(mTimeSlewing[sector])).emplace_back(totFin[i], (short)(deltat[i] - recentering)); + } } //______________________________________________ void CalibTimeSlewingParamTOF::addTimeSlewingInfo(int channel, float tot, float time) diff --git a/DataFormats/Detectors/TOF/src/Cluster.cxx b/DataFormats/Detectors/TOF/src/Cluster.cxx index 2ca3edeb19f0a..a7f3473e0b61c 100644 --- a/DataFormats/Detectors/TOF/src/Cluster.cxx +++ b/DataFormats/Detectors/TOF/src/Cluster.cxx @@ -23,7 +23,7 @@ using namespace o2::tof; ClassImp(o2::tof::Cluster); -Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC) +Cluster::Cluster(std::int16_t sensid, float x, float y, float z, float sy2, float sz2, float syz, double timeRaw, double time, float tot, int L0L1Latency, int deltaBC, float geanttime, double t0) : o2::BaseCluster(sensid, x, y, z, sy2, sz2, syz), mTimeRaw(timeRaw), mTime(time), mTot(tot), mL0L1Latency(L0L1Latency), mDeltaBC(deltaBC), mTgeant(geanttime), mT0true(t0) { // caching R and phi diff --git a/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h new file mode 100644 index 0000000000000..2d6ee84bedb92 --- /dev/null +++ b/DataFormats/Detectors/TOF/src/DataFormatsParamTOFLinkDef.h @@ -0,0 +1,17 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link C++ class o2::tof::Parameters < 5> + ; +#pragma link C++ class o2::tof::ParameterCollection + ; + +#endif diff --git a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h index 55d1fd3973e70..03004e4c22afa 100644 --- a/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h +++ b/DataFormats/Detectors/TOF/src/DataFormatsTOFLinkDef.h @@ -33,8 +33,6 @@ #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOFshort> + ; #pragma link C++ class std::vector < o2::dataformats::CalibInfoTOF> + ; -#pragma link C++ class o2::tof::Parameters < 5> + ; -#pragma link C++ class o2::tof::ParameterCollection + ; #pragma link C++ class o2::tof::CTFHeader + ; #pragma link C++ class o2::tof::CompressedInfos + ; diff --git a/DataFormats/Detectors/TOF/src/Diagnostic.cxx b/DataFormats/Detectors/TOF/src/Diagnostic.cxx index 464013afe566a..27ca65d124dd7 100644 --- a/DataFormats/Detectors/TOF/src/Diagnostic.cxx +++ b/DataFormats/Detectors/TOF/src/Diagnostic.cxx @@ -104,7 +104,7 @@ void Diagnostic::merge(const Diagnostic* prev) { LOG(debug) << "Merging diagnostic words"; for (auto const& el : prev->mVector) { - fill(el.first, el.second + getFrequency(el.first)); + fill(el.first, el.second); } } diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index 28c0bc4672561..2cc69e16001a6 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -34,7 +34,9 @@ o2_add_library( O2::ReconstructionDataFormats O2::CommonDataFormat O2::Headers - O2::Algorithm) + O2::DataSampling + O2::Algorithm + ROOT::Minuit) o2_target_root_dictionary( DataFormatsTPC @@ -62,7 +64,9 @@ o2_target_root_dictionary( include/DataFormatsTPC/LtrCalibData.h include/DataFormatsTPC/VDriftCorrFact.h include/DataFormatsTPC/CalibdEdxCorrection.h - include/DataFormatsTPC/BetheBlochAleph.h) + include/DataFormatsTPC/BetheBlochAleph.h + include/DataFormatsTPC/PIDResponse.h + include/DataFormatsTPC/AltroSyncSignal.h) o2_add_test( ClusterNative diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h new file mode 100644 index 0000000000000..6dee49e4ed6c6 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AltroSyncSignal.h +/// \brief Definition of the timebin from which syncronization starts + +#include "GPUCommonRtypes.h" + +namespace o2::tpc +{ +struct AltroSyncSignal { + int periodTF = 10; // signal repeats every period-th TF + int timebin = 14192; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 14195, but clusters can be affected before that + + int getTB2Cut(uint32_t tfCounter) const + { + return periodTF > 0 && (tfCounter % periodTF) == 1 && tfCounter > periodTF ? timebin : -1; + } + + ClassDefNV(AltroSyncSignal, 1); +}; +} // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h index 3fff89b44ba0f..28b224298f36f 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h @@ -12,26 +12,17 @@ #ifndef AliceO2_TPC_BETHEBLOCH_H_ #define AliceO2_TPC_BETHEBLOCH_H_ -#include +#include "MathUtils/BetheBlochAleph.h" -namespace o2 -{ -namespace tpc +namespace o2::tpc { template -inline T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) { - T beta = bg / std::sqrt(static_cast(1.) + bg * bg); - - T aa = std::pow(beta, kp4); - T bb = std::pow(static_cast(1.) / bg, kp5); - bb = std::log(kp3 + bb); - - return (kp2 - aa - bb) * kp1 / aa; + return o2::common::BetheBlochAleph(bg, kp1, kp2, kp3, kp4, kp5); } -} // namespace tpc -} // namespace o2 +} // namespace o2::tpc #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h index 1e57c9654932c..1f4f08db9d1b9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h @@ -27,14 +27,15 @@ namespace tpc struct CTFHeader : public ctf::CTFDictHeader, public CompressedClustersCounters { enum : uint32_t { CombinedColumns = 0x1 }; uint32_t flags = 0; - - ClassDefNV(CTFHeader, 2); + uint32_t firstOrbitTrig = 0; /// orbit of 1st trigger + uint16_t nTriggers = 0; /// number of triggers + ClassDefNV(CTFHeader, 3); }; /// wrapper for the Entropy-encoded clusters of the TF -struct CTF : public o2::ctf::EncodedBlocks { +struct CTF : public o2::ctf::EncodedBlocks { - using container_t = o2::ctf::EncodedBlocks; + using container_t = o2::ctf::EncodedBlocks; static constexpr size_t N = getNBlocks(); static constexpr int NBitsQTot = 16; @@ -66,9 +67,14 @@ struct CTF : public o2::ctf::EncodedBlocks { BLCsigmaPadU, BLCsigmaTimeU, // can be combined with BLCsigmaPadU BLCnTrackClusters, - BLCnSliceRowClusters }; + BLCnSliceRowClusters, + // trigger info + BLCTrigOrbitInc, + BLCTrigBCInc, + BLCTrigType + }; - ClassDefNV(CTF, 2); + ClassDefNV(CTF, 4); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index eb9fd33c27476..024d6189593e9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -22,6 +22,7 @@ #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #endif // o2 includes @@ -30,6 +31,11 @@ namespace o2::tpc { +namespace conf_dedx_corr +{ +GPUconstexpr() float TglScale[4] = {1.9, 1.5, 1.22, 1.02}; ///< Max Tgl values for each ROC type +} + class CalibdEdxCorrection { public: @@ -43,9 +49,9 @@ class CalibdEdxCorrection } CalibdEdxCorrection(std::string_view fileName) { loadFromFile(fileName); } #else - CalibdEdxCorrection() CON_DEFAULT; + CalibdEdxCorrection() = default; #endif - ~CalibdEdxCorrection() CON_DEFAULT; + ~CalibdEdxCorrection() = default; GPUd() float getCorrection(const StackID& stack, ChargeType charge, float tgl = 0, float snp = 0) const { @@ -54,7 +60,8 @@ class CalibdEdxCorrection return 1; } - tgl = o2::gpu::CAMath::Abs(tgl); + // limit to the fit range in the respective region + tgl = o2::gpu::CAMath::Min(conf_dedx_corr::TglScale[stack.type], o2::gpu::CAMath::Abs(tgl)); auto p = mParams[stackIndex(stack, charge)]; float result = p[0]; // Tgl part @@ -84,12 +91,33 @@ class CalibdEdxCorrection void clear(); - void writeToFile(std::string_view fileName) const; - void loadFromFile(std::string_view fileName); + void writeToFile(std::string_view fileName, std::string_view objName = "ccdb_object") const; + void loadFromFile(std::string_view fileName, std::string_view objName = "ccdb_object"); /// \param outFileName name of the output file void dumpToTree(const char* outFileName = "calib_dedx.root") const; + /// Parameters averaged over all stacks + const std::array getMeanParams(ChargeType charge) const; + + /// Parameters averaged over all sectors for a stack type + const std::array getMeanParams(const GEMstack stack, ChargeType charge) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanParam(ChargeType charge, uint32_t param) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanParam(const GEMstack stack, ChargeType charge, uint32_t param) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanEntries(ChargeType charge) const; + + /// Single fit parameters averaged over all sectors for a stack type + float getMeanEntries(const GEMstack stack, ChargeType charge) const; + + /// set all corrections to 1, used for default initialization and to reset corrections + void setUnity(); + #endif private: diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h index 49d61007092a8..f3070d456afb1 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h @@ -31,7 +31,7 @@ template class ConstMCTruthContainer; template class ConstMCTruthContainerView; -} +} // namespace dataformats } // namespace o2 namespace o2 @@ -76,7 +76,7 @@ struct ClusterNative { GPUd() static float unpackPad(uint16_t pad) { return float(pad) * (1.f / scalePadPacked); } GPUd() static float unpackTime(uint32_t time) { return float(time) * (1.f / scaleTimePacked); } - GPUdDefault() ClusterNative() CON_DEFAULT; + GPUdDefault() ClusterNative() = default; GPUd() ClusterNative(uint32_t time, uint8_t flags, uint16_t pad, uint8_t sigmaTime, uint8_t sigmaPad, uint16_t qmax, uint16_t qtot) : padPacked(pad), sigmaTimePacked(sigmaTime), sigmaPadPacked(sigmaPad), qMax(qmax), qTot(qtot) { setTimePackedFlags(time, flags); @@ -156,6 +156,17 @@ struct ClusterNative { return (this->getFlags() < rhs.getFlags()); } } + + GPUd() bool operator==(const ClusterNative& rhs) const + { + return this->getTimePacked() == rhs.getTimePacked() && + this->padPacked == rhs.padPacked && + this->sigmaTimePacked == rhs.sigmaTimePacked && + this->sigmaPadPacked == rhs.sigmaPadPacked && + this->qMax == rhs.qMax && + this->qTot == rhs.qTot && + this->getFlags() == rhs.getFlags(); + } }; // This is an index struct to access TPC clusters inside sectors and rows. It shall not own the data, but just point to diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h index 9f49884035b7e..18ad5c6819344 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h @@ -29,7 +29,7 @@ struct CompressedClustersCounters { unsigned int nUnattachedClusters = 0; unsigned int nAttachedClustersReduced = 0; unsigned int nSliceRows = 36 * 152; - unsigned char nComppressionModes = 0; + unsigned char nComppressionModes = 0; // Don't fix this name due to ROOT dictionaries! float solenoidBz = -1e6f; int maxTimeBin = -1e6; @@ -77,8 +77,8 @@ struct CompressedClustersOffsets : public CompressedClustersPtrs_x #include #include +#include #include #include #include @@ -29,9 +30,13 @@ #include "Framework/Logger.h" #include "DataFormatsTPC/Defs.h" +#include "MathUtils/fit.h" using namespace o2::tpc; +class TLinearFitter; +class TTree; + namespace o2::tpc::dcs { @@ -50,6 +55,8 @@ struct DataPoint { bool equalTime(const DataPoint& other) const { return time == other.time; } bool operator<(const DataPoint& other) const { return time < other.time; } bool operator<(const TimeStampType timeStamp) const { return time < timeStamp; } + DataPoint operator+(const DataPoint& other) const { return DataPoint{(time + other.time) / TimeStampType{2}, value + other.value}; } + DataPoint operator/(const DataType denom) const { return DataPoint{time, value / denom}; } ClassDefNV(DataPoint, 1); }; @@ -63,6 +70,19 @@ struct DataPointVector { uint32_t sensorNumber{}; std::vector data; + /// \brief convert data points to a vector of pairs: pair.first -> data and pair.second -> time + auto getPairOfVector() const + { + std::pair, std::vector> pairs; + pairs.first.reserve(data.size()); + pairs.second.reserve(data.size()); + for (const auto& dp : data) { + pairs.first.emplace_back(dp.value); + pairs.second.emplace_back(dp.time); + } + return pairs; + } + void fill(const TimeStampType time, const T& value) { data.emplace_back(DPType{time, value}); } void fill(const DPType& dataPoint) { data.emplace_back(dataPoint); } @@ -83,6 +103,11 @@ struct DataPointVector { void clear() { data.clear(); } + void append(const DataPointVector& other) + { + data.insert(data.end(), other.data.begin(), other.data.end()); + } + /// return value at the last valid time stamp /// /// values are valid unitl the next time stamp @@ -90,7 +115,32 @@ struct DataPointVector { { const auto i = std::upper_bound(data.begin(), data.end(), DPType{timeStamp, {}}); return (i == data.begin()) ? (*i).value : (*(i - 1)).value; - }; + } + + /// calculate average value for `timeStamp`, extending the range by +- range elements + const T getAverageValueForTime(const TimeStampType timeStamp, const long range) const + { + return getAverageValueForTime(timeStamp, timeStamp, range); + } + + /// calculate average value between `from` and `unil`, extending the range by +- `range` elements + const std::pair getSumAndPoints(const TimeStampType from, const TimeStampType until, const long range) const + { + const auto iFrom = std::upper_bound(data.begin(), data.end(), DPType{from, {}}); + const auto iUntil = (from == until) ? iFrom : std::upper_bound(data.begin(), data.end(), DPType{until, {}}); + const auto distFrom = std::distance(data.begin(), iFrom); + const auto distUntil = std::distance(iUntil, data.end()); + const auto nFrom = std::min(distFrom, range + 1); + const auto nUntil = std::min(distUntil, range); + const auto nPoints = std::distance(iFrom - nFrom, iUntil + nUntil); + return {std::accumulate(iFrom - nFrom, iUntil + nUntil, DPType{(*(iFrom - nFrom)).time, T{}}).value, nPoints}; + } + + const T getAverageValueForTime(const TimeStampType from, const TimeStampType until, const long range) const + { + const auto sumAndPoints = getSumAndPoints(from, until, range); + return sumAndPoints.first / static_cast(sumAndPoints.second); + } ClassDefNV(DataPointVector, 1); }; @@ -111,6 +161,70 @@ void doClear(std::vector>& dataVector) } } +template +void doAppend(std::vector>& a, const std::vector>& b) +{ + if (a.size() != b.size()) { + LOGP(warning, "Trying to append std::vector>s of different size: {} != {}", a.size(), b.size()); + } + for (size_t i = 0; i < a.size(); ++i) { + a[i].append(b[i]); + } +} + +template +const T getAverageValueForTime(const std::vector>& dpVec, const TimeStampType from, const TimeStampType until, const long range) +{ + T ret{}; + long nPoints{}; + + for (const auto& dps : dpVec) { + const auto sumAndPoints = dps.getSumAndPoints(from, until, range); + ret += sumAndPoints.first; + nPoints += sumAndPoints.second; + } + return (nPoints > 0) ? ret / static_cast(nPoints) : T{}; +} + +template +dcs::TimeStampType getMinTime(const std::vector>& data, const bool roundToInterval, dcs::TimeStampType fitInterval) +{ + constexpr auto max = std::numeric_limits::max(); + dcs::TimeStampType firstTime = std::numeric_limits::max(); + for (const auto& sensor : data) { + const auto time = sensor.data.size() ? sensor.data.front().time : max; + firstTime = std::min(firstTime, time); + } + + // mFitInterval is is seconds. Round to full amount. + // if e.g. mFitInterval = 5min, then round 10:07:20.510 to 10:05:00.000 + if (roundToInterval) { + firstTime -= (firstTime % fitInterval); + } + + return firstTime; +} + +template +dcs::TimeStampType getMaxTime(const std::vector>& data) +{ + constexpr auto min = 0; + dcs::TimeStampType lastTime = 0; + for (const auto& sensor : data) { + const auto time = sensor.data.size() ? sensor.data.back().time : 0; + lastTime = std::max(lastTime, time); + } + + // mFitInterval is is seconds. Round to full amount. + // if e.g. mFitInterval = 5min, then round 10:07:20.510 to 10:05:00.000 + // TODO: fix this + // if (mRoundToInterval) { + // lastTime -= (lastTime % mFitInterval); + //} + + return lastTime; +} + using RawDPsF = DataPointVector; // using RawDPsI = DataPointVector; @@ -152,11 +266,20 @@ struct Temperature { static constexpr auto& getSensorPosition(const size_t sensor) { return SensorPosition[sensor]; } + /// \brief make fit of the mean temperature and gradients in time intervals + /// \param Side TPC side for which to make the fit + /// \param fitInterval time interval for the fits + /// \param roundToInterval round min time + void fitTemperature(Side side, dcs::TimeStampType fitInterval = 5 * 60 * 1000, const bool roundToInterval = false); + struct Stats { DataType mean{}; ///< average temperature in K DataType gradX{}; ///< horizontal temperature gradient in K/cm DataType gradY{}; ///< vertical temperature gradient in K/cm + Stats operator+(const Stats& other) const { return Stats{mean + other.mean, gradX + other.gradX, gradY + other.gradY}; } + Stats operator/(const DataType val) const { return Stats{mean / val, gradX / val, gradY / val}; } + ClassDefNV(Stats, 1); }; using StatsDPs = DataPointVector; @@ -170,6 +293,11 @@ struct Temperature { return (s == Side::A) ? statsA.getValueForTime(timeStamp) : statsC.getValueForTime(timeStamp); } + DataType getMeanTempRaw() + { + return getAverageValueForTime(raw, 0, 9999999999999, 0); + } + void fill(std::string_view sensor, const TimeStampType time, const DataType temperature) { raw[SensorNameMap.at(sensor.data())].fill(time, temperature); @@ -177,6 +305,8 @@ struct Temperature { void sortAndClean() { + statsA.sortAndClean(); + statsC.sortAndClean(); doSortAndClean(raw); } @@ -187,6 +317,16 @@ struct Temperature { statsC.clear(); } + void append(const Temperature& other) + { + statsA.append(other.statsA); + statsC.append(other.statsC); + doAppend(raw, other.raw); + } + + private: + bool makeFit(TLinearFitter& fitter, const int nDim, std::vector& xVals, std::vector& temperatures); + ClassDefNV(Temperature, 1); }; @@ -195,8 +335,7 @@ struct Temperature { /// struct HV { - HV() - noexcept; + HV() noexcept; // Exmple strings // TPC_HV_A03_I_G1B_I @@ -295,6 +434,13 @@ struct HV { doClear(states); } + void append(const HV& other) + { + doAppend(voltages, other.voltages); + doAppend(currents, other.currents); + doAppend(states, other.states); + } + ClassDefNV(HV, 1); }; @@ -382,6 +528,18 @@ struct Gas { o2Sensor.clear(); } + void append(const Gas& other) + { + neon.append(other.neon); + co2.append(other.co2); + n2.append(other.n2); + argon.append(other.argon); + h2o.append(other.h2o); + o2.append(other.o2); + h2oSensor.append(other.h2oSensor); + o2Sensor.append(other.o2Sensor); + } + TimeStampType getMinTime() const; TimeStampType getMaxTime() const; @@ -389,5 +547,77 @@ struct Gas { ClassDefNV(Gas, 1); }; +struct RobustPressure { + using Stats = o2::math_utils::RollingStats; + Stats surfaceAtmosPressure; ///< rolling statistics of surface sensor + Stats cavernAtmosPressure; ///< rolling statistics of cavern sensor 1 + Stats cavernAtmosPressure2; ///< rolling statistics of cavern sensor 2 + Stats cavernAtmosPressure12; ///< rolling statistics of cavernAtmosPressure/cavernAtmosPressure2 + Stats cavernAtmosPressure1S; ///< rolling statistics of cavernAtmosPressure/surfaceAtmosPressure + Stats cavernAtmosPressure2S; ///< rolling statistics of cavernAtmosPressure2/surfaceAtmosPressure + std::vector isOk; ///< bit mask of valid sensors: cavernBit 0, cavern2Bit = 1, surfaceBit = 2 + std::vector robustPressure; ///< combined robust pressure value that should be used + std::vector time; ///< time stamps of all pressure values + TimeStampType timeInterval; ///< time interval used for rolling statistics + TimeStampType timeIntervalRef; ///< reference time interval used for normalization of pressure sensors + float maxDist{}; ///< maximum allowed time distance between sensors to be accepted for robust pressure calculation + float maxDiff{0.2f}; ///< maximum allowed pressure difference between sensors to be accepted for robust pressure calculation + + ClassDefNV(RobustPressure, 2); +}; + +struct Pressure { + + /// \brief fill pressure data + /// \param sensor name of the sensor from DCS data stream + /// \param time measurement time + /// \param value pressure value + void fill(std::string_view sensor, const TimeStampType time, const DataType value); + + /// sort pressure values and remove obvious outliers + /// \param pMin min accepted pressure + /// \param pMax max accepted pressure + void sortAndClean(float pMin = 800, float pMax = 1100); + + /// \clear all stored data except the buffer + void clear(); + + /// append other pressure values + void append(const Pressure& other); + + /// \return get minimum time of stored data + TimeStampType getMinTime() const; + + /// \return get maximum time of stored data + TimeStampType getMaxTime() const; + + /// \brief average pressure values for given time interval + /// \param timeInterval time interval for which the pressure values are averaged + /// \param timeIntervalRef time interval used to calculate the normalization values for the pressure + /// \param tStart min time of the data + /// \param tEnd max time of the data + /// \param nthreads numbe rof threads used for some calculations + void makeRobustPressure(TimeStampType timeInterval = 100 * 1000, TimeStampType timeIntervalRef = 24 * 60 * 1000, TimeStampType tStart = 1, TimeStampType tEnd = 0, const int nthreads = 1); + + /// set aliases for the cuts used in the calculation of the robust pressure + static void setAliases(TTree* tree); + + RawDPsF cavernAtmosPressure{}; ///< raw pressure in the cavern from sensor 1 + RawDPsF cavernAtmosPressure2{}; ///< raw pressure in the cavern from sensor 2 + RawDPsF surfaceAtmosPressure{}; ///< raw pressure at the surface + RobustPressure robustPressure{}; ///< combined robust pressure estimator from all three sensors + + std::pair, std::vector> mCavernAtmosPressure1Buff{}; ///, std::vector> mCavernAtmosPressure2Buff{}; ///, std::vector> mSurfaceAtmosPressureBuff{}; ///, std::vector> mPressure12Buff{}; ///, std::vector> mPressure1SBuff{}; ///, std::vector> mPressure2SBuff{}; ///, std::vector> mRobPressureBuff{}; /// matchedLtrIDs; ///< list of matched laser track IDs + std::vector matchedLtrIDs; ///< matched laser track IDs + std::vector nTrackTF; ///< number of laser tracks per TF + std::vector dEdx; ///< dE/dx of each track + float tp{0.f}; ///< temperature over pressure ratio - float getDriftVCorrection() const { return 0.5f * (dvCorrectionA + dvCorrectionC); } + bool isValid() const + { + return (std::abs(dvCorrectionA - 1.f) < 0.2) || (std::abs(dvCorrectionC - 1.f) < 0.2); + } + + float getDriftVCorrection() const + { + float correction = 0; + int nCorr = 0; + // only allow +- 20% around reference correction + if (std::abs(dvCorrectionA - 1.f) < 0.2) { + correction += dvCorrectionA; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionA ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionA); + } + + if (std::abs(dvCorrectionC - 1.f) < 0.2) { + correction += dvCorrectionC; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionC ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionC); + } + + if (nCorr == 0) { + LOGP(error, "no valid drift velocity correction"); + return 1.f; + } + + return correction / nCorr; + } + + float getVDrift() const { return refVDrift / getDriftVCorrection(); } + + float getTimeOffset() const { return refTimeOffset + timeOffsetCorr; } // renormalize reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference void normalize(float newVRef = 0.f) @@ -61,6 +100,20 @@ struct LtrCalibData { dvCorrectionC *= fact; } + // similarly, the time offset reference is set to provided newRefTimeOffset (if > -998) or modified to have timeOffsetCorr to + // be 0 otherwise + + void normalizeOffset(float newRefTimeOffset = -999.) + { + if (newRefTimeOffset > -999.) { + timeOffsetCorr = getTimeOffset() - newRefTimeOffset; + refTimeOffset = newRefTimeOffset; + } else { + refTimeOffset = getTimeOffset(); + timeOffsetCorr = 0.; + } + } + float getT0A() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / refVDrift; } float getT0C() const { return (250.f * (1.f - dvCorrectionC) + dvOffsetC) / refVDrift; } float getZOffsetA() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / dvCorrectionA; } @@ -79,10 +132,14 @@ struct LtrCalibData { nTracksA = 0; nTracksC = 0; refVDrift = 0; + refTimeOffset = 0; + timeOffsetCorr = 0; matchedLtrIDs.clear(); + nTrackTF.clear(); + dEdx.clear(); } - ClassDefNV(LtrCalibData, 2); + ClassDefNV(LtrCalibData, 5); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h new file mode 100644 index 0000000000000..277011a260631 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h @@ -0,0 +1,145 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// @file PIDResponse.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// + +#ifndef AliceO2_TPC_PIDResponse_H +#define AliceO2_TPC_PIDResponse_H + +// o2 includes +#include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" +#include "ReconstructionDataFormats/PID.h" +#include "DataFormatsTPC/PIDResponse.h" +#include "DataFormatsTPC/BetheBlochAleph.h" +#include "DataFormatsTPC/TrackTPC.h" + +namespace o2::tpc +{ +class TrackTPC; + +/// \brief PID response class +/// +/// This class is used to handle the TPC PID response. +/// + +class PIDResponse +{ + public: + /// default constructor + PIDResponse() = default; + + /// default destructor + ~PIDResponse() = default; + + /// setters + GPUd() void setBetheBlochParams(const float betheBlochParams[5]); + GPUd() void setMIP(float mip) { mMIP = mip; } + GPUd() void setChargeFactor(float chargeFactor) { mChargeFactor = chargeFactor; } + + /// getters + GPUd() const float* getBetheBlochParams() const { return mBetheBlochParams; } + GPUd() float getMIP() const { return mMIP; } + GPUd() float getChargeFactor() const { return mChargeFactor; } + + /// get expected signal of the track + GPUd() float getExpectedSignal(const TrackTPC& track, const o2::track::PID::ID id) const; + + /// get most probable PID of the track + GPUd() o2::track::PID::ID getMostProbablePID(const TrackTPC& track, float PID_EKrangeMin, float PID_EKrangeMax, float PID_EPrangeMin, float PID_EPrangeMax, float PID_EDrangeMin, float PID_EDrangeMax, float PID_ETrangeMin, float PID_ETrangeMax, char PID_useNsigma, float PID_sigma) const; + + private: + float mBetheBlochParams[5] = {0.19310481, 4.26696118, 0.00522579, 2.38124907, 0.98055396}; // BBAleph average fit parameters + float mMIP = 50.f; + float mChargeFactor = 2.299999952316284f; + + ClassDefNV(PIDResponse, 1); +}; + +GPUd() void PIDResponse::setBetheBlochParams(const float betheBlochParams[5]) +{ + for (int i = 0; i < 5; i++) { + mBetheBlochParams[i] = betheBlochParams[i]; + } +} + +GPUd() float PIDResponse::getExpectedSignal(const TrackTPC& track, const o2::track::PID::ID id) const +{ + const float bg = static_cast(track.getP() / o2::track::pid_constants::sMasses[id]); + if (bg < 0.05) { + return -999.; + } + const float bethe = mMIP * o2::tpc::BetheBlochAleph(bg, mBetheBlochParams[0], mBetheBlochParams[1], mBetheBlochParams[2], mBetheBlochParams[3], mBetheBlochParams[4]) * o2::gpu::GPUCommonMath::Pow(static_cast(o2::track::pid_constants::sCharges[id]), mChargeFactor); + return bethe >= 0. ? bethe : -999.; +} + +// get most probable PID +GPUd() o2::track::PID::ID PIDResponse::getMostProbablePID(const TrackTPC& track, float PID_EKrangeMin, float PID_EKrangeMax, float PID_EPrangeMin, float PID_EPrangeMax, float PID_EDrangeMin, float PID_EDrangeMax, float PID_ETrangeMin, float PID_ETrangeMax, char PID_useNsigma, float PID_sigma) const +{ + const float dEdx = track.getdEdx().dEdxTotTPC; + + if (dEdx < 10.) { + return o2::track::PID::Pion; + } + + auto id = o2::track::PID::Electron; + float distanceMin = 0.; + float dEdxExpected = getExpectedSignal(track, id); + if (PID_useNsigma) { + // using nSigma + distanceMin = o2::gpu::GPUCommonMath::Abs((dEdx - dEdxExpected) / (PID_sigma * dEdxExpected)); + } else { + // using absolute distance + distanceMin = o2::gpu::GPUCommonMath::Abs(dEdx - dEdxExpected); + } + + // calculate the distance to the expected dEdx signals + // start from Pion to exlude Muons + for (o2::track::PID::ID i = o2::track::PID::Pion; i < o2::track::PID::NIDs; i++) { + float distance = 0.; + dEdxExpected = getExpectedSignal(track, i); + if (PID_useNsigma) { + // using nSigma + distance = o2::gpu::GPUCommonMath::Abs((dEdx - dEdxExpected) / (PID_sigma * dEdxExpected)); + } else { + // using absolute distance + distance = o2::gpu::GPUCommonMath::Abs(dEdx - dEdxExpected); + } + if (distance < distanceMin) { + id = i; + distanceMin = distance; + } + } + + // override the electrons to the closest alternative PID in the bands crossing regions + if (id == o2::track::PID::Electron) { + const float p = track.getP(); + if ((p > PID_EKrangeMin) && (p < PID_EKrangeMax)) { + id = o2::track::PID::Kaon; + } else if ((p > PID_EPrangeMin) && (p < PID_EPrangeMax)) { + id = o2::track::PID::Proton; + } else if ((p > PID_EDrangeMin) && (p < PID_EDrangeMax)) { + id = o2::track::PID::Deuteron; + } else if ((p > PID_ETrangeMin) && (p < PID_ETrangeMax)) { + id = o2::track::PID::Triton; + } + } + + return id; +} + +} // namespace o2::tpc + +#endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h index 26d3fe9cf21cc..db96280bde534 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/RawDataTypes.h @@ -28,6 +28,7 @@ enum Type : char { ZS = 2, ///< final Zero Suppression (can be ILBZS, DLBZS) IDC = 3, ///< Integrated Digitial Currents, with priority bit to end up in separate buffer SAC = 4, ///< Sampled Analogue Currents from the current monitor + CMV = 5, ///< Common mode values }; const std::unordered_map TypeNameMap{ @@ -36,6 +37,7 @@ const std::unordered_map TypeNameMap{ {Type::ZS, "ZS"}, {Type::IDC, "IDC"}, {Type::SAC, "SAC"}, + {Type::CMV, "CMV"}, }; } // namespace o2::tpc::raw_data_types diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h index 53cbb66435ee6..416522185dd7d 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/SAC.h @@ -81,7 +81,7 @@ struct dataDef { }; /// /// uint32_t pktNumber = 0; ///< packet number of this front end card. Should always increase by 1 - uint32_t timeStamp = 0; ///< tims stamp + uint32_t timeStamp = 0; ///< time stamp char dataWords[DataSize]; ///< ASCI encoded SAC data uint32_t crc32 = 0; ///< CRC32 checksum uint32_t trailer = 0; ///< trailer magic word, should always be TrailerWord diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h index f8bcc601cf659..451acfe9d1fa0 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackCuts.h @@ -46,13 +46,16 @@ class TrackCuts void setNClusMin(float NClusMin) { mNClusMin = NClusMin; } void setdEdxMin(float dEdxMin) { mdEdxMin = dEdxMin; } void setdEdxMax(float dEdxMax) { mdEdxMax = dEdxMax; } + /// try to remove looper cutting on (abs(z_out) - abs(z_in)) < -10), not very precise + void setCutLooper(bool cut) { mCutLooper = cut; } private: - float mPMin{0}; ///< min momentum allowed - float mPMax{1e10}; ///< max momentum allowed - float mNClusMin{0}; ///< min number of clusters in track allowed - float mdEdxMin{0}; ///< min dEdx - float mdEdxMax{1e10}; ///< max dEdx + float mPMin{0}; ///< min momentum allowed + float mPMax{1e10}; ///< max momentum allowed + float mNClusMin{0}; ///< min number of clusters in track allowed + float mdEdxMin{0}; ///< min dEdx + float mdEdxMax{1e10}; ///< max dEdx + bool mCutLooper{false}; ///< cut looper comparing zout-zin ClassDefNV(TrackCuts, 1) }; diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h index 42c098b7bdd54..e7e23dea91e88 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/TrackTPC.h @@ -79,8 +79,8 @@ class TrackTPC : public o2::track::TrackParCov GPUd() void setClusterRef(uint32_t entry, uint16_t ncl) { mClustersReference.set(entry, ncl); } template - GPUd() static inline void getClusterReference(T& clinfo, int nCluster, - uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex, const ClusRef& ref) + GPUdi() static void getClusterReference(T& clinfo, int nCluster, + uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex, const ClusRef& ref) { // data for given tracks starts at clinfo[ ref.getFirstEntry() ], // 1st ref.getEntries() cluster indices are stored as uint32_t @@ -95,15 +95,15 @@ class TrackTPC : public o2::track::TrackParCov } template - GPUd() inline void getClusterReference(T& clinfo, int nCluster, - uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const + GPUdi() void getClusterReference(T& clinfo, int nCluster, + uint8_t& sectorIndex, uint8_t& rowIndex, uint32_t& clusterIndex) const { getClusterReference(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, mClustersReference); } template - GPUd() static inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex, const ClusRef& ref) + GPUdi() static const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex, const ClusRef& ref) { uint32_t clusterIndex; getClusterReference(clinfo, nCluster, sectorIndex, rowIndex, clusterIndex, ref); @@ -111,15 +111,15 @@ class TrackTPC : public o2::track::TrackParCov } template - GPUd() inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const + GPUdi() const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters, uint8_t& sectorIndex, uint8_t& rowIndex) const { return getCluster(clinfo, nCluster, clusters, sectorIndex, rowIndex, mClustersReference); } template - GPUd() inline const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, - const o2::tpc::ClusterNativeAccess& clusters) const + GPUdi() const o2::tpc::ClusterNative& getCluster(T& clinfo, int nCluster, + const o2::tpc::ClusterNativeAccess& clusters) const { uint8_t sectorIndex, rowIndex; return (getCluster(clinfo, nCluster, clusters, sectorIndex, rowIndex)); @@ -128,6 +128,9 @@ class TrackTPC : public o2::track::TrackParCov GPUd() const dEdxInfo& getdEdx() const { return mdEdx; } GPUd() void setdEdx(const dEdxInfo& v) { mdEdx = v; } + GPUd() const dEdxInfo& getdEdxAlt() const { return mdEdxAlt; } + GPUd() void setdEdxAlt(const dEdxInfo& v) { mdEdxAlt = v; } + private: float mTime0 = 0.f; ///< Assumed time of the vertex that created the track in TPC time bins, 0 for triggered data float mDeltaTFwd = 0; ///< max possible increment to mTime0 @@ -136,9 +139,10 @@ class TrackTPC : public o2::track::TrackParCov float mChi2 = 0.f; // Chi2 of the track o2::track::TrackParCov mOuterParam; // Track parameters at outer end of TPC. dEdxInfo mdEdx; // dEdx Information + dEdxInfo mdEdxAlt; // dEdx alternative Information ClusRef mClustersReference; // reference to externale cluster indices - ClassDefNV(TrackTPC, 4); + ClassDefNV(TrackTPC, 5); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h index 03ad9755fedae..a20c37e9b2cee 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/VDriftCorrFact.h @@ -26,14 +26,15 @@ namespace o2::tpc { struct VDriftCorrFact { - long firstTime{}; ///< first time stamp of processed TFs - long lastTime{}; ///< last time stamp of processed TFs - long creationTime{}; ///< time of creation - float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) - float corrFactErr{0.0}; ///< stat error of correction factor - float refVDrift{0.}; ///< reference vdrift for which factor was extracted + long firstTime{}; ///< first time stamp of processed TFs + long lastTime{}; ///< last time stamp of processed TFs + long creationTime{}; ///< time of creation + float corrFact{1.0}; ///< drift velocity correction factor (multiplicative) + float corrFactErr{0.0}; ///< stat error of correction factor + float refVDrift{0.}; ///< reference vdrift for which factor was extracted float refTimeOffset{0.}; ///< additive time offset reference (\mus) float timeOffsetCorr{0.}; ///< additive time offset correction (\mus) + float refTP{0.}; ///< reference temperature / pressure for which refVDrift was extracted float getVDrift() const { return refVDrift * corrFact; } float getVDriftError() const { return refVDrift * corrFactErr; } @@ -41,12 +42,19 @@ struct VDriftCorrFact { float getTimeOffset() const { return refTimeOffset + timeOffsetCorr; } // renormalize VDrift reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference - void normalize(float newVRef = 0.f) + void normalize(float newVRef = 0.f, float tp = 0.f) { + float normVDrift = newVRef; if (newVRef == 0.f) { - newVRef = refVDrift * corrFact; + normVDrift = refVDrift * corrFact; + newVRef = normVDrift; + if ((tp > 0) && (refTP > 0)) { + // linear scaling based on relative change of T/P + normVDrift *= refTP / tp; + refTP = tp; // update reference T/P + } } - float fact = refVDrift / newVRef; + float fact = refVDrift / normVDrift; refVDrift = newVRef; corrFactErr *= fact; corrFact *= fact; @@ -66,7 +74,7 @@ struct VDriftCorrFact { } } - ClassDefNV(VDriftCorrFact, 2); + ClassDefNV(VDriftCorrFact, 3); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h index 30b40ed70b9c7..f4a318bc30101 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/WorkflowHelper.h @@ -21,6 +21,7 @@ #include "Framework/DataRefUtils.h" #include #include "Framework/InputRecordWalker.h" +#include "DataSampling/DataSamplingHeader.h" #include "DataFormatsTPC/TrackTPC.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -80,7 +81,7 @@ static auto getWorkflowTPCInput(o2::framework::ProcessingContext& pc, int verbos if (do_digits) { std::fill(inputDigitsMCIndex.begin(), inputDigitsMCIndex.end(), -1); } - for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { + for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { auto const* sectorHeader = o2::framework::DataRefUtils::getHeader(ref); if (sectorHeader == nullptr) { // FIXME: think about error policy @@ -127,7 +128,7 @@ static auto getWorkflowTPCInput(o2::framework::ProcessingContext& pc, int verbos {"check", o2::framework::ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "CLUSTERNATIVE"}, o2::framework::Lifetime::Timeframe}, }; unsigned long recvMask = 0; - for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { + for (auto const& ref : o2::framework::InputRecordWalker(pc.inputs(), filter)) { auto const* sectorHeader = o2::framework::DataRefUtils::getHeader(ref); if (sectorHeader == nullptr) { throw std::runtime_error("sector header missing on header stack"); diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h index b83a894c1aa6f..b1df9445bcf42 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h @@ -19,6 +19,7 @@ #include // for size_t #endif #include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" namespace o2 { @@ -84,6 +85,35 @@ struct ZeroSuppressedContainer { // Struct for the TPC zero suppressed data form TPCZSHDR hdr; // ZS header }; +/// Trigger word for dense link-base ZS +/// +/// Trigger word is always 128bit and occurs always in the last page of a HBF before the meta header +struct TriggerWordDLBZS { + static constexpr uint16_t MaxTriggerEntries = 8; ///< Maximum number of trigger information + + /// trigger types as in the ttype bits + enum TriggerType : uint8_t { + PhT = 1, ///< Physics Trigger + PP = 2, ///< Pre Pulse for calibration + Cal = 4, ///< Laser (Calibration trigger) + }; + uint16_t triggerEntries[MaxTriggerEntries] = {}; + + uint16_t getTriggerBC(int entry = 0) const { return triggerEntries[entry] & 0xFFF; } + uint16_t getTriggerType(int entry = 0) const { return (triggerEntries[entry] >> 12) & 0x7; } + bool isValid(int entry = 0) const { return triggerEntries[entry] & 0x8000; } + + ClassDefNV(TriggerWordDLBZS, 1); +}; + +/// Trigger info including the orbit +struct TriggerInfoDLBZS { + TriggerWordDLBZS triggerWord{}; ///< trigger Word information + uint32_t orbit{}; ///< orbit of the trigger word + + ClassDefNV(TriggerInfoDLBZS, 1); +}; + } // namespace tpc } // namespace o2 #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h index e8c9dd2317d51..a753f24aec11f 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppressionLinkBased.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #ifndef GPUCA_GPUCODE +#include #include #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h index 63b64a50b63dc..bdf9c41aae421 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h @@ -22,24 +22,24 @@ namespace o2 namespace tpc { struct dEdxInfo { - float dEdxTotIROC; - float dEdxTotOROC1; - float dEdxTotOROC2; - float dEdxTotOROC3; - float dEdxTotTPC; - float dEdxMaxIROC; - float dEdxMaxOROC1; - float dEdxMaxOROC2; - float dEdxMaxOROC3; - float dEdxMaxTPC; - unsigned char NHitsIROC; - unsigned char NHitsSubThresholdIROC; - unsigned char NHitsOROC1; - unsigned char NHitsSubThresholdOROC1; - unsigned char NHitsOROC2; - unsigned char NHitsSubThresholdOROC2; - unsigned char NHitsOROC3; - unsigned char NHitsSubThresholdOROC3; + float dEdxTotIROC = 0.f; + float dEdxTotOROC1 = 0.f; + float dEdxTotOROC2 = 0.f; + float dEdxTotOROC3 = 0.f; + float dEdxTotTPC = 0.f; + float dEdxMaxIROC = 0.f; + float dEdxMaxOROC1 = 0.f; + float dEdxMaxOROC2 = 0.f; + float dEdxMaxOROC3 = 0.f; + float dEdxMaxTPC = 0.f; + unsigned char NHitsIROC = 0; + unsigned char NHitsSubThresholdIROC = 0; + unsigned char NHitsOROC1 = 0; + unsigned char NHitsSubThresholdOROC1 = 0; + unsigned char NHitsOROC2 = 0; + unsigned char NHitsSubThresholdOROC2 = 0; + unsigned char NHitsOROC3 = 0; + unsigned char NHitsSubThresholdOROC3 = 0; ClassDefNV(dEdxInfo, 1); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index b843577f1976a..152feacb41937 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -11,9 +11,11 @@ #include "DataFormatsTPC/CalibdEdxCorrection.h" +#include #include // o2 includes +#include "Framework/Logger.h" #include "DataFormatsTPC/Defs.h" #include "CommonUtils/TreeStreamRedirector.h" @@ -35,18 +37,30 @@ void CalibdEdxCorrection::clear() mDims = -1; } -void CalibdEdxCorrection::writeToFile(std::string_view fileName) const +void CalibdEdxCorrection::writeToFile(std::string_view fileName, std::string_view objName) const { std::unique_ptr file(TFile::Open(fileName.data(), "recreate")); - file->WriteObject(this, "CalibdEdxCorrection"); + if (!file) { + LOGP(error, "Failed to open file {} for writing", fileName.data()); + return; + } + + file->WriteObject(this, objName.data()); } -void CalibdEdxCorrection::loadFromFile(std::string_view fileName) +void CalibdEdxCorrection::loadFromFile(std::string_view fileName, std::string_view objName) { std::unique_ptr file(TFile::Open(fileName.data())); - auto tmp = file->Get("CalibdEdxCorrection"); + if (!file || file->IsZombie()) { + LOGP(error, "Failed to open file {}", fileName.data()); + return; + } + + auto tmp = file->Get(objName.data()); if (tmp != nullptr) { *this = *tmp; + } else { + LOGP(error, "Failed to load object with name {} from file {}", objName.data(), fileName.data()); } } @@ -86,3 +100,84 @@ void CalibdEdxCorrection::dumpToTree(const char* outFileName) const } } } + +const std::array CalibdEdxCorrection::getMeanParams(ChargeType charge) const +{ + std::array params{}; + + for (int index = 0; index < FitSize / 2; ++index) { + std::transform(params.begin(), params.end(), mParams[index + charge * FitSize / 2], params.begin(), std::plus<>()); + } + std::for_each(params.begin(), params.end(), [](auto& val) { val /= (0.5f * FitSize); }); + return params; +} + +const std::array CalibdEdxCorrection::getMeanParams(const GEMstack stack, ChargeType charge) const +{ + std::array params{}; + + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + std::transform(params.begin(), params.end(), getParams(StackID{index, stack}, charge), params.begin(), std::plus<>()); + } + std::for_each(params.begin(), params.end(), [](auto& val) { val /= (SECTORSPERSIDE * SIDES); }); + return params; +} + +float CalibdEdxCorrection::getMeanParam(ChargeType charge, uint32_t param) const +{ + if (param >= ParamSize) { + return 0.f; + } + float mean{}; + for (int index = 0; index < FitSize / 2; ++index) { + mean += mParams[index + charge * FitSize / 2][param]; + } + + return mean / (0.5f * FitSize); +} + +float CalibdEdxCorrection::getMeanParam(const GEMstack stack, ChargeType charge, uint32_t param) const +{ + if (param >= ParamSize) { + return 0.f; + } + float mean{}; + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + mean += getParams(StackID{index, stack}, charge)[param]; + } + + return mean / (SECTORSPERSIDE * SIDES); +} + +float CalibdEdxCorrection::getMeanEntries(ChargeType charge) const +{ + float mean{}; + for (int index = 0; index < FitSize / 2; ++index) { + mean += mEntries[index + charge * FitSize / 2]; + } + + return mean / (0.5f * FitSize); +} + +float CalibdEdxCorrection::getMeanEntries(const GEMstack stack, ChargeType charge) const +{ + float mean{}; + for (int index = 0; index < SECTORSPERSIDE * SIDES; ++index) { + mean += getEntries(StackID{index, stack}, charge); + } + + return mean / (SECTORSPERSIDE * SIDES); +} + +void CalibdEdxCorrection::setUnity() +{ + for (int i = 0; i < FitSize; ++i) { + for (int j = 0; j < ParamSize; ++j) { + mParams[i][j] = 0.f; + } + mParams[i][0] = 1.f; // constant term = 1 + mChi2[i] = 0.f; + mEntries[i] = 0; + } + mDims = 0; +} diff --git a/DataFormats/Detectors/TPC/src/DCS.cxx b/DataFormats/Detectors/TPC/src/DCS.cxx index 131f6c9526647..b56d07acd7c73 100644 --- a/DataFormats/Detectors/TPC/src/DCS.cxx +++ b/DataFormats/Detectors/TPC/src/DCS.cxx @@ -16,6 +16,8 @@ #include #include "DataFormatsTPC/DCS.h" +#include "TLinearFitter.h" +#include "TTree.h" using namespace o2::tpc::dcs; @@ -117,3 +119,431 @@ TimeStampType Gas::getMaxTime() const return *std::max_element(times.begin(), times.end()); } + +TimeStampType Pressure::getMinTime() const +{ + constexpr auto max = std::numeric_limits::max(); + const std::vector times{ + cavernAtmosPressure.data.size() ? cavernAtmosPressure.data.front().time : max, + cavernAtmosPressure2.data.size() ? cavernAtmosPressure2.data.front().time : max, + surfaceAtmosPressure.data.size() ? surfaceAtmosPressure.data.front().time : max, + }; + + return *std::min_element(times.begin(), times.end()); +} + +TimeStampType Pressure::getMaxTime() const +{ + constexpr auto min = 0; + const std::vector times{ + cavernAtmosPressure.data.size() ? cavernAtmosPressure.data.back().time : min, + cavernAtmosPressure2.data.size() ? cavernAtmosPressure2.data.back().time : min, + surfaceAtmosPressure.data.size() ? surfaceAtmosPressure.data.back().time : min, + }; + + return *std::max_element(times.begin(), times.end()); +} + +bool Temperature::makeFit(TLinearFitter& fitter, const int nDim, std::vector& xVals, std::vector& temperatures) +{ + const int minPointsForFit = 5; + if (temperatures.empty() || (temperatures.size() < minPointsForFit)) { + LOGP(warning, "Number of points {} for fit smaller than minimum of {}!", temperatures.size(), minPointsForFit); + return false; + } + + fitter.ClearPoints(); + fitter.AssignData(temperatures.size(), nDim, xVals.data(), temperatures.data()); + int status = fitter.Eval(); + if (status == 1) { + LOGP(warning, "Fit failed!"); + return false; + } + return true; +} + +void Temperature::fitTemperature(Side side, dcs::TimeStampType fitInterval, const bool roundToInterval) +{ + // clear old data + auto& stats = (side == Side::A) ? statsA : statsC; + stats.clear(); + + // temperature fits in x-y + const int nDim = 2; + TLinearFitter fitter(nDim, "1 ++ x0 ++ x1", ""); + std::array startPos{}; + const size_t sensorOffset = (side == Side::C) ? dcs::Temperature::SensorsPerSide : 0; + + const dcs::TimeStampType refTime = getMinTime(raw, fitInterval, roundToInterval); + const dcs::TimeStampType refTimeMax = getMaxTime(raw); + + // calculate number of intervals and see if the last interval should be merged into the previous one + const int lastIntervalDuration = (refTimeMax - refTime) % fitInterval; + + // process the last interval only if it contains more than 50% of the interval duration + const bool procLastInt = (lastIntervalDuration / fitInterval > 0.5); + int numIntervals = (refTimeMax - refTime) / fitInterval + procLastInt; + if (numIntervals == 0) { + numIntervals = 1; + } + + // buffer for fit values + std::vector xVals; + std::vector temperatures; + xVals.reserve(2 * 1000); + temperatures.reserve(1000); + + for (int interval = 0; interval < numIntervals; ++interval) { + const dcs::TimeStampType timeStart = refTime + interval * fitInterval; + + // clear buffer + xVals.clear(); + temperatures.clear(); + + // TODO: check if we should use refTime + dcs::TimeStampType firstTime = std::numeric_limits::max(); + dcs::TimeStampType LastTime = 0; + + for (size_t iSensor = 0; iSensor < dcs::Temperature::SensorsPerSide; ++iSensor) { + const auto& sensor = raw[iSensor + sensorOffset]; + + LOGP(debug, "sensor {}, start {}, size {}", sensor.sensorNumber, startPos[iSensor], sensor.data.size()); + while (startPos[iSensor] < sensor.data.size()) { + const auto& dataPoint = sensor.data[startPos[iSensor]]; + if (((dataPoint.time - timeStart) >= fitInterval) && (interval != numIntervals - 1)) { + LOGP(debug, "sensor {}, {} - {} >= {}", sensor.sensorNumber, dataPoint.time, timeStart, fitInterval); + break; + } + firstTime = std::min(firstTime, dataPoint.time); + LastTime = std::max(LastTime, dataPoint.time); + const auto temperature = dataPoint.value; + // sanity check + ++startPos[iSensor]; + if (temperature < 15 || temperature > 25) { + continue; + } + const auto& pos = dcs::Temperature::SensorPosition[iSensor + sensorOffset]; + xVals.emplace_back(pos.x); + xVals.emplace_back(pos.y); + temperatures.emplace_back(temperature); + } + } + if (firstTime < std::numeric_limits::max() && !temperatures.empty()) { + const bool fitOk = makeFit(fitter, nDim, xVals, temperatures); + if (!fitOk) { + continue; + } + auto& stat = stats.data.emplace_back(); + stat.time = (firstTime + LastTime) / 2; + stat.value.mean = fitter.GetParameter(0); + stat.value.gradX = fitter.GetParameter(1); + stat.value.gradY = fitter.GetParameter(2); + + // check if data contains outliers + const float maxDeltaT = 1; + const float meanTemp = fitter.GetParameter(0); + const bool isDataGood = std::all_of(temperatures.begin(), temperatures.end(), [meanTemp, maxDeltaT](double t) { return std::abs(t - meanTemp) < maxDeltaT; }); + + // do second iteration only in case of outliers + if (!isDataGood) { + std::vector xVals2; + std::vector temperatures2; + xVals2.reserve(xVals.size()); + temperatures2.reserve(temperatures.size()); + for (int i = 0; i < temperatures.size(); ++i) { + if (std::abs(temperatures[i] - meanTemp) < maxDeltaT) { + const int idx = 2 * i; + xVals2.emplace_back(xVals[idx]); + xVals2.emplace_back(xVals[idx + 1]); + temperatures2.emplace_back(temperatures[i]); + } + } + const bool fitOk2 = makeFit(fitter, nDim, xVals2, temperatures2); + if (fitOk2) { + stat.value.mean = fitter.GetParameter(0); + stat.value.gradX = fitter.GetParameter(1); + stat.value.gradY = fitter.GetParameter(2); + } + } + } + } +} + +void Pressure::fill(std::string_view sensor, const TimeStampType time, const DataType value) +{ + if (sensor == "CavernAtmosPressure") { + cavernAtmosPressure.fill(time, value); + } else if (sensor == "CavernAtmosPressure2") { + cavernAtmosPressure2.fill(time, value); + } else if (sensor == "SurfaceAtmosPressure") { + surfaceAtmosPressure.fill(time, value); + } else { + LOGP(warning, "Unknown pressure sensor {}", sensor); + } +} + +void Pressure::sortAndClean(float pMin, float pMax) +{ + cavernAtmosPressure.sortAndClean(); + cavernAtmosPressure2.sortAndClean(); + surfaceAtmosPressure.sortAndClean(); + + auto removeOutliers = [](auto& dataVec, auto minVal, auto maxVal) { + dataVec.erase( + std::remove_if(dataVec.begin(), dataVec.end(), + [minVal, maxVal](const auto& dp) { + return (dp.value < minVal || dp.value > maxVal); + }), + dataVec.end()); + }; + + removeOutliers(cavernAtmosPressure.data, pMin, pMax); + removeOutliers(cavernAtmosPressure2.data, pMin, pMax); + removeOutliers(surfaceAtmosPressure.data, pMin, pMax); +} + +void Pressure::clear() +{ + cavernAtmosPressure.clear(); + cavernAtmosPressure2.clear(); + surfaceAtmosPressure.clear(); + robustPressure = RobustPressure(); +} + +void Pressure::append(const Pressure& other) +{ + cavernAtmosPressure.append(other.cavernAtmosPressure); + cavernAtmosPressure2.append(other.cavernAtmosPressure2); + surfaceAtmosPressure.append(other.surfaceAtmosPressure); +} + +void fillBuffer(std::pair, std::vector>& buffer, const std::pair, std::vector>& values, TimeStampType tStart, const int minPoints) +{ + const auto itStartBuff = std::lower_bound(buffer.second.begin(), buffer.second.end(), tStart); + size_t idxStartBuffer = std::distance(buffer.second.begin(), itStartBuff); + if (buffer.first.size() - idxStartBuffer < minPoints) { + if (buffer.first.size() < minPoints) { + idxStartBuffer = 0; + } else { + idxStartBuffer = buffer.first.size() - minPoints; + } + } + + std::pair, std::vector> buffTmp; + auto& [buffVals, buffTimes] = buffTmp; + + // Preallocate enough capacity to avoid reallocations + buffVals.reserve(buffer.first.size() - idxStartBuffer + values.first.size()); + buffTimes.reserve(buffer.second.size() - idxStartBuffer + values.second.size()); + // Insert the kept part of the old buffer + buffVals.insert(buffVals.end(), buffer.first.begin() + idxStartBuffer, buffer.first.end()); + buffTimes.insert(buffTimes.end(), buffer.second.begin() + idxStartBuffer, buffer.second.end()); + // Insert the new values + buffVals.insert(buffVals.end(), values.first.begin(), values.first.end()); + buffTimes.insert(buffTimes.end(), values.second.begin(), values.second.end()); + + // this should not happen + if (!std::is_sorted(buffTimes.begin(), buffTimes.end())) { + LOGP(info, "Pressure buffer not sorted after filling - sorting it"); + std::vector idx(buffTimes.size()); + o2::math_utils::SortData(buffTimes, idx); + o2::math_utils::Reorder(buffVals, idx); + o2::math_utils::Reorder(buffTimes, idx); + } + + buffer = std::move(buffTmp); +} + +void Pressure::makeRobustPressure(TimeStampType timeInterval, TimeStampType timeIntervalRef, TimeStampType tStart, TimeStampType tEnd, const int nthreads) +{ + const auto surfaceAtmosPressurePair = surfaceAtmosPressure.getPairOfVector(); + const auto cavernAtmosPressurePair = cavernAtmosPressure.getPairOfVector(); + const auto cavernAtmosPressure2Pair = cavernAtmosPressure2.getPairOfVector(); + + // round to second + tStart = tStart / 1000 * 1000; + const TimeStampType tStartRef = (tStart - timeIntervalRef); + const int minPointsRef = 50; + fillBuffer(mCavernAtmosPressure1Buff, cavernAtmosPressurePair, tStartRef, minPointsRef); + fillBuffer(mCavernAtmosPressure2Buff, cavernAtmosPressure2Pair, tStartRef, minPointsRef); + fillBuffer(mSurfaceAtmosPressureBuff, surfaceAtmosPressurePair, tStartRef, minPointsRef); + + int nIntervals = std::round((tEnd - tStart) / timeInterval); + if (nIntervals == 0) { + nIntervals = 1; // at least one interval + } + std::vector times; + times.reserve(nIntervals); + for (int i = 0; i < nIntervals; ++i) { + times.emplace_back(tStart + (i + 0.5) * timeInterval); + } + + /// minimum number of points in the interval - otherwise use the n closest points + const int minPoints = 4; + const auto cavernAtmosPressureStats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure1Buff.second, mCavernAtmosPressure1Buff.first, times, timeInterval, nthreads, minPoints, minPoints); + const auto cavernAtmosPressure2Stats = o2::math_utils::getRollingStatistics(mCavernAtmosPressure2Buff.second, mCavernAtmosPressure2Buff.first, times, timeInterval, nthreads, minPoints, minPoints); + const auto surfaceAtmosPressureStats = o2::math_utils::getRollingStatistics(mSurfaceAtmosPressureBuff.second, mSurfaceAtmosPressureBuff.first, times, timeInterval, nthreads, minPoints, minPoints); + + // subtract the moving median values from the different sensors if they are ok + std::pair, std::vector> cavernAtmosPressure12; + std::pair, std::vector> cavernAtmosPressure1S; + std::pair, std::vector> cavernAtmosPressure2S; + cavernAtmosPressure12.first.reserve(nIntervals); + cavernAtmosPressure1S.first.reserve(nIntervals); + cavernAtmosPressure2S.first.reserve(nIntervals); + cavernAtmosPressure12.second.reserve(nIntervals); + cavernAtmosPressure1S.second.reserve(nIntervals); + cavernAtmosPressure2S.second.reserve(nIntervals); + + for (int i = 0; i < nIntervals; i++) { + // coarse check if data is close by + const int maxDist = 600 * 1000; + const bool cavernOk = (cavernAtmosPressureStats.median[i] > 0) && (cavernAtmosPressureStats.closestDistanceL[i] < maxDist) && (cavernAtmosPressureStats.closestDistanceR[i] < maxDist); + const bool cavern2Ok = (cavernAtmosPressure2Stats.median[i] > 0) && (cavernAtmosPressure2Stats.closestDistanceL[i] < maxDist) && (cavernAtmosPressure2Stats.closestDistanceR[i] < maxDist); + const bool surfaceOk = (surfaceAtmosPressureStats.median[i] > 0) && (surfaceAtmosPressureStats.closestDistanceL[i] < maxDist) && (surfaceAtmosPressureStats.closestDistanceR[i] < maxDist); + + if (cavernOk && cavern2Ok) { + cavernAtmosPressure12.first.emplace_back(cavernAtmosPressureStats.median[i] - cavernAtmosPressure2Stats.median[i]); + cavernAtmosPressure12.second.emplace_back(times[i]); + } + if (cavernOk && surfaceOk) { + cavernAtmosPressure1S.first.emplace_back(cavernAtmosPressureStats.median[i] - surfaceAtmosPressureStats.median[i]); + cavernAtmosPressure1S.second.emplace_back(times[i]); + } + if (cavern2Ok && surfaceOk) { + cavernAtmosPressure2S.first.emplace_back(cavernAtmosPressure2Stats.median[i] - surfaceAtmosPressureStats.median[i]); + cavernAtmosPressure2S.second.emplace_back(times[i]); + } + } + + fillBuffer(mPressure12Buff, cavernAtmosPressure12, tStartRef, minPointsRef); + fillBuffer(mPressure1SBuff, cavernAtmosPressure1S, tStartRef, minPointsRef); + fillBuffer(mPressure2SBuff, cavernAtmosPressure2S, tStartRef, minPointsRef); + + // get long term median of diffs - this is used for normalization of the pressure values - + const auto cavernAtmosPressure12Stats = o2::math_utils::getRollingStatistics(mPressure12Buff.second, mPressure12Buff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + const auto cavernAtmosPressure1SStats = o2::math_utils::getRollingStatistics(mPressure1SBuff.second, mPressure1SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + const auto cavernAtmosPressure2SStats = o2::math_utils::getRollingStatistics(mPressure2SBuff.second, mPressure2SBuff.first, times, timeIntervalRef, nthreads, 3, minPointsRef); + + // calculate diffs of median values + const float maxDist = 20 * timeInterval; + const float maxDiff = 0.2; + std::pair, std::vector> robustPressureTmp; + robustPressureTmp.first.reserve(nIntervals); + robustPressureTmp.second.reserve(nIntervals); + std::vector isOk(nIntervals); + + for (int i = 0; i < nIntervals; ++i) { + // difference beween pressure values corrected for the long term median + const float delta12 = cavernAtmosPressureStats.median[i] - cavernAtmosPressure2Stats.median[i] - cavernAtmosPressure12Stats.median[i]; + const float delta1S = cavernAtmosPressureStats.median[i] - surfaceAtmosPressureStats.median[i] - cavernAtmosPressure1SStats.median[i]; + const float delta2S = cavernAtmosPressure2Stats.median[i] - surfaceAtmosPressureStats.median[i] - cavernAtmosPressure2SStats.median[i]; + + const auto distCavernAtmosPressureL = cavernAtmosPressureStats.closestDistanceL[i]; + const auto distCavernAtmosPressure2L = cavernAtmosPressure2Stats.closestDistanceL[i]; + const auto distSurfaceAtmosPressureL = surfaceAtmosPressureStats.closestDistanceL[i]; + const auto distCavernAtmosPressureR = cavernAtmosPressureStats.closestDistanceR[i]; + const auto distCavernAtmosPressure2R = cavernAtmosPressure2Stats.closestDistanceR[i]; + const auto distSurfaceAtmosPressureR = surfaceAtmosPressureStats.closestDistanceR[i]; + + // check if data is ok + const bool cavernDistOk = (cavernAtmosPressureStats.median[i] > 0) && ((distCavernAtmosPressureL < maxDist) || (distCavernAtmosPressureR < maxDist)); + const bool cavern2DistOk = (cavernAtmosPressure2Stats.median[i] > 0) && ((distCavernAtmosPressure2L < maxDist) || (distCavernAtmosPressure2R < maxDist)); + const bool surfaceDistOk = (surfaceAtmosPressureStats.median[i] > 0) && ((distSurfaceAtmosPressureL < maxDist) || (distSurfaceAtmosPressureR < maxDist)); + const bool onlyOneSensor = (cavernDistOk + cavern2DistOk + surfaceDistOk) == 1; // check if only 1 sensor exists, if so use that sensor + + uint8_t maskIsOkTmp = 0; + const int cavernBit = 0; // val 1 + const int cavern2Bit = 1; // val 2 + const int surfaceBit = 2; // val 4 + + // check if ratio sensor 1 and 2 are good + // maskIsOkTmp = 3 + if (((std::abs(delta12) < maxDiff) && (cavernDistOk && cavern2DistOk)) || onlyOneSensor) { + if (cavernDistOk) { + maskIsOkTmp |= (1 << cavernBit); + } + if (cavern2DistOk) { + maskIsOkTmp |= (1 << cavern2Bit); + } + } + + // check if ratio sensor 1 and surface are good + // maskIsOkTmp = 5 + if ((std::abs(delta1S) < maxDiff) && ((cavernDistOk && surfaceDistOk)) || onlyOneSensor) { + if (cavernDistOk) { + maskIsOkTmp |= (1 << cavernBit); + } + if (surfaceDistOk) { + maskIsOkTmp |= (1 << surfaceBit); + } + } + + // check if ratio sensor 2 and surface are good + // maskIsOkTmp = 6 + if ((std::abs(delta2S) < maxDiff) && ((cavern2DistOk && surfaceDistOk)) || onlyOneSensor) { + if (cavern2DistOk) { + maskIsOkTmp |= (1 << cavern2Bit); + } + if (surfaceDistOk) { + maskIsOkTmp |= (1 << surfaceBit); + } + } + + // calculate robust pressure + float pressure = 0; + int pressureCount = 0; + if ((maskIsOkTmp >> cavernBit) & 1) { + pressure += cavernAtmosPressureStats.median[i]; + pressureCount++; + } + + if ((maskIsOkTmp >> cavern2Bit) & 1) { + pressure += cavernAtmosPressure2Stats.median[i] + cavernAtmosPressure12Stats.median[i]; + pressureCount++; + } + + if ((maskIsOkTmp >> surfaceBit) & 1) { + pressure += surfaceAtmosPressureStats.median[i] + cavernAtmosPressure1SStats.median[i]; + pressureCount++; + } + + isOk[i] = maskIsOkTmp; + if (pressureCount > 0) { + pressure /= pressureCount; + robustPressureTmp.first.emplace_back(pressure); + robustPressureTmp.second.emplace_back(times[i]); + } + } + + fillBuffer(mRobPressureBuff, robustPressureTmp, tStartRef, minPointsRef); + + RobustPressure& pOut = robustPressure; + pOut.surfaceAtmosPressure = std::move(surfaceAtmosPressureStats); + pOut.cavernAtmosPressure2 = std::move(cavernAtmosPressure2Stats); + pOut.cavernAtmosPressure = std::move(cavernAtmosPressureStats); + pOut.cavernAtmosPressure12 = std::move(cavernAtmosPressure12Stats); + pOut.cavernAtmosPressure1S = std::move(cavernAtmosPressure1SStats); + pOut.cavernAtmosPressure2S = std::move(cavernAtmosPressure2SStats); + pOut.isOk = std::move(isOk); + pOut.robustPressure = o2::math_utils::getRollingStatistics(mRobPressureBuff.second, mRobPressureBuff.first, times, timeInterval, nthreads, 1, 5).median; + pOut.time = std::move(times); + pOut.timeInterval = timeInterval; + pOut.timeIntervalRef = timeIntervalRef; + pOut.maxDist = maxDist; + pOut.maxDiff = maxDiff; +} + +void Pressure::setAliases(TTree* tree) +{ + tree->SetAlias("cavernDistOk", "robustPressure.cavernAtmosPressure.median>0 && (robustPressure.cavernAtmosPressure.closestDistanceRSetAlias("cavern2DistOk", "robustPressure.cavernAtmosPressure2.median>0 && (robustPressure.cavernAtmosPressure2.closestDistanceRSetAlias("surfaceDistOk", "robustPressure.surfaceAtmosPressure.median>0 && (robustPressure.surfaceAtmosPressure.closestDistanceRSetAlias("onlyOneSensor", "(cavernDistOk + cavern2DistOk + surfaceDistOk) == 1"); + tree->SetAlias("delta12", "robustPressure.cavernAtmosPressure.median - robustPressure.cavernAtmosPressure2.median - robustPressure.cavernAtmosPressure12.median"); + tree->SetAlias("delta1S", "robustPressure.cavernAtmosPressure.median - robustPressure.surfaceAtmosPressure.median - robustPressure.cavernAtmosPressure1S.median"); + tree->SetAlias("delta2S", "robustPressure.surfaceAtmosPressure.median - robustPressure.cavernAtmosPressure2.median - robustPressure.cavernAtmosPressure2S.median"); + tree->SetAlias("delta12_Ok", "abs(delta12)SetAlias("delta1S_Ok", "abs(delta1S)SetAlias("delta2S_Ok", "abs(delta2S) + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CTFHeader, 26, uint32_t> + ; #pragma link C++ enum o2::tpc::StatisticsType; #pragma link C++ class o2::tpc::TrackCuts + ; #pragma link C++ class o2::tpc::KrCluster + ; @@ -69,5 +69,12 @@ #pragma link C++ class o2::tpc::dcs::DataPoint < o2::tpc::dcs::HV::StackState> + ; #pragma link C++ class o2::tpc::dcs::DataPointVector < o2::tpc::dcs::HV::StackState> + ; #pragma link C++ class o2::tpc::dcs::Gas + ; +#pragma link C++ class o2::tpc::dcs::Pressure + ; +#pragma link C++ class o2::tpc::dcs::RobustPressure + ; +#pragma link C++ class o2::tpc::PIDResponse + ; +#pragma link C++ class o2::tpc::TriggerWordDLBZS + ; +#pragma link C++ class o2::tpc::TriggerInfoDLBZS + ; +#pragma link C++ class std::vector < o2::tpc::TriggerInfoDLBZS> + ; +#pragma link C++ class o2::tpc::AltroSyncSignal + ; #endif diff --git a/DataFormats/Detectors/TPC/src/TrackCuts.cxx b/DataFormats/Detectors/TPC/src/TrackCuts.cxx index 43039e25734b0..111f3c3da8920 100644 --- a/DataFormats/Detectors/TPC/src/TrackCuts.cxx +++ b/DataFormats/Detectors/TPC/src/TrackCuts.cxx @@ -49,5 +49,8 @@ bool TrackCuts::goodTrack(o2::tpc::TrackTPC const& track) if (dEdx < mdEdxMin) { return false; } + if ((std::abs(track.getOuterParam().getZ()) - std::abs(track.getZ())) < -10) { + return false; + } return true; } diff --git a/DataFormats/Detectors/TRD/CMakeLists.txt b/DataFormats/Detectors/TRD/CMakeLists.txt index fa5d9121527be..35712caf5bfb9 100644 --- a/DataFormats/Detectors/TRD/CMakeLists.txt +++ b/DataFormats/Detectors/TRD/CMakeLists.txt @@ -13,6 +13,8 @@ o2_add_library(DataFormatsTRD SOURCES src/TriggerRecord.cxx src/LinkRecord.cxx src/AngularResidHistos.cxx + src/GainCalibHistos.cxx + src/T0FitHistos.cxx src/DcsCcdbObjects.cxx src/Tracklet64.cxx src/RawData.cxx @@ -35,14 +37,19 @@ o2_target_root_dictionary(DataFormatsTRD include/DataFormatsTRD/CalibratedTracklet.h include/DataFormatsTRD/DcsCcdbObjects.h include/DataFormatsTRD/AngularResidHistos.h + include/DataFormatsTRD/GainCalibHistos.h + include/DataFormatsTRD/T0FitHistos.h include/DataFormatsTRD/HelperMethods.h include/DataFormatsTRD/Hit.h include/DataFormatsTRD/Digit.h include/DataFormatsTRD/KrCluster.h include/DataFormatsTRD/KrClusterTriggerRecord.h include/DataFormatsTRD/NoiseCalibration.h + include/DataFormatsTRD/PHData.h + include/DataFormatsTRD/RawDataStats.h include/DataFormatsTRD/CTF.h include/DataFormatsTRD/CalVdriftExB.h + include/DataFormatsTRD/CalGain.h include/DataFormatsTRD/CalT0.h include/DataFormatsTRD/SignalArray.h include/DataFormatsTRD/CompressedDigit.h @@ -63,4 +70,3 @@ o2_add_test(RawData ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage LABELS trd ) - diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h index bf935866564c0..4380c79cc48e8 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/AngularResidHistos.h @@ -38,7 +38,6 @@ class AngularResidHistos size_t getNEntries() const { return mNEntriesTotal; } void fill(const AngularResidHistos& input); - void fill(const gsl::span input); // dummy! void merge(const AngularResidHistos* prev); void print(); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h new file mode 100644 index 0000000000000..b4e64db094a5c --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CalGain.h +/// \brief Object with MPV dEdx values per chamber to be written into the CCDB + +#ifndef ALICEO2_CALGAIN_H +#define ALICEO2_CALGAIN_H + +#include "DataFormatsTRD/Constants.h" +#include "Rtypes.h" +#include + +namespace o2 +{ +namespace trd +{ + +class CalGain +{ + public: + CalGain() = default; + CalGain(const CalGain&) = default; + ~CalGain() = default; + + void setMPVdEdx(int iDet, float mpv) { mMPVdEdx[iDet] = mpv; } + + float getMPVdEdx(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || isGoodGain(iDet)) + return mMPVdEdx[iDet]; + else { + if (TMath::Abs(mMeanGain + 999.) < 1e-6) + mMeanGain = getAverageGain(); + return mMeanGain; + } + } + + float getAverageGain() const + { + float averageGain = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodGain(iDet)) { + // The chamber has correct calibration + ngood++; + averageGain += mMPVdEdx[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::MPVDEDXDEFAULT; + } + averageGain /= ngood; + return averageGain; + } + + bool isGoodGain(int iDet) const + { + if (TMath::Abs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) + return true; + else + return false; + } + + private: + std::array mMPVdEdx{}; ///< Most probable value of dEdx distribution per TRD chamber + mutable float mMeanGain{-999.}; ///! average gain, calculated only once + + ClassDefNV(CalGain, 2); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_CALGAIN_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h index 30474888a7424..69fc23646c912 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h @@ -32,13 +32,38 @@ class CalT0 ~CalT0() = default; void setT0(int iDet, float t0) { mT0[iDet] = t0; } + void setT0av(float t0) { mT0av = t0; } float getT0(int iDet) const { return mT0[iDet]; } + // getT0av() returns the average T0 obtained by fitting the data from all chambers combined + float getT0av() const { return mT0av; } + // calcT0av() returns the average T0 from all individual chambers + float calcT0av() const + { + if (mT0.size() == 0) { + return -1; + } + float sum = 0; + int counts = 0; + for (int iDet = 0; iDet < constants::MAXCHAMBER; ++iDet) { + if (mT0[iDet] > -5) { + sum += mT0[iDet]; + ++counts; + } + } + + if (counts > 0) { + return sum / counts; + } else { + return -2; + } + } private: std::array mT0{}; ///< calibrated T0 per TRD chamber + float mT0av{-1}; ///< average T0 obtained from fitting the PH data from all chambers combined - ClassDefNV(CalT0, 1); + ClassDefNV(CalT0, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h index bad9dcfef4e37..65981e928fb39 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h @@ -34,14 +34,102 @@ class CalVdriftExB void setVdrift(int iDet, float vd) { mVdrift[iDet] = vd; } void setExB(int iDet, float exb) { mExB[iDet] = exb; } - float getVdrift(int iDet) const { return mVdrift[iDet]; } - float getExB(int iDet) const { return mExB[iDet]; } + float getVdrift(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mVdrift[iDet]; + else { + if (TMath::Abs(mMeanVdrift + 999.) < 1e-6) + mMeanVdrift = getAverageVdrift(); + return mMeanVdrift; + } + } + float getExB(int iDet, bool defaultAvg = true) const + { + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mExB[iDet]; + else { + if (TMath::Abs(mMeanExB + 999.) < 1e-6) + mMeanExB = getAverageExB(); + return mMeanExB; + } + } + + float getAverageVdrift() const + { + float averageVdrift = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageVdrift += mVdrift[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::VDRIFTDEFAULT; + } + averageVdrift /= ngood; + return averageVdrift; + } + + float getAverageExB() const + { + float averageExB = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageExB += mExB[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::EXBDEFAULT; + } + averageExB /= ngood; + return averageExB; + } + + bool isGoodExB(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && + TMath::Abs(mExB[iDet] - constants::EXBMIN) > 0.01 && + TMath::Abs(mExB[iDet] - constants::EXBMAX) > 0.01) + return true; + else + return false; + } + + bool isGoodVdrift(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) + return true; + else + return false; + } private: std::array mVdrift{}; ///< calibrated drift velocity per TRD chamber std::array mExB{}; ///< calibrated Lorentz angle per TRD chamber + mutable float mMeanVdrift{-999.}; ///! average drift velocity, calculated only once + mutable float mMeanExB{-999.}; ///! average lorentz angle, calculated only once - ClassDefNV(CalVdriftExB, 1); + ClassDefNV(CalVdriftExB, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h index 8185bf2718629..9a4da1024e251 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -41,6 +41,7 @@ constexpr int TRDLINKID = 15; ///< hard coded link id, specific to TRD constexpr int NCOLUMN = 144; ///< the number of pad columns for each chamber constexpr int NROWC0 = 12; ///< the number of pad rows for chambers of type C0 (installed in stack 2) constexpr int NROWC1 = 16; ///< the number of pad rows for chambers of type C1 (installed in stacks 0, 1, 3 and 4) +constexpr int FIRSTROW[NSTACK] = {0, 16, 32, 44, 60}; ///< first pad row for each stack constexpr int NMCMROB = 16; ///< the number of MCMs per ROB constexpr int NMCMHCMAX = 64; ///< the maximum number of MCMs for one half chamber (C1 type) @@ -74,7 +75,14 @@ constexpr int TIMEBINS = 30; ///< the number of time bins constexpr float MAXIMPACTANGLE = 25.f; ///< the maximum impact angle for tracks relative to the TRD detector plane to be considered for vDrift and ExB calibration constexpr int NBINSANGLEDIFF = 25; ///< the number of bins for the track angle used for the vDrift and ExB calibration based on the tracking constexpr double VDRIFTDEFAULT = 1.546; ///< default value for vDrift +constexpr double VDRIFTMIN = 0.4; ///< min value for vDrift +constexpr double VDRIFTMAX = 2.0; ///< max value for vDrift constexpr double EXBDEFAULT = 0.0; ///< default value for LorentzAngle +constexpr double EXBMIN = -0.4; ///< min value for LorentzAngle +constexpr double EXBMAX = 0.4; ///< max value for LorentzAngle +constexpr int NBINSGAINCALIB = 320; ///< number of bins in the charge (Q0+Q1+Q2) histogram for gain calibration +constexpr float MPVDEDXDEFAULT = 42.; ///< default Most Probable Value of TRD dEdx +constexpr float T0DEFAULT = 1.2; ///< default value for t0 // array size to store incoming half cru payload. constexpr int HBFBUFFERMAX = 1048576; ///< max buffer size for data read from a half cru, (all events) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h index b76732f80c5d4..9eba0318a5a13 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h @@ -59,7 +59,7 @@ class Digit Digit(int det, int row, int pad, ArrayADC adc, int phase = 0); Digit(int det, int row, int pad); // add adc data and pretrigger phase in a separate step Digit(int det, int rob, int mcm, int channel, ArrayADC adc, int phase = 0); - Digit(int det, int rob, int mcm, int channel); // add adc data in a seperate step + Digit(int det, int rob, int mcm, int channel, int phase = 0); // add adc data // Copy Digit(const Digit&) = default; @@ -74,22 +74,27 @@ class Digit void setDetector(int det) { mDetector = ((mDetector & 0xf000) | (det & 0xfff)); } void setADC(ArrayADC const& adc) { mADC = adc; } void setADC(const gsl::span& adc) { std::copy(adc.begin(), adc.end(), mADC.begin()); } - void setPreTrigPhase(int phase) { mDetector = (((phase & 0xf) << 12) | (mDetector & 0xfff)); } + // set the trigger phase make sure it is mapped to 2 bits as it can only have 4 valid numbers shifted 0,3,6,9 or 1,4,7,10 etc. + void setPreTrigPhase(int phase); // Get methods int getDetector() const { return mDetector & 0xfff; } + int getDetectorInFull() const { return mDetector; } // return the entire mDetector 16 bits, so far only for CTF encoding. int getHCId() const { return (mDetector & 0xfff) * 2 + (mROB % 2); } int getPadRow() const { return HelperMethods::getPadRowFromMCM(mROB, mMCM); } int getPadCol() const { return HelperMethods::getPadColFromADC(mROB, mMCM, mChannel); } int getROB() const { return mROB; } int getMCM() const { return mMCM; } + int getMCMCol() const { return (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); } int getChannel() const { return mChannel; } int getPreTrigPhase() const { return ((mDetector >> 12) & 0xf); } bool isSharedDigit() const; + bool isNeighbour(const Digit& other) const; ArrayADC const& getADC() const { return mADC; } ADC_t getADCsum() const { return std::accumulate(mADC.begin(), mADC.end(), (ADC_t)0); } // returns the max ADC value and sets idx to the time bin with the largest ADC value ADC_t getADCmax(int& idx) const; + ADC_t getADCval(int tb) const { return mADC[tb]; } bool operator==(const Digit& o) const { diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h new file mode 100644 index 0000000000000..6142646950969 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/GainCalibHistos.h @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GainCalibHistos.h +/// \brief Class to store the TRD dEdx distribution for each TRD chamber + +#ifndef ALICEO2_GAINCALIBHISTOS_H +#define ALICEO2_GAINCALIBHISTOS_H + +#include "DataFormatsTRD/Constants.h" +#include "Rtypes.h" +#include + +namespace o2 +{ +namespace trd +{ + +class GainCalibHistos +{ + public: + GainCalibHistos() = default; + GainCalibHistos(const GainCalibHistos&) = default; + ~GainCalibHistos() = default; + void reset(); + void init(); + auto getHistogramEntry(int index) const { return mdEdxEntries[index]; } + auto getNEntries() const { return mNEntriesTot; } + + void fill(const std::vector& input); + void merge(const GainCalibHistos* prev); + void print(); + + private: + std::vector mdEdxEntries{}; ///< dEdx histograms + size_t mNEntriesTot{0}; + bool mInitialized{false}; + + ClassDefNV(GainCalibHistos, 1); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_GAINCALIBHISTOS_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h index 4468528865d3b..ecab7f49b92a2 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h @@ -14,6 +14,8 @@ #include "DataFormatsTRD/Constants.h" #include +#include +#include namespace o2 { @@ -54,12 +56,17 @@ struct HelperMethods { printf("%02i_%i_%i\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER); } - static void printSectorStackLayerSide(int hcid) + static std::string getSectorStackLayerSide(int hcid) { - // for a given half-chamber number prints SECTOR_STACK_LAYER_side int det = hcid / 2; std::string side = (hcid % 2 == 0) ? "A" : "B"; - printf("%02i_%i_%i%s\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER, side.c_str()); + return fmt::format("{}_{}_{}{}", getSector(det), getStack(det), getLayer(det), side); + } + + static void printSectorStackLayerSide(int hcid) + { + // for a given half-chamber number prints SECTOR_STACK_LAYER_side + printf("%s\n", getSectorStackLayerSide(hcid).c_str()); } static int getPadColFromADC(int irob, int imcm, int iadc) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h new file mode 100644 index 0000000000000..fc46ca0207993 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_TRD_PHDATA_H_ +#define ALICEO2_TRD_PHDATA_H_ + +#include +#include "Rtypes.h" + +namespace o2::trd +{ + +/* + This data type is used to send around the information required to fill PH plots per chamber + + |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------------------------------------------------------- + |type |nNeighb | time bin | detector number | ADC sum for all neigbours | + ------------------------------------------------------------------------------------------------- +*/ + +class PHData +{ + public: + enum Origin : uint8_t { + ITSTPCTRD, + TPCTRD, + TRACKLET, + OTHER + }; + + PHData() = default; + PHData(int adc, int det, int tb, int nb, int type) { set(adc, det, tb, nb, type); } + + void set(int adc, int det, int tb, int nb, int type) + { + mData = ((type & 0x3) << 30) | ((nb & 0x7) << 27) | ((tb & 0x1f) << 22) | ((det & 0x3ff) << 12) | (adc & 0xfff); + } + + // the ADC sum for given time bin for up to three neighbours + int getADC() const { return mData & 0xfff; } + // the TRD detector number + int getDetector() const { return (mData >> 12) & 0x3ff; } + // the given time bin + int getTimebin() const { return (mData >> 22) & 0x1f; } + // number of neighbouring digits for which the ADC is accumulated + int getNneighbours() const { return (mData >> 27) & 0x7; } + // the origin of this point: digit on ITS-TPC-TRD track, ... (see enum Origin above) + int getType() const { return (mData >> 30) & 0x3; } + + private: + uint32_t mData{0}; // see comment above for data content + + ClassDefNV(PHData, 1); +}; + +/* + This data type is used to send around the information required to fill PH plots per chamber + + |19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------------------- + |type |nNeighb | time bin | detector number | + ------------------------------------------------------------- +*/ +/* + This data type is used to send around the information required to fill PH plots per chamber + + |15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00| + ------------------------------------------------ + | ADC sum for all neigbours | + ------------------------------------------------ +*/ + +class PHDataHD +{ + public: + enum Origin : uint8_t { + ITSTPCTRD, + TPCTRD, + TRACKLET, + OTHER + }; + + PHDataHD() = default; + PHDataHD(int adc, int det, int tb, int nb, int type) { set(adc, det, tb, nb, type); } + + void set(int adc, int det, int tb, int nb, int type) + { + mDetector = det; + mTimeBin = tb; + mType = type; + mNNeighbours = nb; + mADC = adc; + } + + // the ADC sum for given time bin for up to three neighbours + int getADC() const { return mADC; } + // the TRD detector number + int getDetector() const { return mDetector; } + // the given time bin + int getTimebin() const { return mTimeBin; } + // number of neighbouring digits for which the ADC is accumulated + int getNNeighbours() const { return mNNeighbours; } + // the origin of this point: digit on ITS-TPC-TRD track, ... (see enum Origin above) + int getType() const { return mType; } + + private: + uint16_t mDetector{0}; + uint8_t mTimeBin{0}; + uint8_t mType{0}; + uint8_t mNNeighbours{0}; + uint16_t mADC{0}; + + ClassDefNV(PHDataHD, 1); +}; +} // namespace o2::trd + +#endif // ALICEO2_TRD_PHDATA_H_ diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h index cbb62d8f1e164..d99a5be14db4a 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PID.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace o2 { @@ -28,46 +29,71 @@ namespace trd enum class PIDPolicy : unsigned int { // Classical Algorithms LQ1D = 0, ///< 1-Dimensional Likelihood model + LQ2D, ///< 2-Dimensional Likelihood model LQ3D, ///< 3-Dimensional Likelihood model +#ifdef TRDPID_WITH_ONNX // ML models XGB, ///< XGBOOST + PY, ///< Pytorch +#endif // Do not add anything after this! NMODELS, ///< Count of all models - Test, ///< Load object for testing Dummy, ///< Dummy object outputting -1.f DEFAULT = Dummy, ///< The default option }; +inline std::ostream& operator<<(std::ostream& os, const PIDPolicy& policy) +{ + std::string name; + switch (policy) { + case PIDPolicy::LQ1D: + name = "LQ1D"; + break; + case PIDPolicy::LQ2D: + name = "LQ2D"; + break; + case PIDPolicy::LQ3D: + name = "LQ3D"; + break; +#ifdef TRDPID_WITH_ONNX + case PIDPolicy::XGB: + name = "XGBoost"; + break; + case PIDPolicy::PY: + name = "PyTorch"; + break; +#endif + case PIDPolicy::Dummy: + name = "Dummy"; + break; + default: + name = "Default"; + } + os << name; + return os; +} + /// Transform PID policy from string to enum. static const std::unordered_map PIDPolicyString{ // Classical Algorithms {"LQ1D", PIDPolicy::LQ1D}, + {"LQ2D", PIDPolicy::LQ2D}, {"LQ3D", PIDPolicy::LQ3D}, +#ifdef TRDPID_WITH_ONNX // ML models {"XGB", PIDPolicy::XGB}, + {"PY", PIDPolicy::PY}, +#endif // General - {"TEST", PIDPolicy::Test}, {"DUMMY", PIDPolicy::Dummy}, // Default {"default", PIDPolicy::DEFAULT}, }; -/// Transform PID policy from string to enum. -static const char* PIDPolicyEnum[] = { - "LQ1D", - "LQ3D", - "XGBoost", - "NMODELS", - "Test", - "Dummy", - "default(=TODO)"}; - -using PIDValue = float; - } // namespace trd } // namespace o2 diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h index 0bc9ec474529e..f7f5f96208191 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawData.h @@ -447,10 +447,10 @@ void setHalfCRUHeaderLinkSizeAndFlags(HalfCRUHeader& cruhead, int link, int size DigitMCMADCMask constructBlankADCMask(); uint32_t getHalfCRULinkInfo(const HalfCRUHeader& cruhead, const uint32_t link, const bool data); -uint32_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link); -uint32_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link); -void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags); -void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes); +uint8_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link); +uint16_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link); +void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags); +void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes); std::ostream& operator<<(std::ostream& stream, const TrackletHCHeader& halfchamberheader); std::ostream& operator<<(std::ostream& stream, const TrackletMCMHeader& tracklmcmhead); std::ostream& operator<<(std::ostream& stream, const TrackletMCMData& trackletmcmdata); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h index f75c684a37fd8..04ebd9c7eea3a 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h @@ -16,14 +16,13 @@ #ifndef O2_TRD_RAWDATASTATS #define O2_TRD_RAWDATASTATS -#include "TObject.h" +#include "Rtypes.h" #include #include #include -#include #include -#include #include "DataFormatsTRD/Constants.h" +#include "CommonDataFormat/TFIDInfo.h" namespace o2::trd { @@ -120,22 +119,14 @@ enum OptionBits { TRDIgnore2StageTrigger, TRDGenerateStats, TRDOnlyCalibrationTriggerBit, - TRDDisableRootOutputBit + TRDSortDigits, + TRDLinkStats }; // this is currently 16 options, the array is 16, if you add here you need to change the 16; -//Data to be stored and accumulated on an event basis. -//events are spread out with in the data coming in with a halfcruheader per event, per ... half cru. -//this is looked up via the interaction record (orbit and bunchcrossing). -//this permits averaging in the data that gets senton per timeframe -struct TRDDataCountersPerEvent { - TRDDataCountersPerEvent() = default; - double mTimeTaken = 0.; // time take to process an event (summed trackletparsing and digitparsing) parts not accounted for. - double mTimeTakenForDigits = 0.; // time take to process tracklet data blocks [us]. - double mTimeTakenForTracklets = 0.; // time take to process digit data blocks [us]. - uint64_t mWordsRead = 0; // words read in - uint64_t mWordsRejected = 0; // words skipped for various reasons. - uint16_t mTrackletsFound = 0; // tracklets found in the event - uint16_t mDigitsFound = 0; // digits found in the event +struct DataCountersPerTrigger { + std::array mLinkWords{}; + std::array mLinkErrorFlag{}; + ClassDefNV(DataCountersPerTrigger, 1); }; //Data to be stored on a timeframe basis to then be sent as a message to be ultimately picked up by qc. @@ -143,39 +134,42 @@ struct TRDDataCountersPerEvent { class TRDDataCountersPerTimeFrame { public: - std::array mLinkErrorFlag{}; // status of the error flags for this timeframe, 8bit values from cru halfchamber header. - std::array mLinkNoData; // Link had no data or was not present. - std::array mLinkWords{}; // units of 256bits, read from the cru half chamber header - std::array mLinkWordsRead{}; // units of 32 bits the data words read before dumping or finishing - std::array mLinkWordsRejected{}; // units of 32 bits the data dumped due to some or other error + std::array mLinkErrorFlag{}; // status of the error flags for this timeframe, 8bit values from cru halfchamber header. + std::array mLinkNoData{}; // Link had no data or was not present. + std::array mLinkWords{}; // units of 256bits, read from the cru half chamber header + std::array mLinkWordsRead{}; // units of 32 bits the data words read before dumping or finishing + std::array mLinkWordsRejected{}; // units of 32 bits the data dumped due to some or other error + std::array mParsingOK{}; // count how often given link could be parsed without any errors std::array mParsingErrors{}; // errors in parsing, indexed by enum above of ParsingErrors - std::array mParsingErrorsByLink{}; // errors in parsing, indexed by enum above of ParsingErrors - uint16_t mDigitsPerEvent; // average digits found per event in this timeframe, ignoring the no digit events where there is no calibration trigger. - uint16_t mTrackletsPerEvent; // average tracklets found per event in this timeframe - double mTimeTaken; // time taken to process the entire timeframe [ms]. - double mTimeTakenForDigits; // time take to process tracklet data blocks [ms]. - double mTimeTakenForTracklets; // time take to process digit data blocks [ms]. + std::vector mParsingErrorsByLink{}; // each entry is for a single parsing error on a given link (HCID * number of Errors + error index) + float mTimeTaken; // time taken to process all half-CRU data blocks combined [us]. + float mTimeTakenForDigits; // time take to process tracklet data blocks [us]. + float mTimeTakenForTracklets; // time take to process digit data blocks [us]. uint32_t mDigitsFound; // digits found in the time frame. uint32_t mTrackletsFound; // tracklets found in the time frame. - std::array mDataFormatRead{}; // We just keep the major version number + uint16_t mNTriggersCalib; // number of triggers with digit readout + uint16_t mNTriggersTotal; // total number of triggers + std::array mDataFormatRead{}; // We just keep the major version number + o2::dataformats::TFIDInfo mTFIDInfo; // keep track of TF ID void clear() { mLinkNoData.fill(0); mLinkWords.fill(0); mLinkWordsRead.fill(0); mLinkWordsRejected.fill(0); + mParsingOK.fill(0); mParsingErrors.fill(0); - mParsingErrorsByLink.fill(0); - mDigitsPerEvent = 0; - mTrackletsPerEvent = 0; + mParsingErrorsByLink.clear(); mTimeTaken = 0; mTimeTakenForDigits = 0; mTimeTakenForTracklets = 0; mDigitsFound = 0; - mTrackletsFound = 0; //tracklets found in timeframe. + mTrackletsFound = 0; + mNTriggersCalib = 0; + mNTriggersTotal = 0; mDataFormatRead.fill(0); }; - ClassDefNV(TRDDataCountersPerTimeFrame, 1); // primarily for serialisation so we can send this as a message in o2 + ClassDefNV(TRDDataCountersPerTimeFrame, 3); // primarily for serialisation so we can send this as a message in o2 }; } // namespace o2::trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h index 21a079ec0df77..032dd4162a785 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RecoInputContainer.h @@ -27,7 +27,7 @@ #include "Framework/InputRecord.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include #include @@ -88,7 +88,7 @@ inline void RecoInputContainer::fillGPUIOPtr(o2::gpu::GPUTrackingInOutPointers* ptrs->nTRDTriggerRecords = mNTriggerRecords; ptrs->trdTriggerTimes = &(trdTriggerTimes[0]); ptrs->trdTrackletIdxFirst = &(trdTriggerIndices[0]); - ptrs->trdTrigRecMask = reinterpret_cast(mTrigRecMask.data()); + ptrs->trdTrigRecMask = reinterpret_cast(mTrigRecMask.data()); ptrs->nTRDTracklets = mNTracklets; ptrs->trdTracklets = reinterpret_cast(mTracklets.data()); ptrs->trdSpacePoints = reinterpret_cast(mSpacePoints.data()); diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h new file mode 100644 index 0000000000000..93448696025c9 --- /dev/null +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/T0FitHistos.h @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file T0FitHistos.h +/// \brief Class to store the TRD PH values for each TRD chamber + +#ifndef ALICEO2_T0FITHISTOS_H +#define ALICEO2_T0FITHISTOS_H + +#include "DataFormatsTRD/Constants.h" +#include "DataFormatsTRD/PHData.h" +#include "Rtypes.h" +#include +#include + +namespace o2 +{ +namespace trd +{ + +class T0FitHistos +{ + public: + T0FitHistos() = default; + T0FitHistos(const T0FitHistos&) = default; + ~T0FitHistos() = default; + auto getDetector(int index) const { return mDet[index]; } + auto getTimeBin(int index) const { return mTB[index]; } + auto getADC(int index) const { return mADC[index]; } + auto getNEntries() const { return mNEntriesTot; } + + void fill(const std::vector& data); + void merge(const T0FitHistos* prev); + void print(); + + private: + std::vector mDet{}; + std::vector mTB{}; + std::vector mADC{}; + size_t mNEntriesTot{0}; + + ClassDefNV(T0FitHistos, 1); +}; + +} // namespace trd +} // namespace o2 + +#endif // ALICEO2_T0FITHISTOS_H diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h index 463effd19106f..e63d8fbb5f277 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h @@ -9,9 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -//#include "TRDBase/GeometryBase.h" -//#include "DetectorsCommonDataFormats/DetMatrixCache.h" -//#include "DetectorsCommonDataFormats/DetID.h" +// #include "TRDBase/GeometryBase.h" +// #include "DetectorsCommonDataFormats/DetMatrixCache.h" +// #include "DetectorsCommonDataFormats/DetID.h" #ifndef O2_TRD_TRACKLET64_H #define O2_TRD_TRACKLET64_H @@ -27,8 +27,12 @@ // //////////////////////////////////////////////////////////////////////////// +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" +#include "GPUCommonMath.h" #include "DataFormatsTRD/Constants.h" namespace o2 @@ -84,16 +88,73 @@ class Tracklet64 GPUdDefault() ~Tracklet64() = default; GPUdDefault() Tracklet64& operator=(const Tracklet64& rhs) = default; + GPUd() bool operator==(const Tracklet64& o) const { return mtrackletWord == o.mtrackletWord; } + + // ----- Getters ----- + GPUd() uint64_t getTrackletWord() const { return mtrackletWord; } + + // position / direction information + + // half-chamber number [0..1079] + GPUd() int getHCID() const { return ((mtrackletWord & hcidmask) >> hcidbs); }; + + // chamber number [0..539] + GPUd() int getDetector() const { return getHCID() / 2; } + + // pad row number on ROB [0..15] + GPUd() int getPadRow() const { return ((mtrackletWord & padrowmask) >> padrowbs); }; + + // MCM position in column direction on ROB [0..3] + GPUd() int getColumn() const { return ((mtrackletWord & colmask) >> colbs); }; + + // in units of 1/40 pads, 11 bit granularity (MSP is used to indicate sign) + GPUd() int getPosition() const { return ((mtrackletWord & posmask) >> posbs); }; + + // applies two's complement to getPosition() to obtain signed value + GPUd() int getPositionBinSigned() const; - // ----- Getters for contents of tracklet word ----- - GPUd() int getFormat() const { return ((mtrackletWord & formatmask) >> formatbs); }; // no units 0..15 - GPUd() int getHCID() const { return ((mtrackletWord & hcidmask) >> hcidbs); }; // no units 0..1079 - GPUd() int getPadRow() const { return ((mtrackletWord & padrowmask) >> padrowbs); }; // pad row number [0..15] - GPUd() int getPadCol() const; // estimate the pad column number from the tracklet offset (can be off by +-1 pad) - GPUd() int getColumn() const { return ((mtrackletWord & colmask) >> colbs); }; // column refers to MCM position in column direction on readout board [0..3] - GPUd() int getPosition() const { return ((mtrackletWord & posmask) >> posbs); }; // in units of 1/40 pads, 11 bit granularity - GPUd() int getSlope() const { return ((mtrackletWord & slopemask) >> slopebs); }; // in units of 1/1000 pads/timebin, 8 bit granularity - GPUd() int getPID() const { return ((mtrackletWord & PIDmask)); }; // no unit, all 3 charge windows combined + // the position as float in unit of pads relative to MCM center + GPUd() float getPositionFloat() const { return getPositionBinSigned() * constants::GRANULARITYTRKLPOS; } + + // in units of 1/128 pads/timebin, 8 bit granularity (MSP is used to indicate sign) + GPUd() int getSlope() const { return ((mtrackletWord & slopemask) >> slopebs); }; + + // applies two's complement to getSlope() to obtain signed value + GPUd() int getSlopeBinSigned() const; + + // as float in units of pads per time bin + GPUd() float getSlopeFloat() const { return getSlopeBinSigned() * constants::GRANULARITYTRKLSLOPE / constants::ADDBITSHIFTSLOPE; } + + // MCM position on ROB [0..15] + GPUd() int getMCM() const { return constants::NMCMROBINROW * (getPadRow() % constants::NMCMROBINCOL) + getColumn(); } + + // ROB number [0..5] for C0 chamber and [0..7] for C1 chamber + GPUd() int getROB() const { return (getHCID() % 2) ? (getPadRow() / constants::NMCMROBINROW) * 2 + 1 : (getPadRow() / constants::NMCMROBINROW) * 2; } + + // MCM number in pad column direction [0..7] + GPUd() int getMCMCol() const { return (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); } + + // pad width in cm required for transformation from pad coordinates to tracking coordinates + GPUd() float getPadWidth() const { return 0.635f + 0.03f * (getDetector() % constants::NLAYER); } + + // pad column number inside pad row as float + // FIXME: understand why the offset seems to be 8 pads and not nChannels / 2 = 10.5 + // Due to wrong pad shift included in alignment we need to optionally shift the tracklets by one pad + // in case we are not using the ideal alignment + GPUd() float getPadColFloat(bool applyShift) const { return getPositionFloat() + getMCMCol() * constants::NCOLMCM + 8.f + (applyShift ? 1.f : 0.f); } + + // pad column number inside pad row as int can be off by +-1 pad (same function name as for TRD digit) + GPUd() int getPadCol(bool applyShift = false) const { return o2::gpu::CAMath::Float2IntRn(getPadColFloat(applyShift)); } + + // translate local position into global y (in cm) not taking into account calibrations (ExB, vDrift, t0) + GPUd() float getUncalibratedY(bool applyShift = false) const { return (getPadColFloat(applyShift) - (constants::NCOLUMN / 2.f)) * getPadWidth(); } + + // translate local slope into dy/dx with dx=3m (drift length) and default drift time in time bins (19.4 timebins / 3cm) + GPUd() float getUncalibratedDy(float nTbDrift = 19.4f) const { return getSlopeFloat() * getPadWidth() * nTbDrift; } + + // PID related getters + GPUd() int getFormat() const { return ((mtrackletWord & formatmask) >> formatbs); }; + GPUd() int getPID() const { return ((mtrackletWord & PIDmask)); }; GPUd() int getDynamicCharge(unsigned int charge) const { int shift = (charge >> 6) & 0x3; @@ -130,54 +191,14 @@ class Tracklet64 } }; // no unit + // ----- Setters for tracklet word manipulation ----- GPUd() void setTrackletWord(uint64_t trackletword) { mtrackletWord = trackletword; } - - // ----- Getters for tracklet information ----- - GPUd() int getMCM() const { return 4 * (getPadRow() % 4) + getColumn(); } // returns MCM position on ROB [0..15] - GPUd() int getROB() const { return (getHCID() % 2) ? (getPadRow() / 4) * 2 + 1 : (getPadRow() / 4) * 2; } // returns ROB number [0..5] for C0 chamber and [0..7] for C1 chamber - GPUd() int getPositionBinSigned() const; - GPUd() int getSlopeBinSigned() const; - GPUd() float getUncalibratedY() const; // translate local position into global y (in cm) not taking into account calibrations (ExB, vDrift, t0) - GPUd() float getUncalibratedDy(float nTbDrift = 19.4f) const; // translate local slope into dy/dx with dx=3m (drift length) and default drift time in time bins (19.4 timebins / 3cm) - - // ----- Getters for offline corresponding values ----- - GPUd() int getDetector() const { return getHCID() / 2; } - - GPUd() uint64_t getTrackletWord() const { return mtrackletWord; } - - GPUd() void setQ0(int charge) - { - mtrackletWord &= ~Q0mask; - mtrackletWord |= ((charge << Q0bs) & Q0mask); - } - GPUd() void setQ1(int charge) - { - mtrackletWord &= ~Q1mask; - mtrackletWord |= ((charge << Q1bs) & Q1mask); - } - GPUd() void setQ2(int charge) - { - mtrackletWord &= ~Q2mask; - mtrackletWord |= ((charge << Q2bs) & Q2mask); - } - GPUd() void setPID(uint64_t pid) - { - // set the entire pid area of the trackletword, all the 3 Q's - mtrackletWord &= ~PIDmask; - mtrackletWord |= ((pid << PIDbs) & PIDmask); - } - GPUd() void setPosition(uint64_t position) - { - mtrackletWord &= ~posmask; - mtrackletWord |= ((position << posbs) & posmask); - } - GPUd() void setSlope(uint64_t slope) - { - mtrackletWord &= ~slopemask; - mtrackletWord |= ((slope << slopebs) & slopemask); - } - - GPUd() bool operator==(const Tracklet64& o) const { return mtrackletWord == o.mtrackletWord; } + GPUd() void setQ0(int charge) { mtrackletWord = (mtrackletWord & ~Q0mask) | ((charge & Q0mask) << Q0bs); } + GPUd() void setQ1(int charge) { mtrackletWord = (mtrackletWord & ~Q1mask) | ((charge & Q1mask) << Q1bs); } + GPUd() void setQ2(int charge) { mtrackletWord = (mtrackletWord & ~Q2mask) | ((charge & Q2mask) << Q2bs); } + GPUd() void setPID(int pid) { mtrackletWord = (mtrackletWord & ~PIDmask) | ((pid & PIDmask) << PIDbs); } + GPUd() void setPosition(int position) { mtrackletWord = (mtrackletWord & ~posmask) | ((position & posmask) << posbs); } + GPUd() void setSlope(int slope) { mtrackletWord = (mtrackletWord & ~slopemask) | ((slope & slopemask) << slopebs); } GPUd() void print() const; #ifndef GPUCA_GPUCODE_DEVICE @@ -195,7 +216,7 @@ class Tracklet64 static constexpr uint64_t Q1mask = 0x000000000000ff00; static constexpr uint64_t Q0mask = 0x00000000000000ff; static constexpr uint64_t PIDmask = 0x0000000000ffffff; - //bit shifts for the above raw data + // bit shifts for the above raw data static constexpr uint64_t formatbs = 60; static constexpr uint64_t hcidbs = 49; static constexpr uint64_t padrowbs = 45; @@ -225,17 +246,6 @@ GPUdi() int Tracklet64::getPositionBinSigned() const return padLocal; } -GPUdi() float Tracklet64::getUncalibratedY() const -{ - int padLocal = getPositionBinSigned(); - int mcmCol = (getMCM() % constants::NMCMROBINCOL) + constants::NMCMROBINCOL * (getROB() % 2); - // one pad column has 144 pads, the offset of -63 is the center of the first MCM in that column - // which is connected to the pads -63 - 9 = -72 to -63 + 9 = -54 - float offset = -63.f + ((float)constants::NCOLMCM) * mcmCol; - float padWidth = 0.635f + 0.03f * (getDetector() % constants::NLAYER); - return (offset + padLocal * constants::GRANULARITYTRKLPOS) * padWidth; -} - GPUdi() int Tracklet64::getSlopeBinSigned() const { int slopeBin = getSlope(); @@ -248,18 +258,11 @@ GPUdi() int Tracklet64::getSlopeBinSigned() const return -slope; } -GPUdi() float Tracklet64::getUncalibratedDy(float nTbDrift) const -{ - int slope = getSlopeBinSigned(); - float padWidth = 0.635f + 0.03f * (getDetector() % constants::NLAYER); - return slope * constants::GRANULARITYTRKLSLOPE * padWidth * nTbDrift / constants::ADDBITSHIFTSLOPE; -} - #ifndef GPUCA_GPUCODE_DEVICE std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg); #endif // GPUCA_GPUCODE_DEVICE -} //namespace trd -} //namespace o2 +} // namespace trd +} // namespace o2 #endif diff --git a/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx b/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx index 5253fdfa9b359..dfee36f944723 100644 --- a/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx +++ b/DataFormats/Detectors/TRD/src/AngularResidHistos.cxx @@ -52,12 +52,6 @@ void AngularResidHistos::fill(const AngularResidHistos& input) } } -void AngularResidHistos::fill(const gsl::span input) -{ - (void)input; - LOG(fatal) << "This function must not be called. But it must be available for the compilation to work"; -} - void AngularResidHistos::merge(const AngularResidHistos* prev) { for (int i = 0; i < MAXCHAMBER * NBINSANGLEDIFF; ++i) { diff --git a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h index 35a8ed26a4b30..c6d36a7aee495 100644 --- a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h +++ b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h @@ -32,13 +32,20 @@ #pragma link C++ class o2::trd::KrClusterTriggerRecord + ; #pragma link C++ class o2::trd::NoiseStatusMCM + ; #pragma link C++ class o2::trd::AngularResidHistos + ; +#pragma link C++ class o2::trd::GainCalibHistos + ; +#pragma link C++ class o2::trd::T0FitHistos + ; #pragma link C++ class o2::trd::CalVdriftExB + ; +#pragma link C++ class o2::trd::CalGain + ; #pragma link C++ class o2::trd::CalT0 + ; #pragma link C++ class o2::trd::CompressedDigit + ; #pragma link C++ class o2::trd::HelperMethods + ; #pragma link C++ class o2::trd::LinkToHCIDMapping + ; #pragma link C++ class o2::trd::ChannelInfo + ; #pragma link C++ class o2::trd::ChannelInfoContainer + ; +#pragma link C++ struct o2::trd::PHData + ; +#pragma link C++ struct o2::trd::PHDataHD + ; +#pragma link C++ class o2::trd::TRDDataCountersPerTimeFrame + ; +#pragma link C++ class o2::trd::DataCountersPerTrigger + ; #pragma link C++ class std::vector < o2::trd::Tracklet64> + ; #pragma link C++ class std::vector < o2::trd::CalibratedTracklet> + ; #pragma link C++ class std::vector < o2::trd::TrackTriggerRecord> + ; @@ -47,8 +54,13 @@ #pragma link C++ class std::vector < o2::trd::Hit > +; #pragma link C++ class std::vector < o2::trd::Digit> + ; #pragma link C++ class std::vector < o2::trd::AngularResidHistos> + ; +#pragma link C++ class std::vector < o2::trd::GainCalibHistos> + ; +#pragma link C++ class std::vector < o2::trd::T0FitHistos> + ; +#pragma link C++ class std::vector < o2::trd::PHData> + ; +#pragma link C++ class std::vector < o2::trd::PHDataHD> + ; #pragma link C++ class std::vector < o2::trd::KrCluster> + ; #pragma link C++ class std::vector < o2::trd::KrClusterTriggerRecord> + ; +#pragma link C++ class std::vector < o2::trd::DataCountersPerTrigger> + ; #pragma link C++ struct o2::trd::CTFHeader + ; #pragma link C++ struct o2::trd::CTF + ; diff --git a/DataFormats/Detectors/TRD/src/Digit.cxx b/DataFormats/Detectors/TRD/src/Digit.cxx index d8a7e7613d7e8..37d6638ac0996 100644 --- a/DataFormats/Detectors/TRD/src/Digit.cxx +++ b/DataFormats/Detectors/TRD/src/Digit.cxx @@ -12,6 +12,7 @@ #include "DataFormatsTRD/Digit.h" #include #include +#include "fairlogger/Logger.h" namespace o2::trd { @@ -46,12 +47,18 @@ Digit::Digit(int det, int rob, int mcm, int channel, ArrayADC adc, int pretrigph setPreTrigPhase(pretrigphase); } -Digit::Digit(int det, int rob, int mcm, int channel) // add adc data in a seperate step +Digit::Digit(int det, int rob, int mcm, int channel, int pretrigphase) // add adc data in a seperate step { setDetector(det); setROB(rob); setMCM(mcm); setChannel(channel); + setPreTrigPhase(pretrigphase); +} + +void Digit::setPreTrigPhase(int phase) +{ + mDetector = ((((phase) & 0x3) << 12) | (mDetector & 0xfff)); } bool Digit::isSharedDigit() const @@ -63,6 +70,11 @@ bool Digit::isSharedDigit() const } } +bool Digit::isNeighbour(const Digit& other) const +{ + return (getDetector() == other.getDetector() && getROB() == other.getROB() && getMCM() == other.getMCM() && std::abs(getChannel() - other.getChannel()) == 1); +} + ADC_t Digit::getADCmax(int& idx) const { auto itMax = std::max_element(mADC.begin(), mADC.end()); diff --git a/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx b/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx new file mode 100644 index 0000000000000..5580c03d40c5c --- /dev/null +++ b/DataFormats/Detectors/TRD/src/GainCalibHistos.cxx @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GainCalibHistos.cxx +/// \brief Class to store the output of the global tracking based TRD calibration + +#include "DataFormatsTRD/GainCalibHistos.h" +#include +#include + +using namespace o2::trd; +using namespace o2::trd::constants; + +void GainCalibHistos::init() +{ + mdEdxEntries.resize(constants::MAXCHAMBER * constants::NBINSGAINCALIB, 0); + mInitialized = true; +} + +void GainCalibHistos::reset() +{ + if (!mInitialized) { + init(); + } + std::fill(mdEdxEntries.begin(), mdEdxEntries.end(), 0); + mNEntriesTot = 0; +} + +void GainCalibHistos::fill(const std::vector& input) +{ + if (!mInitialized) { + init(); + } + for (auto elem : input) { + ++mdEdxEntries[elem]; + } + mNEntriesTot += input.size(); +} + +void GainCalibHistos::merge(const GainCalibHistos* prev) +{ + if (!mInitialized) { + init(); + } + for (int i = 0; i < MAXCHAMBER * NBINSGAINCALIB; ++i) { + mdEdxEntries[i] += prev->getHistogramEntry(i); + mNEntriesTot += prev->getHistogramEntry(i); + } +} + +void GainCalibHistos::print() +{ + LOG(info) << "There are " << mNEntriesTot << " entries in the container"; +} diff --git a/DataFormats/Detectors/TRD/src/RawData.cxx b/DataFormats/Detectors/TRD/src/RawData.cxx index 8447f1e3c695d..8453cd55e4dc4 100644 --- a/DataFormats/Detectors/TRD/src/RawData.cxx +++ b/DataFormats/Detectors/TRD/src/RawData.cxx @@ -112,23 +112,23 @@ void setHalfCRUHeaderLinkSizeAndFlags(HalfCRUHeader& cruhead, int link, int size cruhead.errorflags[link].errorflag = errors; } -uint32_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link) +uint8_t getHalfCRULinkErrorFlag(const HalfCRUHeader& cruhead, const uint32_t link) { // link is the link you are requesting information on, 0-14 - uint32_t errorflag = 0; + uint8_t errorflag = 0; //dealing with word0-2 errorflag = cruhead.errorflags[link].errorflag; return errorflag; } -uint32_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link) +uint16_t getHalfCRULinkDataSize(const HalfCRUHeader& cruhead, const uint32_t link) { // link is the link you are requesting information on, 0-14 //return number 32 byte blocks for the link 3x64bit ints. return cruhead.datasizes[link].size; } -void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags) +void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linkerrorflags) { // retrieve all the link error flags for this half cru for (uint32_t link = 0; link < 15; link++) { @@ -136,7 +136,7 @@ void getHalfCRULinkErrorFlags(const HalfCRUHeader& cruheader, std::array& linksizes) +void getHalfCRULinkDataSizes(const HalfCRUHeader& cruheader, std::array& linksizes) { // retrieve all the link error flags for this half cru for (uint32_t link = 0; link < 15; link++) { @@ -229,8 +229,8 @@ void printTrackletMCMHeader(const o2::trd::TrackletMCMHeader& mcmhead) void printHalfCRUHeader(const o2::trd::HalfCRUHeader& halfcru) { - std::array sizes; - std::array errorflags; + std::array sizes; + std::array errorflags; getHalfCRULinkDataSizes(halfcru, sizes); getHalfCRULinkErrorFlags(halfcru, errorflags); LOGF(info, "V:%d BC:%d SB:%d EType:%d", halfcru.HeaderVersion, halfcru.BunchCrossing, diff --git a/DataFormats/Detectors/TRD/src/T0FitHistos.cxx b/DataFormats/Detectors/TRD/src/T0FitHistos.cxx new file mode 100644 index 0000000000000..623f61b761188 --- /dev/null +++ b/DataFormats/Detectors/TRD/src/T0FitHistos.cxx @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file T0FitHistos.cxx +/// \brief Class to store the TRD PH values for each chamber + +#include "DataFormatsTRD/T0FitHistos.h" +#include +#include + +using namespace o2::trd; +using namespace o2::trd::constants; + +void T0FitHistos::fill(const std::vector& data) +{ + for (const auto& ph : data) { + int det = ph.getDetector(); + int tb = ph.getTimebin(); + int adc = ph.getADC(); + + if (ph.getNneighbours() != 2) { + continue; + } + + mDet.push_back(det); + mTB.push_back(tb); + mADC.push_back(adc); + ++mNEntriesTot; + } +} + +void T0FitHistos::merge(const T0FitHistos* prev) +{ + auto sizePrev = (int)prev->getNEntries(); + + for (int i = 0; i < sizePrev; ++i) { + mDet.push_back(prev->getDetector(i)); + mTB.push_back(prev->getTimeBin(i)); + mADC.push_back(prev->getADC(i)); + } +} + +void T0FitHistos::print() +{ + LOG(info) << "There are " << mNEntriesTot << " entries in the container"; +} diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx index 35879fcec09e3..d7b63cae45354 100644 --- a/DataFormats/Detectors/TRD/src/Tracklet64.cxx +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -10,37 +10,19 @@ // or submit itself to any jurisdiction. #include "DataFormatsTRD/Tracklet64.h" -#include "DataFormatsTRD/Constants.h" #include "DataFormatsTRD/HelperMethods.h" -#include "GPUCommonMath.h" - #include "fairlogger/Logger.h" #include -using namespace GPUCA_NAMESPACE::gpu; - namespace o2 { - namespace trd { -using namespace constants; - void Tracklet64::print() const { - LOGF(info, "%02i_%i_%i, row(%i), col(%i), position(%i), slope(%i), pid(%i), q0(%i), q1(%i), q2(%i). Format(%i)", - HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getPadRow(), getColumn(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); -} - -GPUd() int Tracklet64::getPadCol() const -{ - // obtain pad number relative to MCM center - int padLocal = getPositionBinSigned() * GRANULARITYTRKLPOS; - // MCM number in column direction (0..7) - int mcmCol = (getMCM() % NMCMROBINCOL) + NMCMROBINCOL * (getROB() % 2); - // FIXME: understand why the offset seems to be 6 pads and not nChannels / 2 = 10.5 - return CAMath::Nint(6.f + mcmCol * ((float)NCOLMCM) + padLocal); + LOGF(info, "%02i_%i_%i, ROB(%i), MCM(%i), row(%i), col(%i), position(%i), slope(%i), pid(%i), q0(%i), q1(%i), q2(%i). Format(%i)", + HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getROB(), getMCM(), getPadRow(), getPadCol(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); } #ifndef GPUCA_GPUCODE_DEVICE @@ -58,6 +40,16 @@ std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg) trg.printStream(stream); return stream; } + +bool operator<(const Tracklet64& lhs, const Tracklet64& rhs) +{ + return (lhs.getDetector() < rhs.getDetector()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() < rhs.getROB()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() < rhs.getMCM()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() < rhs.getPadRow()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() == rhs.getPadRow() && lhs.getPadCol() < rhs.getPadCol()); +} + #endif // GPUCA_GPUCODE_DEVICE } // namespace trd diff --git a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt new file mode 100644 index 0000000000000..b3944c2e502d8 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright 2019-2025 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(FD3) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt new file mode 100644 index 0000000000000..e2219bb893612 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(DataFormatsFD3 + SOURCES src/Hit.cxx + PUBLIC_LINK_LIBRARIES O2::FD3Base + O2::SimulationDataFormat + O2::CommonDataFormat + Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats +) + +o2_target_root_dictionary(DataFormatsFD3 + HEADERS include/DataFormatsFD3/Hit.h +) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h new file mode 100644 index 0000000000000..4fde2f6cde6b4 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/include/DataFormatsFD3/Hit.h @@ -0,0 +1,125 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.h +/// \brief Definition of the FD3 Hit class (based on ITSMFT and FV0) + +#ifndef ALICEO2_FVD_HIT_H_ +#define ALICEO2_FVD_HIT_H_ + +#include +#include "SimulationDataFormat/BaseHits.h" // for BasicXYZEHit +#include "Rtypes.h" // for Bool_t, Double_t, int, Double32_t, etc +#include "TVector3.h" // for TVector3 +#include "CommonUtils/ShmAllocator.h" + +namespace o2 +{ +namespace fd3 +{ + +class Hit : public o2::BasicXYZEHit +{ + public: + /// Default constructor + Hit() = default; + + /// Class Constructor + /// \param trackID Index of MCTrack + /// \param cellID Cell ID + /// \param startPos Coordinates at entrance to active volume [cm] + /// \param endPos Coordinates to active volume [cm] + /// \param startMom Momentum of track at entrance [GeV] + /// \param startE Energy of track at entrance [GeV] + /// \param endTime Final time [ns] + /// \param eLoss Energy deposit [GeV] + /// \param particlePdg PDG code of the partcile associated with the track + inline Hit(int trackID, + int cellID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg); + + // Entrance position getters + math_utils::Point3D const& GetPosStart() const { return mPositionStart; } + float GetStartX() const { return mPositionStart.X(); } + float GetStartY() const { return mPositionStart.Y(); } + float GetStartZ() const { return mPositionStart.Z(); } + template + void GetStartPosition(F& x, F& y, F& z) const + { + x = GetStartX(); + y = GetStartY(); + z = GetStartZ(); + } + + // Momentum getters + math_utils::Vector3D const& GetMomentum() const { return mMomentumStart; } + math_utils::Vector3D& GetMomentum() { return mMomentumStart; } + float GetPx() const { return mMomentumStart.X(); } + float GetPy() const { return mMomentumStart.Y(); } + float GetPz() const { return mMomentumStart.Z(); } + float GetE() const { return mEnergyStart; } + float GetTotalEnergyAtEntrance() const { return GetE(); } + int GetParticlePdg() const { return mParticlePdg; } + + void Print(const Option_t* opt) const; + + private: + math_utils::Vector3D mMomentumStart; ///< momentum at entrance + math_utils::Point3D mPositionStart; ///< position at entrance (base mPos give position on exit) + float mEnergyStart; ///< total energy at entrance + int mParticlePdg; ///< PDG code of the particle associated with this track + + ClassDefNV(Hit, 1); +}; + +Hit::Hit(int trackID, + int detID, + const math_utils::Point3D& startPos, + const math_utils::Point3D& endPos, + const math_utils::Vector3D& startMom, + double startE, + double endTime, + double eLoss, + int particlePdg) + : BasicXYZEHit(endPos.X(), + endPos.Y(), + endPos.Z(), + endTime, + eLoss, + trackID, + detID), + mMomentumStart(startMom.X(), startMom.Y(), startMom.Z()), + mPositionStart(startPos.X(), startPos.Y(), startPos.Z()), + mEnergyStart(startE), + mParticlePdg(particlePdg) +{ +} + +} // namespace fd3 +} // namespace o2 + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; + +} // namespace std +#endif /* USESHM */ +#endif /* ALICEO2_FD3_HIT_H_ */ diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h new file mode 100644 index 0000000000000..1014b3d8c704e --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/DataFormatsFD3LinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fd3::Hit + ; +#pragma link C++ class vector < o2::fd3::Hit> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx new file mode 100644 index 0000000000000..403a3402bd30c --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/FD3/src/Hit.cxx @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Hit.cxx +/// \brief Implementation of the Hit class + +#include "DataFormatsFD3/Hit.h" +#include + +ClassImp(o2::fd3::Hit); + +namespace o2 +{ +namespace fd3 +{ + +void Hit::Print(const Option_t* opt) const +{ + printf( + "Det: %5d Track: %6d E.loss: %.3e P: %+.3e %+.3e %+.3e\n" + "PosIn: %+.3e %+.3e %+.3e PosOut: %+.3e %+.3e %+.3e\n", + GetDetectorID(), GetTrackID(), GetEnergyLoss(), GetPx(), GetPy(), GetPz(), + GetStartX(), GetStartY(), GetStartZ(), GetX(), GetY(), GetZ()); +} + +} // namespace fd3 +} // namespace o2 diff --git a/DataFormats/Detectors/Upgrades/CMakeLists.txt b/DataFormats/Detectors/Upgrades/CMakeLists.txt index a2d470b8ff6d5..0dfe07dc2827d 100644 --- a/DataFormats/Detectors/Upgrades/CMakeLists.txt +++ b/DataFormats/Detectors/Upgrades/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# Copyright 2019-2025 CERN and copyright holders of ALICE O2. # See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. # All rights not expressly granted are reserved. # @@ -10,3 +10,4 @@ # or submit itself to any jurisdiction. message(STATUS "Building dataformats for upgrades") +add_subdirectory(ALICE3) diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h index da1ca9d1d0292..916181030c583 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/BCData.h @@ -55,8 +55,9 @@ struct BCData { o2::dataformats::RangeRefComp<6> ref; o2::InteractionRecord ir; std::array moduleTriggers{}; + // N.B. channels and triggers have geographical addressing (0x1 << (NChPerModule * im + ic) uint32_t channels = 0; // pattern of channels it refers to - uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC + uint32_t triggers = 0; // pattern of triggered channels (not necessarily stored) in this BC (i.e. with Hit bit on) uint8_t ext_triggers = 0; // pattern of ALICE triggers BCData() = default; diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h index 6e0b99dca6761..3ce90a95f7248 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/Hit.h @@ -58,6 +58,7 @@ class Hit : public o2::BasicXYZEHit float getPMCLightYield() const { return mNphePMC; } float getPMQLightYield() const { return mNphePMQ; } int getNumContributingSteps() const { return mNoContributingSteps; } + bool getSecFlag() const { return mSecFlag; } private: Int_t mParentID; diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h index 744d36d62e2dc..ea5fd10f92dcc 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/RecEventFlat.h @@ -37,6 +37,21 @@ namespace zdc using FirstEntry = int; using NElem = int; +struct RecEventScale { + static std::array fe; + static std::array fa; + static void reset(); + static void setGeV(); + static void setGeVZN(); + static void setGeVZP(); + static void setTeV(); + static void setTeVZN(); + static void setTeVZP(); + static void setNucleonEnergy(float energy); + static void setNucleonEnergyZN(float energy); + static void setNucleonEnergyZP(float energy); +}; + struct RecEventFlat { // NOLINT: false positive in clang-tidy !! o2::InteractionRecord ir; uint32_t channels = 0; /// pattern of channels acquired @@ -172,7 +187,7 @@ struct RecEventFlat { // NOLINT: false positive in clang-tidy !! { if (ich < NTDCChannels) { if (ipos < TDCAmp[ich].size()) { - return FTDCAmp * TDCAmp[ich][ipos]; + return TDCAmp[ich][ipos]; } } return -std::numeric_limits::infinity(); @@ -244,12 +259,16 @@ struct RecEventFlat { // NOLINT: false positive in clang-tidy !! // Unit: cm void centroidZNA(float& x, float& y); void centroidZNC(float& x, float& y); + void centroidZPA(float& x, float& rms); + void centroidZPC(float& x, float& rms); float xZNA(); float yZNA(); float xZNC(); float yZNC(); float xZPA(); // Positive float xZPC(); // Negative + float rmsZPA(); + float rmsZPC(); void decodeInfo(uint8_t ch, uint16_t code); void decodeMapInfo(uint32_t ch, uint16_t code); diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h index 4e3d0bc9f9d4f..d4d6bcdf75be0 100644 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h +++ b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/ZDCTDCData.h @@ -30,7 +30,6 @@ namespace zdc struct ZDCTDCDataErr { static uint32_t mErrVal[NTDCChannels]; // Errors in encoding TDC values - static uint32_t mErrAmp[NTDCChannels]; // Errors in encoding TDC amplitudes static uint32_t mErrId; // Errors with TDC Id static void print() @@ -43,11 +42,6 @@ struct ZDCTDCDataErr { LOG(error) << "TDCVal itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " was out of range #times = " << mErrVal[itdc]; } } - for (int itdc = 0; itdc < NTDCChannels; itdc++) { - if (mErrAmp[itdc] > 0) { - LOG(warning) << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " was out of range #times = " << mErrAmp[itdc]; - } - } } }; @@ -55,10 +49,11 @@ struct ZDCTDCData { uint8_t id = 0xff; // channel ID int16_t val = 0; // tdc value - int16_t amp = 0; // tdc amplitude + float amp = 0; // tdc amplitude ZDCTDCData() = default; - ZDCTDCData(uint8_t ida, int16_t vala, int16_t ampa, bool isbeg = false, bool isend = false) + + ZDCTDCData(uint8_t ida, int16_t vala, float ampa, bool isbeg = false, bool isend = false) { // TDC value and amplitude are encoded externally id = ida < NTDCChannels ? ida : 0xf; @@ -70,7 +65,7 @@ struct ZDCTDCData { amp = ampa; } else { val = kMaxShort; - amp = kMaxShort; + amp = FInfty; #ifdef O2_ZDC_DEBUG LOG(error) << __func__ << "TDC Id = " << int(ida) << " is out of range"; #endif @@ -87,7 +82,7 @@ struct ZDCTDCData { if (ida >= NTDCChannels) { val = kMaxShort; - amp = kMaxShort; + amp = FInfty; #ifdef O2_ZDC_DEBUG LOG(error) << __func__ << "TDC Id = " << int(ida) << " is out of range"; #endif @@ -96,7 +91,6 @@ struct ZDCTDCData { } auto TDCVal = std::nearbyint(vala); - auto TDCAmp = std::nearbyint(ampa); if (TDCVal < kMinShort) { int itdc = int(id); @@ -116,46 +110,31 @@ struct ZDCTDCData { TDCVal = kMaxShort; } - if (TDCAmp < kMinShort) { - int itdc = int(ida); -#ifdef O2_ZDC_DEBUG - LOG(warning) << __func__ << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " = " << TDCAmp << " is out of range"; -#endif - ZDCTDCDataErr::mErrAmp[itdc]++; - TDCAmp = kMinShort; - } - - if (TDCAmp > kMaxShort) { - int itdc = int(ida); -#ifdef O2_ZDC_DEBUG - LOG(warning) << __func__ << "TDCAmp itdc=" << itdc << " " << ChannelNames[TDCSignal[itdc]] << " = " << TDCAmp << " is out of range"; -#endif - ZDCTDCDataErr::mErrAmp[itdc]++; - TDCAmp = kMaxShort; - } - val = TDCVal; - amp = TDCAmp; + amp = ampa; } inline float amplitude() const { - // Return decoded value - return FTDCAmp * amp; + return amp; } + inline float value() const { // Return decoded value (ns) return FTDCVal * val; } + inline int ch() const { return (id & 0x0f); } + inline bool isBeg() const { return id & 0x80 ? true : false; } + inline bool isEnd() const { return id & 0x40 ? true : false; @@ -163,7 +142,7 @@ struct ZDCTDCData { void print() const; - ClassDefNV(ZDCTDCData, 1); + ClassDefNV(ZDCTDCData, 2); }; } // namespace zdc } // namespace o2 diff --git a/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h b/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h index 6e272dbc8a118..0fbc7308f39e3 100644 --- a/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h +++ b/DataFormats/Detectors/ZDC/src/DataFormatsZDCLinkDef.h @@ -51,4 +51,6 @@ #pragma link C++ struct o2::zdc::CTF + ; #pragma link C++ class o2::ctf::EncodedBlocks < o2::zdc::CTFHeader, 12, uint32_t> + ; +#pragma read sourceClass = "ZDCTDCData" targetClass = "ZDCTDCData" source = "int16_t amp" version = "[-1]" target = "amp" targetType = "float" code = "{ amp=onfile.amp / 8.; }" + #endif diff --git a/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx b/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx index b5632285fd55f..cb2e298eee317 100644 --- a/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx +++ b/DataFormats/Detectors/ZDC/src/RecEventFlat.cxx @@ -15,6 +15,133 @@ using namespace o2::zdc; +std::array RecEventScale::fe = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; +std::array RecEventScale::fa = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +void RecEventScale::reset() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1; + } +} + +void RecEventScale::setGeV() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1000.; + } +} + +void RecEventScale::setGeVZN() +{ + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 1; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 1; + } + fa[TDCZNAC] = 1000.; + fa[TDCZNAS] = 1000.; + fa[TDCZNCC] = 1000.; + fa[TDCZNCS] = 1000.; +} + +void RecEventScale::setGeVZP() +{ + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 1; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 1; + } + fa[TDCZPAC] = 1000.; + fa[TDCZPAS] = 1000.; + fa[TDCZPCC] = 1000.; + fa[TDCZPCS] = 1000.; +} + +void RecEventScale::setTeV() +{ + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 0.001; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1.; + } +}; + +void RecEventScale::setTeVZN() +{ + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 0.001; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 0.001; + } + fa[TDCZNAC] = 1.; + fa[TDCZNAS] = 1.; + fa[TDCZNCC] = 1.; + fa[TDCZNCS] = 1.; +}; + +void RecEventScale::setTeVZP() +{ + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 0.001; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 0.001; + } + fa[TDCZPAC] = 1.; + fa[TDCZPAS] = 1.; + fa[TDCZPCC] = 1.; + fa[TDCZPCS] = 1.; +}; + +void RecEventScale::setNucleonEnergy(float energy) +{ // In GeV + for (int ich = 0; ich < NChannels; ich++) { + fe[ich] = 1. / energy; + } + for (int itdc = 0; itdc < NTDCChannels; itdc++) { + fa[itdc] = 1000. / energy; + } +}; + +void RecEventScale::setNucleonEnergyZN(float energy) +{ // In GeV + for (int ich = IdZNAC; ich <= IdZNASum; ich++) { + fe[ich] = 1. / energy; + } + for (int ich = IdZNCC; ich <= IdZNCSum; ich++) { + fe[ich] = 1. / energy; + } + fa[TDCZNAC] = 1000. / energy; + fa[TDCZNAS] = 1000. / energy; + fa[TDCZNCC] = 1000. / energy; + fa[TDCZNCS] = 1000. / energy; +}; + +void RecEventScale::setNucleonEnergyZP(float energy) +{ // In GeV + for (int ich = IdZPAC; ich <= IdZPASum; ich++) { + fe[ich] = 1. / energy; + } + for (int ich = IdZPCC; ich <= IdZPCSum; ich++) { + fe[ich] = 1. / energy; + } + fa[TDCZPAC] = 1000. / energy; + fa[TDCZPAS] = 1000. / energy; + fa[TDCZPCC] = 1000. / energy; + fa[TDCZPCS] = 1000. / energy; +}; + void RecEventFlat::init(const std::vector* RecBC, const std::vector* Energy, const std::vector* TDCData, const std::vector* Info) { mRecBC = *RecBC; @@ -143,7 +270,7 @@ int RecEventFlat::at(int ientry) for (int i = mFirstE; i < mStopE; i++) { auto myenergy = mEnergy[i]; auto ch = myenergy.ch(); - ezdc[ch] = myenergy.energy(); + ezdc[ch] = myenergy.energy() * RecEventScale::fe[ch]; // Assign implicit event info if (adcPedOr[ch] == false && adcPedQC[ch] == false && adcPedMissing[ch] == false) { adcPedEv[ch] = true; @@ -163,7 +290,7 @@ int RecEventFlat::at(int ientry) isEnd[ch] = true; } TDCVal[ch].push_back(mytdc.val); - TDCAmp[ch].push_back(mytdc.amp); + TDCAmp[ch].push_back(mytdc.amp * RecEventScale::fa[ch]); // Assign implicit event info if (tdcPedQC[ch] == false && tdcPedMissing[ch] == false) { tdcPedOr[ch] = true; @@ -386,13 +513,44 @@ float RecEventFlat::yZNC() float RecEventFlat::xZPA() { - // X coordinates of tower centers - // Positive because calorimeter is on the right of ZNA when looking - // from IP - const static float xc[4] = {2.8, 8.4, 14.0, 19.6}; + float x, rms; + centroidZPA(x, rms); + return x; +} + +float RecEventFlat::xZPC() +{ + float x, rms; + centroidZPC(x, rms); + return x; +} + +float RecEventFlat::rmsZPA() +{ + float x, rms; + centroidZPA(x, rms); + return rms; +} + +float RecEventFlat::rmsZPC() +{ + float x, rms; + centroidZPC(x, rms); + return rms; +} + +void RecEventFlat::centroidZPA(float& x, float& rms) +{ + // x coordinates of tower centers + // Negative because ZPA calorimeter is on the left of ZNA when looking from IP + // rms can result from 0 to sqrt((2.8^2+19.6^2)/2-((2.6+19.4)/2)^2) = 8.66 + const static float xc[4] = {-19.6, -14.0, -8.4, -2.8}; + const static float xq[4] = {19.6 * 19.6, 14.0 * 14.0, 8.4 * 8.4, 2.8 * 2.8}; static float c = 0; - if (mComputed[2]) { - return c; + static float d = 0; + if (mComputed[2] == true) { + x = c; + rms = d; } mComputed[2] = true; float e[4], w[4]; @@ -402,35 +560,56 @@ float RecEventFlat::xZPA() e[3] = EZDC(IdZPA4); if (e[0] < -1000000 || e[1] < -1000000 || e[2] < -1000000 || e[3] < -1000000) { c = -std::numeric_limits::infinity(); - return c; + d = -std::numeric_limits::infinity(); + x = c; + rms = d; + return; } float sumw = 0; c = 0; + d = 0; for (int i = 0; i < 4; i++) { if (e[i] < 0) { - e[i] = 0; + w[i] = 0; + } else { + // w[i] = std::sqrt(e[i]); + w[i] = e[i]; } - w[i] = std::sqrt(e[i]); sumw += w[i]; - c += w[i] * xc[i]; + c = c + w[i] * xc[i]; + d = d + w[i] * xq[i]; } if (sumw <= 0) { c = -std::numeric_limits::infinity(); + d = -std::numeric_limits::infinity(); } else { - c /= sumw; + c = c / sumw; + d = d / sumw; + d = d - c * c; // variance + if (d >= 0) { + d = TMath::Sqrt(d); + } else { + LOGF(warn, "%s FOP exception @ %d", __FILE__, __LINE__); + d = -std::numeric_limits::infinity(); + } } - return c; + x = c; + rms = d; + return; } -float RecEventFlat::xZPC() +void RecEventFlat::centroidZPC(float& x, float& rms) { - // X coordinates of tower centers - // Negative because calorimeter is on the left of ZNC when looking - // from IP - const static float xc[4] = {-2.8, -8.4, -14.0, -19.6}; + // x coordinates of tower centers + // Positive because ZPC calorimeter is on the right of ZNC when looking from IP + // x can result in a value from 2.8 to 19.6 + const static float xc[4] = {2.8, 8.4, 14.0, 19.6}; + const static float xq[4] = {2.8 * 2.8, 8.4 * 8.4, 14.0 * 14.0, 19.6 * 19.6}; static float c = 0; + static float d = 0; if (mComputed[3]) { - return c; + x = c; + rms = d; } mComputed[3] = true; float e[4], w[4]; @@ -440,24 +619,42 @@ float RecEventFlat::xZPC() e[3] = EZDC(IdZPC4); if (e[0] < -1000000 || e[1] < -1000000 || e[2] < -1000000 || e[3] < -1000000) { c = -std::numeric_limits::infinity(); - return c; + d = -std::numeric_limits::infinity(); + x = c; + rms = d; + return; } float sumw = 0; c = 0; + d = 0; for (int i = 0; i < 4; i++) { if (e[i] < 0) { - e[i] = 0; + w[i] = 0; + } else { + // w[i] = std::sqrt(e[i]); + w[i] = e[i]; } - w[i] = std::sqrt(e[i]); sumw += w[i]; c += w[i] * xc[i]; + d += w[i] * xq[i]; } if (sumw <= 0) { c = -std::numeric_limits::infinity(); + d = -std::numeric_limits::infinity(); } else { c /= sumw; + d /= sumw; + d = d - c * c; // variance + if (d >= 0) { + d = TMath::Sqrt(d); + } else { + LOGF(warn, "%s FOP exception @ %d", __FILE__, __LINE__); + d = -std::numeric_limits::infinity(); + } } - return c; + x = c; + rms = d; + return; } void RecEventFlat::print() const @@ -568,12 +765,11 @@ void RecEventFlat::printEvent() const int ich = o2::zdc::TDCSignal[itdc]; int nhit = NtdcV(itdc); if (NtdcA(itdc) != nhit) { - fprintf(stderr, "Mismatch in TDC %d data Val=%d Amp=%d\n", itdc, NtdcV(itdc), NtdcA(ich)); + fprintf(stderr, "Mismatch in TDC %d data length Val=%d Amp=%d values\n", itdc, NtdcV(itdc), NtdcA(ich)); continue; } for (int32_t ipos = 0; ipos < nhit; ipos++) { - printf("%9u.%04u T %2d %s = %g @ %g \n", mCurB.ir.orbit, mCurB.ir.bc, ich, ChannelNames[ich].data(), - FTDCAmp * TDCAmp[itdc][ipos], FTDCVal * TDCVal[itdc][ipos]); + printf("%9u.%04u T %2d %s = %g @ %g \n", mCurB.ir.orbit, mCurB.ir.bc, ich, ChannelNames[ich].data(), TDCAmp[itdc][ipos], FTDCVal * TDCVal[itdc][ipos]); } } // Energy diff --git a/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx b/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx index e86ddddbf2ce7..5c55eddee1525 100644 --- a/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx +++ b/DataFormats/Detectors/ZDC/src/ZDCTDCData.cxx @@ -14,7 +14,6 @@ using namespace o2::zdc; uint32_t ZDCTDCDataErr::mErrVal[NTDCChannels] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -uint32_t ZDCTDCDataErr::mErrAmp[NTDCChannels] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint32_t ZDCTDCDataErr::mErrId = 0; void o2::zdc::ZDCTDCData::print() const @@ -24,5 +23,5 @@ void o2::zdc::ZDCTDCData::print() const if (id != 0xff && itdc >= 0 && itdc < NTDCChannels) { isig = TDCSignal[itdc]; } - printf("%2d (%s) %d = %8.3f @ %d = %6.3f%s%s\n", isig, channelName(isig), amp, amplitude(), val, value(), isBeg() ? " B" : "", isEnd() ? " E" : ""); + printf("%2d (%s) %8.3f @ %d = %6.3f%s%s\n", isig, channelName(isig), amp, val, value(), isBeg() ? " B" : "", isEnd() ? " E" : ""); } diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index 8e2b8961c15cf..b44f41c5d3cb3 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -224,14 +224,21 @@ struct Descriptor { // Note: don't need to define operator=(ItgType v) because the compiler // can use Descriptor(ItgType initializer) for conversion + using ImplicitConversion = std::conditional_t<(size <= 8), ItgType, std::string_view>; // type cast operator for simplified usage of the descriptor's integer member - // TODO: this is sort of a hack, takes the first element. - // we should rethink these implicit conversions - operator ItgType() const + // in case it does not fit into the descriptor, the string representation is returned + operator ImplicitConversion() const { - static_assert(arraySize == 1, "casting Descriptor to ItgType only allowed for N<=8"); - return itg[0]; + if constexpr (std::is_same_v) { + return itg[0]; + } else { + size_t len = size; + while (len > 1 && str[len - 1] == 0) { + --len; + } + return std::string_view(str, len); + } } /// constructor from a compile-time string @@ -279,6 +286,8 @@ struct Descriptor { bool operator<(const Descriptor& other) const { return std::memcmp(this->str, other.str, N) < 0; } bool operator!=(const Descriptor& other) const { return not this->operator==(other); } + // Convesion operators for comparison with their implicitly convertible types + friend bool operator==(const Descriptor& lhs, ImplicitConversion rhs) { return static_cast(lhs) == rhs; } // explicitly forbid comparison with e.g. const char* strings // use: value == Descriptor("DESC") for the appropriate // template instantiation instead @@ -364,7 +373,8 @@ struct BaseHeader { uint32_t flags; struct { uint32_t flagsNextHeader : 1, // do we have a next header after this one? - flagsUnused : 31; // currently unused + flagsReserved : 15, // reserved for future use + flagsDerivedHeader : 16; // reserved for usage by the derived header }; }; @@ -574,6 +584,13 @@ constexpr o2::header::DataOrigin gDataOriginFOC{"FOC"}; constexpr o2::header::DataOrigin gDataOriginTRK{"TRK"}; constexpr o2::header::DataOrigin gDataOriginFT3{"FT3"}; constexpr o2::header::DataOrigin gDataOriginFCT{"FCT"}; +constexpr o2::header::DataOrigin gDataOriginTF3{"TF3"}; +constexpr o2::header::DataOrigin gDataOriginRCH{"RCH"}; +constexpr o2::header::DataOrigin gDataOriginMI3{"MI3"}; +constexpr o2::header::DataOrigin gDataOriginECL{"ECL"}; // upgrades +constexpr o2::header::DataOrigin gDataOriginFD3{"FD3"}; // upgrades + +constexpr o2::header::DataOrigin gDataOriginGPU{"GPU"}; // possible data types constexpr o2::header::DataDescription gDataDescriptionAny{"***************"}; diff --git a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h index 41d59090e0274..4f7e49acb4d98 100644 --- a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h +++ b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h @@ -38,23 +38,23 @@ struct fmt::formatter::value, c } template - auto format(const T& p, FormatContext& ctx) + auto format(const T& p, FormatContext& ctx) const { - return format_to(ctx.out(), "{}", p.template as()); + return fmt::format_to(ctx.out(), "{}", p.template as()); } }; template <> struct fmt::formatter { - // Presentation format: 'f' - fixed, 'e' - exponential. char presentation = 's'; - // Parses format specifications of the form ['f' | 'e']. constexpr auto parse(format_parse_context& ctx) { auto it = ctx.begin(), end = ctx.end(); if (it != end && (*it == 's')) { presentation = *it++; + } else if (it != end && (*it == 'x')) { + presentation = *it++; } // Check if reached the end of the range: @@ -67,19 +67,28 @@ struct fmt::formatter { } template - auto format(const o2::header::DataHeader& h, FormatContext& ctx) + auto format(const o2::header::DataHeader& h, FormatContext& ctx) const { - auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + - fmt::format(" origin : {}\n", h.dataOrigin.str) + - fmt::format(" serialization: {}\n", h.payloadSerializationMethod.str) + - fmt::format(" description : {}\n", h.dataDescription.str) + - fmt::format(" sub spec. : {}\n", (long long unsigned int)h.subSpecification) + - fmt::format(" header size : {}\n", h.headerSize) + - fmt::format(" payloadSize : {}\n", (long long unsigned int)h.payloadSize) + - fmt::format(" firstTForbit : {}\n", h.firstTForbit) + - fmt::format(" tfCounter : {}\n", h.tfCounter) + - fmt::format(" runNumber : {}\n", h.runNumber); - return format_to(ctx.out(), "{}", res); + if (presentation == 's') { + auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + + fmt::format(" origin : {}\n", h.dataOrigin.str) + + fmt::format(" serialization: {}\n", h.payloadSerializationMethod.str) + + fmt::format(" description : {}\n", h.dataDescription.str) + + fmt::format(" sub spec. : {}\n", (long long unsigned int)h.subSpecification) + + fmt::format(" header size : {}\n", h.headerSize) + + fmt::format(" payloadSize : {}\n", (long long unsigned int)h.payloadSize) + + fmt::format(" firstTForbit : {}\n", h.firstTForbit) + + fmt::format(" tfCounter : {}\n", h.tfCounter) + + fmt::format(" runNumber : {}\n", h.runNumber) + + fmt::format(" split : {}/{}\n", h.splitPayloadIndex, h.splitPayloadParts); + return fmt::format_to(ctx.out(), "{}", res); + } else { + auto res = fmt::format("{}/{}/{}", + h.dataOrigin.str, + h.dataDescription.str, + (long long unsigned int)h.subSpecification); + return fmt::format_to(ctx.out(), "{}", res); + } } }; diff --git a/DataFormats/Headers/include/Headers/Stack.h b/DataFormats/Headers/include/Headers/Stack.h index 184733e35c339..9770df9fa54ef 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -14,9 +14,7 @@ #include "MemoryResources/MemoryResources.h" #include "Headers/DataHeader.h" -namespace o2 -{ -namespace header +namespace o2::header { //__________________________________________________________________________________________________ /// @struct Stack @@ -38,26 +36,27 @@ struct Stack { private: struct freeobj { - freeobj(memory_resource* mr) : resource(mr) {} + freeobj(memory_resource* mr, size_t s) : resource(mr), size(s) {} memory_resource* resource{nullptr}; - void operator()(std::byte* ptr) { resource->deallocate(ptr, 0, 0); } + size_t size{0}; + void operator()(std::byte* ptr) { resource->deallocate(ptr, size, alignof(std::max_align_t)); } }; public: - using allocator_type = boost::container::pmr::polymorphic_allocator; + using allocator_type = fair::mq::pmr::polymorphic_allocator; using value_type = std::byte; - using BufferType = std::unique_ptr; //this gives us proper default move semantics for free + using BufferType = std::unique_ptr; // this gives us proper default move semantics for free Stack() = default; Stack(Stack&&) = default; Stack(Stack&) = delete; Stack& operator=(Stack&) = delete; - Stack& operator=(Stack&&) = default; + Stack& operator=(Stack&&) = delete; - value_type* data() const { return buffer.get(); } - size_t size() const { return bufferSize; } + [[nodiscard]] value_type* data() const { return buffer.get(); } + [[nodiscard]] size_t size() const { return bufferSize; } allocator_type get_allocator() const { return allocator; } - const BaseHeader* first() const { return reinterpret_cast(this->data()); } + [[nodiscard]] const BaseHeader* first() const { return reinterpret_cast(this->data()); } static const BaseHeader* firstHeader(std::byte const* buf) { return BaseHeader::get(buf); } static const BaseHeader* lastHeader(std::byte const* buf) { @@ -89,9 +88,9 @@ struct Stack { /// all headers must derive from BaseHeader, in addition also other stacks can be passed to ctor. template >::value, int> = 0> + !std::is_convertible>::value, int> = 0> Stack(FirstArgType&& firstHeader, Headers&&... headers) - : Stack(boost::container::pmr::new_delete_resource(), std::forward(firstHeader), + : Stack(fair::mq::pmr::new_delete_resource(), std::forward(firstHeader), std::forward(headers)...) { } @@ -101,9 +100,13 @@ struct Stack { Stack(const allocator_type allocatorArg, Headers&&... headers) : allocator{allocatorArg}, bufferSize{calculateSize(std::forward(headers)...)}, - buffer{static_cast(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))), freeobj{allocator.resource()}} + buffer{static_cast(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))), freeobj{allocator.resource(), bufferSize}} { - inject(buffer.get(), std::forward(headers)...); + if constexpr (sizeof...(headers) > 1) { + injectAll(buffer.get(), std::forward(headers)...); + } else if (sizeof...(headers) == 1) { + injectBool(buffer.get(), std::forward(headers)..., false); + } } //______________________________________________________________________________________________ @@ -117,7 +120,7 @@ struct Stack { template constexpr static size_t calculateSize(T&& h) noexcept { - //if it's a pointer (to a stack) traverse it + // if it's a pointer (to a stack) traverse it if constexpr (std::is_convertible_v) { const BaseHeader* next = BaseHeader::get(std::forward(h)); if (!next) { @@ -128,23 +131,23 @@ struct Stack { size += next->size(); } return size; - //otherwise get the size directly + // otherwise get the size directly } else { return h.size(); } } - //recursion terminator + // recursion terminator constexpr static size_t calculateSize() { return 0; } private: - allocator_type allocator{boost::container::pmr::new_delete_resource()}; + allocator_type allocator{fair::mq::pmr::new_delete_resource()}; size_t bufferSize{0}; - BufferType buffer{nullptr, freeobj{allocator.resource()}}; + BufferType buffer{nullptr, freeobj{allocator.resource(), 0}}; //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, bool more = false) noexcept + static std::byte* injectBool(std::byte* here, T&& h, bool more) noexcept { using headerType = typename std::remove_cv::type>::type; if (here == nullptr) { @@ -189,11 +192,15 @@ struct Stack { //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, Args&&... args) noexcept + static std::byte* injectAll(std::byte* here, T&& h, Args&&... args) noexcept { bool more = hasNonEmptyArg(args...); - auto alsohere = inject(here, h, more); - return inject(alsohere, args...); + auto alsohere = injectBool(here, h, more); + if constexpr (sizeof...(args) > 1) { + return injectAll(alsohere, args...); + } else { + return injectBool(alsohere, args..., false); + } } //______________________________________________________________________________________________ @@ -222,7 +229,6 @@ struct Stack { } }; -} // namespace header -} // namespace o2 +} // namespace o2::header #endif // HEADERS_STACK_H diff --git a/DataFormats/Headers/include/Headers/SubframeMetadata.h b/DataFormats/Headers/include/Headers/SubframeMetadata.h deleted file mode 100644 index 255fa0ceb8db6..0000000000000 --- a/DataFormats/Headers/include/Headers/SubframeMetadata.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef SUBFRAMEMETADATA_H -#define SUBFRAMEMETADATA_H - -#include - -namespace o2 -{ -namespace data_flow -{ - -struct SubframeMetadata { - // TODO: replace with timestamp struct - // IDEA: not timeframeID because can be calculcated with helper function - // QUESTION: isn't the duration set to ~22ms? - uint64_t startTime = ~(uint64_t)0; - uint64_t duration = ~(uint64_t)0; - - //further meta data to be added - - // putting data specific to FLP origin - int flpIndex; -}; - -// Helper function to derive the timeframe id from the actual timestamp. -// Timestamp is in nanoseconds. Each Timeframe is ~22ms i.e. 2^17 nanoseconds, -// so we can get a unique id by dividing by the timeframe period and masking -// the lower 16 bits. Overlaps will only happen every ~ 22 minutes. -constexpr uint16_t - timeframeIdFromTimestamp(uint64_t timestamp, uint64_t timeFrameDuration) -{ - return (timestamp / timeFrameDuration) & 0xffff; -} - -// A Mockup class to describe some TPC-like payload -struct TPCTestCluster { - float x = 0.f; - float y = 0.f; - float z = 1.5f; - float q = 0.; - uint64_t timeStamp; // the time this thing was digitized/recorded -}; - -struct TPCTestPayload { - std::vector clusters; -}; - -// a mockup class to describe some "ITS" payload -struct ITSRawData { - float x = -1.; - float y = 1.; - uint64_t timeStamp; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/DataFormats/Headers/include/Headers/TimeStamp.h b/DataFormats/Headers/include/Headers/TimeStamp.h index bd4bd8c8b94e9..b6d2b26ea4822 100644 --- a/DataFormats/Headers/include/Headers/TimeStamp.h +++ b/DataFormats/Headers/include/Headers/TimeStamp.h @@ -23,9 +23,7 @@ #include #include // for std::integral_constant -namespace o2 -{ -namespace header +namespace o2::header { // https://lhc-machine-outreach.web.cern.ch/lhc-machine-outreach/collisions.htm @@ -140,12 +138,14 @@ class TimeStamp static_assert(std::is_same::value && std::is_same::value, "only clock and duration types defining the rep and period member types are allowed"); using duration = std::chrono::duration; - if (mUnit == sClockLHC) { + static_assert(sizeof(mUnit) == sizeof(sClockLHC), "size mismatch of mUnit and sClockLHC"); + if (memcmp(&mUnit, &sClockLHC, sizeof(sClockLHC)) == 0) { // cast each part individually, if the precision of the return type // is smaller the values are simply truncated return std::chrono::duration_cast(LHCOrbitClock::duration(mPeriod) + LHCBunchClock::duration(mBCNumber)); } - if (mUnit == sMicroSeconds) { + static_assert(sizeof(mUnit) == sizeof(sMicroSeconds), "size mismatch of mUnit and sMicroSeconds"); + if (memcmp(&mUnit, &sMicroSeconds, sizeof(sMicroSeconds)) == 0) { // TODO: is there a better way to mark the subticks invalid for the // micro seconds representation? First step is probably to remove/rename the // variable @@ -159,7 +159,7 @@ class TimeStamp } // TODO: implement transformation from one unit to the other - //void transform(const TimeUnitID& unit) { + // void transform(const TimeUnitID& unit) { // if (mUnit == unit) return; // ... //} @@ -182,7 +182,6 @@ class TimeStamp }; }; }; -} //namespace header -} //namespace o2 +} // namespace o2::header #endif diff --git a/DataFormats/Headers/test/testDataHeader.cxx b/DataFormats/Headers/test/testDataHeader.cxx index 0703fc6c3ae71..2403c1a6230be 100644 --- a/DataFormats/Headers/test/testDataHeader.cxx +++ b/DataFormats/Headers/test/testDataHeader.cxx @@ -280,8 +280,8 @@ BOOST_AUTO_TEST_CASE(headerStack_test) Stack s2{s1, meta}; BOOST_CHECK(s2.size() == s1.size() + sizeof(decltype(meta))); - //check dynamic construction - where we don't have the type information and need to - //work with BaseHeader pointers + // check dynamic construction - where we don't have the type information and need to + // work with BaseHeader pointers const test::MetaHeader thead{2}; o2::header::BaseHeader const* bname = reinterpret_cast(&thead); Stack ds2(s1, *bname); @@ -313,8 +313,8 @@ BOOST_AUTO_TEST_CASE(headerStack_test) BOOST_REQUIRE(h3 != nullptr); BOOST_CHECK(h3->secret == 42); - //test constructing from a buffer and an additional header - using namespace boost::container::pmr; + // test constructing from a buffer and an additional header + using namespace fair::mq::pmr; Stack s5(new_delete_resource(), s1.data(), Stack{}, meta); BOOST_CHECK(s5.size() == s1.size() + sizeof(meta)); // check if we can find the header even though there was an empty stack in the middle diff --git a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h b/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h deleted file mode 100644 index 59be2a86c3c2b..0000000000000 --- a/DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -//-*- Mode: C++ -*- - -#ifndef TPCRAWCLUSTER_H -#define TPCRAWCLUSTER_H -//**************************************************************************** -//* This file is free software: you can redistribute it and/or modify * -//* it under the terms of the GNU General Public License as published by * -//* the Free Software Foundation, either version 3 of the License, or * -//* (at your option) any later version. * -//* * -//* Primary Authors: Matthias Richter * -//* * -//* The authors make no claims about the suitability of this software for * -//* any purpose. It is provided "as is" without express or implied warranty. * -//**************************************************************************** - -// @file TPCRawCluster.h -// @author Matthias Richter -// @since 2015-09-27 -// @brief ALICE HLT TPC raw cluster structure and tools - -#include -#include // ifstream -#include // memcpy - -namespace o2 -{ -namespace AliceHLT -{ - -/** - * @struct RawCluster - * This is a redefinition from AliRoot/HLT/TPCLib/AliHLTTPCRawCluster.h for the - * sake of reading HLT TPC raw cluster files into O2. - * - * TODO: there is no dependence on AliRoot, however, a test needs to be added - * to check consistency if AliRoot is available in the build. - */ -struct RawCluster { - - int16_t GetPadRow() const { return fPadRow; } - float GetPad() const { return fPad; } - float GetTime() const { return fTime; } - float GetSigmaPad2() const { return fSigmaPad2; } - float GetSigmaTime2() const { return fSigmaTime2; } - int32_t GetCharge() const { return fCharge; } - int32_t GetQMax() const { return fQMax; } - bool GetFlagSplitPad() const { return (fFlags & (1 << 0)); } - bool GetFlagSplitTime() const { return (fFlags & (1 << 1)); } - bool GetFlagSplitAny() const { return (fFlags & 3); } - uint16_t GetFlags() const { return (fFlags); } - - int16_t fPadRow; - uint16_t fFlags; //Flags: (1 << 0): Split in pad direction - // (1 << 1): Split in time direction - //During cluster merging, flags are or'd - float fPad; - float fTime; - float fSigmaPad2; - float fSigmaTime2; - uint16_t fCharge; - uint16_t fQMax; -}; - -/** - * @struct RawClusterData - * Header data struct for a raw cluster block - */ -struct RawClusterData { - uint32_t fVersion; // version number - uint32_t fCount; // number of clusters - RawCluster fClusters[0]; // array of clusters -}; - -std::ostream& operator<<(std::ostream& stream, const RawCluster& cluster) -{ - stream << "TPCRawCluster:" - << " " << cluster.GetPadRow() - << " " << cluster.GetPad() - << " " << cluster.GetTime() - << " " << cluster.GetSigmaPad2() - << " " << cluster.GetSigmaTime2() - << " " << cluster.GetCharge() - << " " << cluster.GetQMax(); - return stream; -} - -/** - * @class RawClusterArray Wrapper to binary data block of HLT TPC raw clusters - * Container class which provides access to the content of a binary block of - * HLT TPC raw clusters. - */ -class RawClusterArray -{ - public: - RawClusterArray() : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) {} - RawClusterArray(const char* filename) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(filename); - } - RawClusterArray(unsigned char* buffer, int size) : mBuffer(nullptr), mBufferSize(0), mNClusters(0), mClusters(NULL), mClustersEnd(NULL) - { - init(buffer, size); - } - ~RawClusterArray() {} - - typedef uint8_t Buffer_t; - - int init(const char* filename) - { - std::ifstream input(filename, std::ifstream::binary); - clear(0); - if (input) { - // get length of file: - input.seekg(0, input.end); - int length = input.tellg(); - input.seekg(0, input.beg); - - // allocate memory: - mBuffer = new Buffer_t[length]; - mBufferSize = length; - - // read data as a block: - input.read(reinterpret_cast(mBuffer), length); - if (!input.good()) { - clear(-1); - std::cerr << "failed to read " << length << " byte(s) from file " << filename << std::endl; - } - - input.close(); - return init(); - } - std::cerr << "failed to open file " << filename << std::endl; - return -1; - } - - int init(unsigned char* buffer, int size) - { - if (!buffer || size <= 0) - return -1; - clear(0); - mBuffer = new Buffer_t[size]; - mBufferSize = size; - memcpy(mBuffer, buffer, size); - return init(); - } - - int GetNClusters() const { return mNClusters; } - - RawCluster* begin() { return mClusters; } - - RawCluster* end() { return mClustersEnd; } - - RawCluster& operator[](int i) - { - if (i + 1 > mNClusters) { - // runtime exeption? - static RawCluster dummy; - return dummy; - } - return *(mClusters + i); - } - - void print() { print(std::cout); } - - template - StreamT& print(StreamT& stream) - { - std::cout << "RawClusterArray: " << mNClusters << " cluster(s)" << std::endl; - for (RawCluster* cluster = mClusters; cluster != mClustersEnd; cluster++) { - std::cout << " " << *cluster << std::endl; - } - return stream; - } - - private: - int init() - { - if (mBuffer == nullptr || mBufferSize == 0) - return 0; - if (mBufferSize < sizeof(RawClusterData)) - return -1; - RawClusterData& clusterData = *reinterpret_cast(mBuffer); - - if (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData) > mBufferSize) { - std::cerr << "Format error, " << clusterData.fCount << " cluster(s) " - << "would require " - << (clusterData.fCount * sizeof(RawCluster) + sizeof(RawClusterData)) - << " byte(s), but only " << mBufferSize << " available" << std::endl; - return clear(-1); - } - - mNClusters = clusterData.fCount; - mClusters = clusterData.fClusters; - mClustersEnd = mClusters + mNClusters; - - return mNClusters; - } - - int clear(int returnValue) - { - mNClusters = 0; - mClusters = NULL; - mClustersEnd = NULL; - delete[] mBuffer; - mBuffer = nullptr; - mBufferSize = 0; - - return returnValue; - } - - Buffer_t* mBuffer; - int mBufferSize; - int mNClusters; - RawCluster* mClusters; - RawCluster* mClustersEnd; -}; - -}; // namespace AliceHLT -}; // namespace o2 -#endif diff --git a/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h b/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h index 42dd69aff38e3..b52f5c715575e 100644 --- a/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h +++ b/DataFormats/MemoryResources/include/MemoryResources/MemoryResources.h @@ -111,172 +111,16 @@ class MessageResource : public FairMQMemoryResource } }; -//__________________________________________________________________________________________________ -// A spectator pmr memory resource which only watches the memory of the underlying buffer, does not -// carry out real allocation. It owns the underlying buffer which is destroyed on deallocation. -template -class SpectatorMemoryResource : public boost::container::pmr::memory_resource -{ - public: - using buffer_type = BufferType; - - SpectatorMemoryResource() noexcept = delete; - SpectatorMemoryResource(const SpectatorMemoryResource&) noexcept = delete; - SpectatorMemoryResource(SpectatorMemoryResource&&) noexcept = default; - SpectatorMemoryResource& operator=(const SpectatorMemoryResource&) = delete; - SpectatorMemoryResource& operator=(SpectatorMemoryResource&&) = default; - ~SpectatorMemoryResource() noexcept override = default; - - // the resource is the pointer managed by unique_ptr - template - SpectatorMemoryResource(std::unique_ptr&& buffer, size_t size) - : mBuffer{std::move(buffer)}, mPointer{mBuffer.get()}, mSize{size} - { - } - - // the resource is the data of the vector managed by unique ptr - template - SpectatorMemoryResource(std::unique_ptr, typename buffer_type::deleter_type>&& buffer) - : mBuffer{std::move(buffer)}, mPointer{mBuffer->data()}, mSize{mBuffer->size() * sizeof(T)} - { - } - - // TODO: the underlying resource can be directly the vector or the read only buffer - protected: - void* do_allocate(std::size_t bytes, std::size_t /*alignment*/) override - { - if (mSize > 0) { - if (bytes > mSize) { - throw std::bad_alloc(); - } - mSize = 0; - return mPointer; - } - throw std::runtime_error("Can not allocate: this memory resource is only supposed to provide spectator access to external buffer"); - } - - void do_deallocate(void* p, std::size_t /*bytes*/, std::size_t /*alignment*/) override - { - if (p == mPointer) { - mBuffer.reset(); - mPointer = nullptr; - } else if (mPointer == nullptr) { - // there is an error in the logic flow, this should never be called more than once - throw std::logic_error("underlying controlled resource has been released already"); - } else { - throw std::logic_error("this resource can only deallocate the controlled resource pointer"); - } - } - bool do_is_equal(const memory_resource& /*other*/) const noexcept override - { - // uniquely owns the underlying resource, can never be equal to any other instance - return false; - } - - private: - buffer_type mBuffer; - void* mPointer = nullptr; - size_t mSize = 0; -}; - -//__________________________________________________________________________________________________ -// This in general (as in STL) is a bad idea, but here it is safe to inherit from an allocator since we -// have no additional data and only override some methods so we don't get into slicing and other problems. -template -class SpectatorAllocator : public boost::container::pmr::polymorphic_allocator -{ - public: - using boost::container::pmr::polymorphic_allocator::polymorphic_allocator; - using propagate_on_container_move_assignment = std::true_type; - - // skip default construction of empty elements - // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and - // quickly construct large vectors while skipping the element initialization. - template - void construct(U*) - { - } - - // dont try to call destructors, makes no sense since resource is managed externally AND allowed - // types cannot have side effects - template - void destroy(U*) - { - } - - T* allocate(size_t size) { return reinterpret_cast(this->resource()->allocate(size * sizeof(T), 64)); } - void deallocate(T* ptr, size_t size) - { - this->resource()->deallocate(const_cast::type*>(ptr), size); - } -}; - -//__________________________________________________________________________________________________ -/// This allocator has a pmr-like interface, but keeps the unique MessageResource as internal state, -/// allowing full resource (associated message) management internally without any global state. -template -class OwningMessageSpectatorAllocator -{ - public: - using value_type = T; - - MessageResource mResource; - - OwningMessageSpectatorAllocator() noexcept = default; - OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator&) noexcept = default; - OwningMessageSpectatorAllocator(OwningMessageSpectatorAllocator&&) noexcept = default; - OwningMessageSpectatorAllocator(MessageResource&& resource) noexcept : mResource{resource} {} - - template - OwningMessageSpectatorAllocator(const OwningMessageSpectatorAllocator& other) noexcept : mResource(other.mResource) - { - } - - OwningMessageSpectatorAllocator& operator=(const OwningMessageSpectatorAllocator& other) - { - mResource = other.mResource; - return *this; - } - - OwningMessageSpectatorAllocator select_on_container_copy_construction() const - { - return OwningMessageSpectatorAllocator(); - } - - boost::container::pmr::memory_resource* resource() { return &mResource; } - - // skip default construction of empty elements - // this is important for two reasons: one: it allows us to adopt an existing buffer (e.g. incoming message) and - // quickly construct large vectors while skipping the element initialization. - template - void construct(U*) - { - } - - // dont try to call destructors, makes no sense since resource is managed externally AND allowed - // types cannot have side effects - template - void destroy(U*) - { - } - - T* allocate(size_t size) { return reinterpret_cast(mResource.allocate(size * sizeof(T), 64)); } - void deallocate(T* ptr, size_t size) - { - mResource.deallocate(const_cast::type*>(ptr), size); - } -}; - // The NoConstructAllocator behaves like the normal pmr vector but does not call constructors / destructors template -class NoConstructAllocator : public boost::container::pmr::polymorphic_allocator +class NoConstructAllocator : public fair::mq::pmr::polymorphic_allocator { public: - using boost::container::pmr::polymorphic_allocator::polymorphic_allocator; + using fair::mq::pmr::polymorphic_allocator::polymorphic_allocator; using propagate_on_container_move_assignment = std::true_type; template - NoConstructAllocator(Args&&... args) : boost::container::pmr::polymorphic_allocator(std::forward(args)...) + NoConstructAllocator(Args&&... args) : fair::mq::pmr::polymorphic_allocator(std::forward(args)...) { } @@ -301,20 +145,9 @@ class NoConstructAllocator : public boost::container::pmr::polymorphic_allocator //__________________________________________________________________________________________________ //__________________________________________________________________________________________________ -using ByteSpectatorAllocator = SpectatorAllocator; -using BytePmrAllocator = boost::container::pmr::polymorphic_allocator; +using BytePmrAllocator = fair::mq::pmr::polymorphic_allocator; template -using vector = std::vector>; - -//__________________________________________________________________________________________________ -/// Return a std::vector spanned over the contents of the message, takes ownership of the message -template -auto adoptVector(size_t nelem, fair::mq::MessagePtr message) -{ - static_assert(std::is_trivially_destructible::value); - return std::vector>( - nelem, OwningMessageSpectatorAllocator(MessageResource{std::move(message)})); -}; +using vector = std::vector>; //__________________________________________________________________________________________________ /// Get the allocator associated to a transport factory diff --git a/DataFormats/MemoryResources/test/testMemoryResources.cxx b/DataFormats/MemoryResources/test/testMemoryResources.cxx index 592e76c8ef1f4..a49cd00d75255 100644 --- a/DataFormats/MemoryResources/test/testMemoryResources.cxx +++ b/DataFormats/MemoryResources/test/testMemoryResources.cxx @@ -47,12 +47,12 @@ int testData::nconstructions = 0; BOOST_AUTO_TEST_CASE(transportallocatormap_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "transportallocatormap_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); BOOST_CHECK(allocZMQ != nullptr && allocSHM != allocZMQ); @@ -60,16 +60,16 @@ BOOST_AUTO_TEST_CASE(transportallocatormap_test) BOOST_CHECK(_tmp == allocZMQ); } -using namespace boost::container::pmr; +using namespace fair::mq::pmr; BOOST_AUTO_TEST_CASE(allocator_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000 + 1}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "allocator_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); @@ -88,26 +88,17 @@ BOOST_AUTO_TEST_CASE(allocator_test) } testData::nconstructions = 0; - { - std::vector> v(SpectatorAllocator{allocZMQ}); - v.reserve(3); - BOOST_CHECK(allocZMQ->getNumberOfMessages() == 1); - v.emplace_back(1); - v.emplace_back(2); - v.emplace_back(3); - BOOST_CHECK(testData::nconstructions == 3); - } BOOST_CHECK(allocZMQ->getNumberOfMessages() == 0); } BOOST_AUTO_TEST_CASE(getMessage_test) { - size_t session{fair::mq::tools::UuidHash()}; + size_t session{(size_t)getpid() * 1000 + 2}; fair::mq::ProgOptions config; config.SetProperty("session", std::to_string(session)); auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); + auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem", "getMessage_test", &config); auto allocZMQ = getTransportAllocator(factoryZMQ.get()); auto allocSHM = getTransportAllocator(factorySHM.get()); @@ -147,73 +138,6 @@ BOOST_AUTO_TEST_CASE(getMessage_test) messageArray = static_cast(message->GetData()); BOOST_CHECK(messageArray[0] == 4 && messageArray[1] == 5 && messageArray[2] == 6); - { - std::vector> v(SpectatorAllocator{allocSHM}); - } -} - -BOOST_AUTO_TEST_CASE(adoptVector_test) -{ - size_t session{fair::mq::tools::UuidHash()}; - fair::mq::ProgOptions config; - config.SetProperty("session", std::to_string(session)); - - auto factoryZMQ = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - auto factorySHM = fair::mq::TransportFactory::CreateTransportFactory("shmem"); - auto allocZMQ = getTransportAllocator(factoryZMQ.get()); - auto allocSHM = getTransportAllocator(factorySHM.get()); - - testData::nconstructions = 0; - - //Create a bogus message - auto message = factoryZMQ->CreateMessage(3 * sizeof(testData)); - auto messageAddr = message.get(); - testData tmpBuf[3] = {3, 2, 1}; - std::memcpy(message->GetData(), tmpBuf, 3 * sizeof(testData)); - - auto adoptedOwner = adoptVector(3, std::move(message)); - BOOST_CHECK(adoptedOwner[0].i == 3); - BOOST_CHECK(adoptedOwner[1].i == 2); - BOOST_CHECK(adoptedOwner[2].i == 1); - - auto reclaimedMessage = o2::pmr::getMessage(std::move(adoptedOwner)); - BOOST_CHECK(reclaimedMessage.get() == messageAddr); - BOOST_CHECK(adoptedOwner.size() == 0); - - auto modified = adoptVector(3, std::move(reclaimedMessage)); - modified.emplace_back(9); - BOOST_CHECK(modified[3].i == 9); - BOOST_CHECK(modified.size() == 4); - BOOST_CHECK(testData::nconstructions == 7); - auto modifiedMessage = getMessage(std::move(modified)); - BOOST_CHECK(modifiedMessage != nullptr); - BOOST_CHECK(modifiedMessage.get() != messageAddr); -} - -BOOST_AUTO_TEST_CASE(test_SpectatorMemoryResource) -{ - constexpr int size = 5; - auto buffer = std::make_unique(size); - auto const* bufferdata = buffer.get(); - SpectatorMemoryResource resource(std::move(buffer), size * sizeof(int)); - std::vector> bufferclone(size, o2::pmr::SpectatorAllocator(&resource)); - BOOST_CHECK(bufferclone.data() == bufferdata); - BOOST_CHECK(bufferclone.size() == size); - BOOST_CHECK_THROW(bufferclone.resize(2 * size), std::runtime_error); - - auto vecbuf = std::make_unique>(size); - auto const* vectordata = vecbuf->data(); - SpectatorMemoryResource vecresource(std::move(vecbuf)); - std::vector> vecclone(size, o2::pmr::SpectatorAllocator(&vecresource)); - BOOST_CHECK(vecclone.data() == vectordata); - BOOST_CHECK(vecclone.size() == size); - BOOST_CHECK_THROW(vecclone.resize(2 * size), std::runtime_error); - - std::vector> vecmove; - vecmove = std::move(vecclone); - BOOST_CHECK(vecclone.size() == 0); - BOOST_CHECK(vecmove.data() == vectordata); - BOOST_CHECK(vecmove.size() == size); } }; // namespace o2::pmr diff --git a/DataFormats/Parameters/CMakeLists.txt b/DataFormats/Parameters/CMakeLists.txt index 3539e9e11fd1e..ed137a42f6565 100644 --- a/DataFormats/Parameters/CMakeLists.txt +++ b/DataFormats/Parameters/CMakeLists.txt @@ -14,7 +14,8 @@ o2_add_library(DataFormatsParameters src/GRPLHCIFData.cxx src/GRPECSObject.cxx src/GRPMagField.cxx - PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonConstants + src/AggregatedRunInfo.cxx + PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonConstants O2::CommonTypes O2::CCDB O2::DetectorsCommonDataFormats) @@ -24,6 +25,7 @@ o2_target_root_dictionary(DataFormatsParameters include/DataFormatsParameters/GRPLHCIFData.h include/DataFormatsParameters/GRPECSObject.h include/DataFormatsParameters/GRPMagField.h + include/DataFormatsParameters/AggregatedRunInfo.h LINKDEF src/ParametersDataLinkDef.h) o2_add_executable(simgrp-tool diff --git a/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h new file mode 100644 index 0000000000000..d0347114b5b4c --- /dev/null +++ b/DataFormats/Parameters/include/DataFormatsParameters/AggregatedRunInfo.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GRPECSObject.h +/// \brief Header of the AggregatedRunInfo struct +/// \author ruben.shahoyan@cern.ch sandro.wenzel@cern.ch + +#ifndef ALICEO2_DATA_AGGREGATEDRUNINFO_H_ +#define ALICEO2_DATA_AGGREGATEDRUNINFO_H_ + +#include +#include "CCDB/BasicCCDBManager.h" + +namespace o2::parameters +{ + +class GRPECSObject; +class GRPLHCIFData; + +/// Composite struct where one may collect important global properties of data "runs" +/// aggregated from various sources (GRPECS, RunInformation CCDB entries, etc.). +/// Also offers the authoritative algorithms to collect these information for easy reuse +/// across various algorithms (anchoredMC, analysis, ...) +struct AggregatedRunInfo { + int runNumber = 0; // run number + int64_t sor = 0; // best known timestamp for the start of run + int64_t eor = 0; // best known timestamp for end of run + int64_t orbitsPerTF = 0; // number of orbits per TF (takes precedence over that in GRPECS) + int64_t orbitReset = 0; // timestamp of orbit reset before run + int64_t orbitSOR = 0; // orbit when run starts after orbit reset + int64_t orbitEOR = 0; // orbit when run ends after orbit reset + + // we may have pointers to actual data source objects GRPECS, ... + const o2::parameters::GRPECSObject* grpECS = nullptr; // pointer to GRPECSobject (fetched during struct building) + const o2::parameters::GRPLHCIFData* grpLHC = nullptr; + + static AggregatedRunInfo buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec, const o2::parameters::GRPLHCIFData* grplhcif = nullptr); + + // fills and returns AggregatedRunInfo for a given data run number. + static AggregatedRunInfo buildAggregatedRunInfo_DATA(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber); + + // Returns the meta-data (MCProdInfo) associated to production lpm_prod_tag (performed by username) + static std::map getMCProdInfo(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber, + std::string const& lpm_prod_tag, std::string const& username = "aliprod"); + + // function that adjusts with values from MC + void adjust_from_MC(o2::ccdb::CCDBManagerInstance& ccdb, int run_number, std::string const& lpm_prod_tag, std::string const& username = "aliprod"); + + // Fills and returns AggregatedRunInfo for a given run number. + // If a non-empty lpm_prod_tag is given, it will potentially override values with specifics from a + // MC production identified by that tag and username. + static AggregatedRunInfo buildAggregatedRunInfo(o2::ccdb::CCDBManagerInstance& ccdb, + int runnumber, + std::string const& lpm_prod_tag = "", + std::string const& username = "aliprod"); + + ClassDefNV(AggregatedRunInfo, 1); +}; + +} // namespace o2::parameters + +#endif diff --git a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h index 7ac6cd6b00133..ddcda8f66507c 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h @@ -41,6 +41,7 @@ class GRPLHCIFData std::pair getBeamEnergyPerZWithTime() const { return mBeamEnergyPerZ; } int32_t getBeamEnergyPerZ() const { return mBeamEnergyPerZ.second; } + float getBeamEnergyPerZinGeV() const { return mBeamEnergyPerZ.second * 0.12; } long getBeamEnergyPerZTime() const { return mBeamEnergyPerZ.first; } void setBeamEnergyPerZWithTime(std::pair p) { mBeamEnergyPerZ = p; } void setBeamEnergyPerZWithTime(long t, int32_t v) { mBeamEnergyPerZ = std::make_pair(t, v); } @@ -84,12 +85,18 @@ class GRPLHCIFData /// getters/setters for given beam A and Z info, encoded as A<<16+Z int getBeamZ(beamDirection beam) const { return mBeamAZ[static_cast(beam)] & 0xffff; } int getBeamA(beamDirection beam) const { return mBeamAZ[static_cast(beam)] >> 16; } + int getBeamZ(int beam) const { return mBeamAZ[beam] & 0xffff; } + int getBeamA(int beam) const { return mBeamAZ[beam] >> 16; } float getBeamZoverA(beamDirection beam) const; + float getBeamZoverA(int beam) const; void setBeamAZ(int a, int z, beamDirection beam) { mBeamAZ[static_cast(beam)] = (a << 16) + z; } void setBeamAZ(beamDirection beam); void setBeamAZ(); /// getters/setters for beam energy per charge and per nucleon float getBeamEnergyPerNucleon(beamDirection beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleon(int beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(beamDirection beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(int beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } /// calculate center of mass energy per nucleon collision float getSqrtS() const; /// helper function for BunchFilling @@ -119,6 +126,14 @@ inline float GRPLHCIFData::getBeamZoverA(beamDirection b) const return a ? getBeamZ(b) / static_cast(a) : 0.f; } +//______________________________________________ +inline float GRPLHCIFData::getBeamZoverA(int b) const +{ + // Z/A of beam 0 or 1 + int a = getBeamA(b); + return a ? getBeamZ(b) / static_cast(a) : 0.f; +} + } // namespace parameters } // namespace o2 #endif diff --git a/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h b/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h index e11f9946e8e35..f9749c2b2a36f 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/GRPMagField.h @@ -34,8 +34,18 @@ class GRPMagField ~GRPMagField() = default; /// getters/setters for magnets currents - o2::units::Current_t getL3Current() const { return mL3Current; } - o2::units::Current_t getDipoleCurrent() const { return mDipoleCurrent; } + o2::units::Current_t getL3Current() const + { + static float v = checkL3Override(); + return v == NOOVERRIDEVAL ? mL3Current : v; + } + + o2::units::Current_t getDipoleCurrent() const + { + static float v = checkDipoleOverride(); + return v == NOOVERRIDEVAL ? mDipoleCurrent : v; + } + bool getFieldUniformity() const { return mUniformField; } int8_t getNominalL3Field(); void setL3Current(o2::units::Current_t v) { mL3Current = v; } @@ -54,6 +64,10 @@ class GRPMagField int8_t mNominalL3Field = 0; //!< Nominal L3 field deduced from mL3Current bool mNominalL3FieldValid = false; //!< Has the field been computed (for caching) + static constexpr float NOOVERRIDEVAL = 1e99; + static float checkL3Override(); + static float checkDipoleOverride(); + ClassDefNV(GRPMagField, 2); }; @@ -62,7 +76,7 @@ inline int8_t GRPMagField::getNominalL3Field() // compute nominal L3 field in kG if (mNominalL3FieldValid == false) { - mNominalL3Field = std::lround(5.f * mL3Current / 30000.f); + mNominalL3Field = std::lround(5.f * getL3Current() / 30000.f); mNominalL3FieldValid = true; } return mNominalL3Field; diff --git a/DataFormats/Parameters/src/AggregatedRunInfo.cxx b/DataFormats/Parameters/src/AggregatedRunInfo.cxx new file mode 100644 index 0000000000000..40402a33af68b --- /dev/null +++ b/DataFormats/Parameters/src/AggregatedRunInfo.cxx @@ -0,0 +1,151 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AggregatedRunInfo.cxx +/// \author sandro.wenzel@cern.ch + +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsParameters/GRPECSObject.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "CommonConstants/LHCConstants.h" +#include "Framework/Logger.h" +#include + +using namespace o2::parameters; + +o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo_DATA(o2::ccdb::CCDBManagerInstance& ccdb, int runnumber) +{ + // TODO: could think about caching results per runnumber to + // avoid going to CCDB multiple times ---> but should be done inside the CCDBManagerInstance + + // we calculate the first orbit of a run based on sor (start-of-run) and eor + // we obtain these by calling getRunDuration + auto [sor, eor] = ccdb.getRunDuration(runnumber); + + // determine a good timestamp to query OrbitReset for this run + // --> the middle of the run is very appropriate and safer than just sor + auto run_mid_timestamp = sor + (eor - sor) / 2; + + // query the time of the orbit reset (when orbit is defined to be 0) + auto ctpx = ccdb.getForTimeStamp>("CTP/Calib/OrbitReset", run_mid_timestamp); + int64_t tsOrbitReset = (*ctpx)[0]; // us + + // get timeframe length from GRPECS + std::map metadata; + metadata["runNumber"] = Form("%d", runnumber); + auto grpecs = ccdb.getSpecific("GLO/Config/GRPECS", run_mid_timestamp, metadata); + auto grplhcif = ccdb.getSpecific("GLO/Config/GRPLHCIF", run_mid_timestamp); // no run metadata here + bool oldFatalState = ccdb.getFatalWhenNull(); + ccdb.setFatalWhenNull(false); + auto ctp_first_run_orbit = ccdb.getForTimeStamp>("CTP/Calib/FirstRunOrbit", run_mid_timestamp); + ccdb.setFatalWhenNull(oldFatalState); + return buildAggregatedRunInfo(runnumber, sor, eor, tsOrbitReset, grpecs, ctp_first_run_orbit, grplhcif); +} + +o2::parameters::AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(int runnumber, long sorMS, long eorMS, long orbitResetMUS, const o2::parameters::GRPECSObject* grpecs, const std::vector* ctfFirstRunOrbitVec, const o2::parameters::GRPLHCIFData* grplhcif) +{ + auto nOrbitsPerTF = grpecs->getNHBFPerTF(); + // calculate SOR/EOR orbits + int64_t orbitSOR = -1; + if (ctfFirstRunOrbitVec && ctfFirstRunOrbitVec->size() >= 3) { // if we have CTP first run orbit available, we should use it + int64_t creation_timeIGNORED = (*ctfFirstRunOrbitVec)[0]; // do not use CTP start of run time! + int64_t ctp_run_number = (*ctfFirstRunOrbitVec)[1]; + int64_t ctp_orbitSOR = (*ctfFirstRunOrbitVec)[2]; + if (creation_timeIGNORED == -1 && ctp_run_number == -1 && ctp_orbitSOR == -1) { + LOGP(warn, "Default dummy CTP/Calib/FirstRunOrbit was provides, ignoring"); + } else if (ctp_run_number == runnumber) { + orbitSOR = ctp_orbitSOR; + auto sor_new = (int64_t)((orbitResetMUS + ctp_orbitSOR * o2::constants::lhc::LHCOrbitMUS) / 1000.); + if (sor_new != sorMS) { + LOGP(warn, "Adjusting SOR from {} to {}", sorMS, sor_new); + sorMS = sor_new; + } + } else { + LOGP(error, "AggregatedRunInfo: run number inconsistency found (asked: {} vs CTP found: {}, ignoring", runnumber, ctp_run_number); + } + } + int64_t orbitEOR = (eorMS * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS; + if (runnumber > 523897) { // condition was introduced starting from LHC22o + orbitEOR = orbitEOR / nOrbitsPerTF * nOrbitsPerTF; + } + if (orbitSOR < 0) { // extract from SOR + orbitSOR = (sorMS * 1000 - orbitResetMUS) / o2::constants::lhc::LHCOrbitMUS; + if (runnumber > 523897) { + orbitSOR = (orbitSOR / nOrbitsPerTF + 1) * nOrbitsPerTF; + } + } + return AggregatedRunInfo{runnumber, sorMS, eorMS, nOrbitsPerTF, orbitResetMUS, orbitSOR, orbitEOR, grpecs, grplhcif}; +} + +namespace +{ + +// get path where to find MC production info +std::string getFullPath_MC(std::string const& username, std::string const& lpm_prod_tag) +{ + // construct the path where to lookup + std::string path = "/Users/" + std::string(1, username[0]) + "/" + username; + std::string fullpath = path + "/" + "MCProdInfo/" + lpm_prod_tag; + return fullpath; +} + +} // namespace + +std::map AggregatedRunInfo::getMCProdInfo(o2::ccdb::CCDBManagerInstance& ccdb, + int run_number, + std::string const& lpm_prod_tag, + std::string const& username) +{ + std::map metaDataFilter; + metaDataFilter["LPMProductionTag"] = lpm_prod_tag; + + // fetch the meta information for MC productions + auto header_data = ccdb.getCCDBAccessor().retrieveHeaders(getFullPath_MC(username, lpm_prod_tag), metaDataFilter, run_number); + return header_data; +} + +void AggregatedRunInfo::adjust_from_MC(o2::ccdb::CCDBManagerInstance& ccdb, + int run_number, + std::string const& lpm_prod_tag, + std::string const& username) +{ + auto header_data = AggregatedRunInfo::getMCProdInfo(ccdb, run_number, lpm_prod_tag, username); + + // adjust timeframe length if we find entry for MC production + auto iter = header_data.find("OrbitsPerTF"); + if (iter != header_data.end()) { + auto mc_orbitsPerTF = std::stoi(iter->second); + if (mc_orbitsPerTF != orbitsPerTF) { + LOG(info) << "Adjusting OrbitsPerTF from " << orbitsPerTF << " to " << mc_orbitsPerTF << " based on differing MC info"; + orbitsPerTF = mc_orbitsPerTF; + } + } else { + LOG(warn) << "No OrbitsPerTF information found for MC production " << lpm_prod_tag << " and run number " << run_number; + } +} + +AggregatedRunInfo AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::CCDBManagerInstance& ccdb, int run_number, std::string const& lpm_prod_tag, std::string const& username) +{ + // (a) lookup the AggregatedRunInfo for the data run + // (b) modify/overwrite the info object with MC specific settings if lpm_prod_tag is given + + auto original_info = buildAggregatedRunInfo_DATA(ccdb, run_number); + + if (lpm_prod_tag.size() == 0) { + return original_info; + } + + // in this case we adjust the info from MC + original_info.adjust_from_MC(ccdb, run_number, lpm_prod_tag, username); + + return original_info; +} diff --git a/DataFormats/Parameters/src/GRPLHCIFData.cxx b/DataFormats/Parameters/src/GRPLHCIFData.cxx index dcbca0932cf69..d39569f79376b 100644 --- a/DataFormats/Parameters/src/GRPLHCIFData.cxx +++ b/DataFormats/Parameters/src/GRPLHCIFData.cxx @@ -28,6 +28,8 @@ using namespace o2::constants::lhc; const std::unordered_map GRPLHCIFData::mZtoA = { {1, 1}, + {8, 16}, + {10, 20}, {82, 208}}; //_______________________________________________ @@ -60,8 +62,8 @@ void GRPLHCIFData::setBeamAZ() float GRPLHCIFData::getSqrtS() const { // get center of mass energy - double e0 = getBeamEnergyPerNucleon(BeamC); - double e1 = getBeamEnergyPerNucleon(BeamA); + double e0 = getBeamEnergyPerNucleonInGeV(BeamC); + double e1 = getBeamEnergyPerNucleonInGeV(BeamA); if (e0 <= MassProton || e1 <= MassProton) { return 0.f; } diff --git a/DataFormats/Parameters/src/GRPMagField.cxx b/DataFormats/Parameters/src/GRPMagField.cxx index e3dd412f0d118..8f5ae0b414998 100644 --- a/DataFormats/Parameters/src/GRPMagField.cxx +++ b/DataFormats/Parameters/src/GRPMagField.cxx @@ -41,3 +41,25 @@ void GRPMagField::print() const { printf("magnet currents (A) L3 = %.3f, Dipole = %.f; uniformity = %s\n", getL3Current(), getDipoleCurrent(), mUniformField ? "true" : "false"); } + +o2::units::Current_t GRPMagField::checkDipoleOverride() +{ + static float v = getenv("O2_OVERRIDE_DIPOLE_CURRENT") ? atof(getenv("O2_OVERRIDE_DIPOLE_CURRENT")) : NOOVERRIDEVAL; + static bool alarmShown = false; + if (v != NOOVERRIDEVAL && !alarmShown) { + LOGP(error, "Overriding DIPOLE current to {}", v); + alarmShown = true; + } + return v; +} + +o2::units::Current_t GRPMagField::checkL3Override() +{ + static float v = getenv("O2_OVERRIDE_L3_CURRENT") ? atof(getenv("O2_OVERRIDE_L3_CURRENT")) : NOOVERRIDEVAL; + static bool alarmShown = false; + if (v != NOOVERRIDEVAL && !alarmShown) { + LOGP(error, "Overriding L3 current to {}", v); + alarmShown = true; + } + return v; +} diff --git a/DataFormats/Parameters/src/GRPTool.cxx b/DataFormats/Parameters/src/GRPTool.cxx index 86e4b5002dc05..e7561e6fc1ef6 100644 --- a/DataFormats/Parameters/src/GRPTool.cxx +++ b/DataFormats/Parameters/src/GRPTool.cxx @@ -57,13 +57,15 @@ struct Options { std::string outprefix = ""; std::string fieldstring = ""; std::string bcPatternFile = ""; - bool print = false; // whether to print outcome of GRP operation + bool print = false; // whether to print outcome of GRP operation bool lhciffromccdb = false; // whether only to take GRPLHCIF from CCDB std::string publishto = ""; std::string ccdbhost = "http://alice-ccdb.cern.ch"; bool isRun5 = false; // whether or not this is supposed to be a Run5 detector configuration std::string vertex = "ccdb"; std::string configKeyValues = ""; + uint64_t timestamp = 0; + std::string detectorList; // detector layout }; namespace @@ -77,10 +79,16 @@ class CCDBHelper auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(api, opts.run); runStart = soreor.first; runEnd = soreor.second; + if (opts.timestamp > 0) { + timestamp = opts.timestamp; + } else { + timestamp = runStart; + } } o2::ccdb::CcdbApi api; uint64_t runStart = -1; uint64_t runEnd = -1; + uint64_t timestamp = -1; }; } // namespace @@ -198,7 +206,7 @@ bool publish(std::string const& filename, std::string const& path, std::string C try { o2::utils::createDirectoriesIfAbsent(targetdir); } catch (std::exception e) { - LOGP(error, fmt::format("Could not create local snapshot cache directory {}, reason: {}", targetdir, e.what())); + LOGP(error, "Could not create local snapshot cache directory {}, reason: {}", targetdir, e.what()); return false; } @@ -218,7 +226,7 @@ bool anchor_GRPs(Options const& opts, std::vector const& paths = {" gCCDBWrapper = std::move(std::make_unique(opts)); } // fix the timestamp early - uint64_t runStart = gCCDBWrapper->runStart; + uint64_t timestamp = gCCDBWrapper->timestamp; const bool preserve_path = true; const std::string filename("snapshot.root"); @@ -226,7 +234,7 @@ bool anchor_GRPs(Options const& opts, std::vector const& paths = {" bool success = true; for (auto& p : paths) { LOG(info) << "Fetching " << p << " from CCDB"; - success &= gCCDBWrapper->api.retrieveBlob(p, opts.publishto, filter, runStart, preserve_path, filename); + success &= gCCDBWrapper->api.retrieveBlob(p, opts.publishto, filter, timestamp, preserve_path, filename); } return success; } @@ -304,10 +312,12 @@ bool create_GRPs(Options const& opts) auto soreor = ccdbmgr.getRunDuration(opts.run); runStart = soreor.first; grp.setTimeStart(runStart); - grp.setTimeEnd(runStart + 3600000); + grp.setTimeEnd(soreor.second); grp.setNHBFPerTF(opts.orbitsPerTF); std::vector modules{}; - o2::conf::SimConfig::determineActiveModules(opts.readout, std::vector(), modules, opts.isRun5); + if (!o2::conf::SimConfig::determineActiveModulesList(opts.detectorList, opts.readout, std::vector(), modules)) { + return false; + } std::vector readout{}; o2::conf::SimConfig::determineReadoutDetectors(modules, std::vector(), opts.skipreadout, readout); for (auto& detstr : readout) { @@ -553,6 +563,7 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) // ls command has the following options: bpo::options_description desc("create options"); + desc.add_options()("detectorList", bpo::value(&optvalues.detectorList)->default_value("ALICE2"), "Pick a specific version of ALICE, for specifics check the o2-sim description"); desc.add_options()("readoutDets", bpo::value>(&optvalues.readout)->multitoken()->default_value(std::vector({"all"}), "all Run3 detectors"), "Detector list to be readout/active"); desc.add_options()("skipReadout", bpo::value>(&optvalues.skipreadout)->multitoken()->default_value(std::vector(), "nothing skipped"), "list of inactive detectors (precendence over --readout)"); desc.add_options()("run", bpo::value(&optvalues.run)->default_value(-1), "Run number"); @@ -563,8 +574,9 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) desc.add_options()("lhcif-CCDB", "take GRPLHCIF directly from CCDB"); desc.add_options()("print", "print resulting GRPs"); desc.add_options()("publishto", bpo::value(&optvalues.publishto)->default_value(""), "Base path under which GRP objects should be published on disc. This path can serve as lookup for CCDB queries of the GRP objects."); - desc.add_options()("isRun5", bpo::bool_switch(&optvalues.isRun5), "Whether or not to expect a Run5 detector configuration."); + desc.add_options()("isRun5", bpo::bool_switch(&optvalues.isRun5), "Whether or not to expect a Run5 detector configuration. (deprecated, use detectorList option)"); desc.add_options()("vertex", bpo::value(&optvalues.vertex)->default_value("ccdb"), "How the vertex is to be initialized. Default is CCDB. Alternative is \"Diamond\" which is constructing the mean vertex from the Diamond param via the configKeyValues path"); + desc.add_options()("timestamp", bpo::value(&optvalues.timestamp)->default_value(0), "Force timestamp to be used (useful when anchoring)"); desc.add_options()("configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')"); if (!subparse(desc, vm, "createGRPs")) { return false; diff --git a/DataFormats/Parameters/src/ParametersDataLinkDef.h b/DataFormats/Parameters/src/ParametersDataLinkDef.h index e4817bd443302..d2cbee2049c52 100644 --- a/DataFormats/Parameters/src/ParametersDataLinkDef.h +++ b/DataFormats/Parameters/src/ParametersDataLinkDef.h @@ -30,5 +30,6 @@ #pragma link C++ class o2::parameters::GRPMagField + ; #pragma link C++ class std::unordered_map < unsigned int, unsigned int> + ; #pragma link C++ class std::pair < unsigned long, std::string> + ; +#pragma link C++ struct o2::parameters::AggregatedRunInfo + ; #endif diff --git a/DataFormats/QualityControl/CMakeLists.txt b/DataFormats/QualityControl/CMakeLists.txt index 0d17d0d1d0f9e..c0f40a88f88c1 100644 --- a/DataFormats/QualityControl/CMakeLists.txt +++ b/DataFormats/QualityControl/CMakeLists.txt @@ -10,67 +10,65 @@ # or submit itself to any jurisdiction. file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) -### Prepare the list of methods in FlagReasonFactory -file(READ "etc/flagReasons.csv" CSV_FLAG_REASONS) +### Prepare the list of methods in FlagTypeFactory +file(READ "etc/flagTypes.csv" CSV_FLAG_TYPES) # delete the CSV file header -string(REPLACE \"id\",\"method\",\"name\",\"bad\",\"obsolete\" "" CSV_FLAG_REASONS ${CSV_FLAG_REASONS}) +string(REPLACE \"id\",\"method\",\"name\",\"bad\",\"obsolete\" "" CSV_FLAG_TYPES ${CSV_FLAG_TYPES}) # detects if there is obsolete flag '1' in the last column, adds [[deprecated]] if so and retains the rest of the string string(REGEX REPLACE \([0-9]+,\".[^\"]*.\",.[^\"]*.,[0-1]\),1 "[[deprecated]] \\1,1" - CSV_FLAG_REASONS - ${CSV_FLAG_REASONS}) + CSV_FLAG_TYPES + ${CSV_FLAG_TYPES}) # replaces the flag reason entry with a c++ method to create it string(REGEX REPLACE \([0-9]+\),\"\(.[^\"]*.\)\",\(.[^\"]*.\),\([0-1]\),[0-1] - "static FlagReason \\2\(\) { return { static_cast\(\\1\), \\3, static_cast\(\\4\) }; }" - CSV_FLAG_REASONS - ${CSV_FLAG_REASONS}) + "static FlagType \\2\(\) { return { static_cast\(\\1\), \\3, static_cast\(\\4\) }; }" + CSV_FLAG_TYPES + ${CSV_FLAG_TYPES}) # put the method lists inside the template -configure_file("include/DataFormatsQualityControl/FlagReasonFactory.h.in" - "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagReasonFactory.h" +configure_file("include/DataFormatsQualityControl/FlagTypeFactory.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagTypeFactory.h" @ONLY) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagReasonFactory.h" +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/DataFormatsQualityControl/FlagTypeFactory.h" DESTINATION include/DataFormatsQualityControl) o2_add_library(DataFormatsQualityControl - SOURCES src/FlagReasons.cxx - src/TimeRangeFlag.cxx - src/TimeRangeFlagCollection.cxx - PUBLIC_LINK_LIBRARIES O2::Headers - O2::FrameworkLogger + SOURCES src/FlagType.cxx + src/QualityControlFlag.cxx + src/QualityControlFlagCollection.cxx + PUBLIC_LINK_LIBRARIES O2::FrameworkLogger O2::DetectorsCommonDataFormats PUBLIC_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/include include ) o2_target_root_dictionary(DataFormatsQualityControl - HEADERS include/DataFormatsQualityControl/FlagReasons.h - include/DataFormatsQualityControl/TimeRangeFlag.h - include/DataFormatsQualityControl/TimeRangeFlagCollection.h) - + HEADERS include/DataFormatsQualityControl/FlagType.h + include/DataFormatsQualityControl/QualityControlFlag.h + include/DataFormatsQualityControl/QualityControlFlagCollection.h) if(BUILD_TESTING) -o2_add_test(FlagReasons - SOURCES test/testFlagReasons.cxx +o2_add_test(FlagTypes + SOURCES test/testFlagTypes.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME flagreasons) + TARGETVARNAME flagtypes) -target_include_directories(${flagreasons} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${flagtypes} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) -o2_add_test(TimeRangeFlag - SOURCES test/testTimeRangeFlag.cxx +o2_add_test(QualityControlFlag + SOURCES test/testQualityControlFlag.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME timerangeflag) + TARGETVARNAME qualitycontrolflag) -target_include_directories(${timerangeflag} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${qualitycontrolflag} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) -o2_add_test(TimeRangeFlagCollection - SOURCES test/testTimeRangeFlagCollection.cxx +o2_add_test(QualityControlFlagCollection + SOURCES test/testQualityControlFlagCollection.cxx COMPONENT_NAME DataFormatsQualityControl PUBLIC_LINK_LIBRARIES O2::DataFormatsQualityControl - TARGETVARNAME timerangeflagcollection) + TARGETVARNAME qualitycontrolflagcollection) -target_include_directories(${timerangeflagcollection} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) +target_include_directories(${qualitycontrolflagcollection} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) endif() diff --git a/DataFormats/QualityControl/README.md b/DataFormats/QualityControl/README.md index ededa028c7b93..33821319b7316 100644 --- a/DataFormats/QualityControl/README.md +++ b/DataFormats/QualityControl/README.md @@ -1,63 +1,100 @@ \page refDataFormatsQualityControl Data Formats Quality Control -Data formats for tagging good quality data for analysis. +## Tagging Good Quality Data for Processing and Analysis -# Flagging time ranges -## General idea -* Each detector has its own CCDB (QCDB) entry - TimeRangeFlagCollection -* The CCDB Entry validity will be run or fill, depending on the final data taking granularity -* Each entry can define sub ranges (TimeRangeFlags) of certain data characteristics (FlagReasons) inside the CCDB Entry validity -* Flags are defined in a CSV master file, they are used to derive the Data Tags - the final filters for good quality data during analysis. -* Data Tag are stored as CCDB entries. They might require different detectors and may suppress different flags dependent on the analysis type. - -## Implementation - -[Flag Reason](include/DataFormatsQualityControl/FlagReasons.h) is defined with an identifier number, a name and a 'bad quality' determinant. -The latter decides if such a flag should mark the data quality as bad by default. -FlagReasons can be created only with FlagReasonFactory, so that the list of available reasons is common and centralised. -For example: -``` -id: 10 -name: Limited Acceptance -bad: true -``` -The list of available FlagReasons is defined in [etc/flagReasons.csv](etc/flagReasons.csv), which is used to generate the corresponding list of methods in FlagReasonFactory. -The existing flags should never be modified, except for marking them as obsolete. -New flags may be added via pull requests if there is no flag which conveys a similar meaning. +This document outlines the data formats used for tagging good quality data for further processing and analysis. +They allow us to describe problems affecting the data in concrete time intervals. +Using this information, data can be filtered out according to criteria specific to a given analysis type. + +### Data Quality Control workflow + +Data quality is determined through two methods: + +1. **Automated Checks:** The Quality Control framework runs Checks which may return quality Flags. +2. **Manual Review:** Detector experts review data using the Run Condition Table (RCT) and can modify or add Flags. + +Both methods utilize the same data format for Flags. +During processing (both synchronous and asynchronous), Checks produce Qualities and associate them with Flags. +The Quality Control framework then transmits these Flags to the RCT through a gRPC interface (more details in QC repository documentation). +Detector experts can then review the automatically generated Flags and make any necessary modifications or additions directly in the RCT. + +### Quality Control Flag Structure + +A [Quality Control Flag](include/DataFormatsQualityControl/QualityControlFlag.h) consists of the following elements: + +* **Flag Type:** This identifies the specific issue the flag represents. (More details below) +* **Time Range:** This defines the time period affected by the flag. +* **Comment (Optional):** This allows human-readable explanations for assigning the flag. +* **Source String:** This identifies the entity (person or software module) that created the flag. + +**Example:** -With [TimeRangeFlags](include/DataFormatsQualityControl/TimeRangeFlag.h) we can define the time range of a chosen FlagReason, add an additional comment and specify the source of this flag. -For example: ``` -start: 1612707603626 +flag: Limited Acceptance MC Reproducible +start: 1612707603626 end: 1613999652000 -flag: Limited Acceptance comment: Sector B in TPC inactive -source: o2::quality_control_modules::tpc::ClustersCheck +source: TPC/Clusters Check ``` -[TimeRangeFlagCollection](include/DataFormatsQualityControl/TimeRangeFlagCollection.h) contains all TimeRangeFlags for the validity range (run or fill). -TimeRangeFlags may overlap, e.g. if they use different FlagReasons and they are sorted by their start time. -If certain period does not include any TimeRangeFlags with *bad* FlagReasons, then the data quality can be considered as good. -The [TimeRangeFlagCollection test](test/testTimeRangeFlagCollection.cxx) shows the usage example. +### Flag Types + +[Flag Types](include/DataFormatsQualityControl/FlagType.h) define the specific categories of issues represented by flags. +Each Flag Type has the following attributes: + +* **Identifier Number:** A unique numerical ID for the Flag Type. +* **Name:** A human-readable name describing the issue. +* **"Bad Quality" Determinant:** This boolean constant indicates whether the type inherently signifies bad data quality. + +#### Creating and Managing Flag Types + +* **FlagTypeFactory** ensures a centralized and consistent list of available Flag Types. + New Flags can only be created through this factory. +* **[flagTypes.csv](etc/flagTypes.csv)** defines the existing Flag Types, including their ID, name, and "bad quality" determinant, factory method name and a switch to deprecate a flag. + The table serves as the source to automatically generate the corresponding methods in FlagTypeFactory. +* **Adding new Flag Types:** If a new issue requires a flag not currently defined, propose the addition by contacting the async QC coordinators. + They have the authority to add new Flag Types to the RCT. + These changes will then be reflected in the [flagTypes.csv](etc/flagTypes.csv) file through a pull request. + Any proposals for new Flag Types should describe the effects on usability of data from analyzer point of view and they should not be detector-specific unless well-argumented. +* **Modification of existing Flag Types:** Existing Flag Types should not be modified in terms of their definition. + Instead, one may create a new Flag Type and mark the existing one as obsolete in the CSV table. + This will add the `[[ deprecated ]]` attribute to the corresponding method. -TimeRangeFlagCollections are supposed to be created automatically with QC Post-processing Tasks based on Quality Objects created by QC Checks. -However, they might be created manually by QA experts as well. -The procedure to do that has to be defined. +#### Currently available Flag Types -## TODO -* Define the complete list of available Flag Reasons -* implement CCDB storage and access - - define CCDB storage place e.g. - * `/QC/QualityFlags` - * `Analysis/QualityFlags/` -* Data Tags Definitions and Data Tags +This section details the currently available Flag Types and provides a brief explanation of their intended use cases. -### Notes on plans for Data Tags +* **Good:** a Check or an expert sees nothing wrong with given time interval, but would like to add a comment. + Note that the absence of any flag for a run implies good data quality. + Thus, there is no need to mark it explicitly as such by using this flag type. +* **No Detector Data:** a complete and unexpected absence of data for a specific detector. +* **Limited Acceptance MC Not Reproducible:** a part of a detector did not acquire good data and this condition cannot be reproduced in Monte Carlo. + If an automated Check cannot determine MC reproducibility, it should default to "Not Reproducible" for later expert review. +* **Limited Acceptance MC Reproducible:** a part of a detector did not acquire good data, but this condition can be reproduced in Monte Carlo. +* **Bad Tracking:** analyses relying on accurate track reconstruction should not use this data. +* **Bad PID:** analyses relying on correct identification of all kinds of tracked particles should not use this data. +* **Bad Hadron PID:** analyses relying on correct hadron identification should not use this data. +* **Bad Electron PID:** analyses relying on correct electron identification should not use this data. +* **Bad Photon Calorimetry:** analyses relying on correct photon calorimetry should not use this data. +* **Bad EMCalorimetry:** analyses relying on correct electromagnetic calorimetry should not use this data. +* **Unknown:** the exact impact of an issue on the data is unclear, but it's likely bad. + Treat data with this flag with caution until further investigation. +* **Unknown Quality:** the quality of data could not be determined definitively. +* **Invalid:** there was an issue with processing the flags. + +## Usage in Analysis framework (plans, wishlist) + +## General idea +* RCT exports a read-only copy of the flags for each detector, run and pass combination in the CCDB. +* The Analysis framework uses the flags and user selection criteria to provide their Analysis Task with data matching these criteria. + **Data Tags** are the structures defining the good time intervals for the provided criteria. + +## Notes on plans for Data Tags Data Tag definition has: * name -* global suppression list for bad flag reasons (applies to all detectors) -* global requirement list for not bad flag reasons +* global suppression list for bad flag types (applies to all detectors) +* global requirement list for not bad flag types * list of needed detectors, * list of suppression list and requirement list specific to detectors @@ -94,10 +131,3 @@ Example configurations } ] ``` - -## Wishlist / Ideas -* executable to add flags to the flag store -* executable to extrags the flag store -* summary of all masks for one TimeRangeFlagCollection -* functionality to extract flags for a specific detector (from CCDB) -* cut class to specify detectors and flags which to exclude \ No newline at end of file diff --git a/DataFormats/QualityControl/etc/flagReasons.csv b/DataFormats/QualityControl/etc/flagReasons.csv deleted file mode 100644 index 23acc1b3000fa..0000000000000 --- a/DataFormats/QualityControl/etc/flagReasons.csv +++ /dev/null @@ -1,15 +0,0 @@ -"id","method","name","bad","obsolete" -1,"Unknown","Unknown",1,0 -2,"UnknownQuality","Unknown Quality",1,0 -3,"CertifiedByExpert","Certified by Expert",0,0 -10,"NoDetectorData","No Detector Data",1,0 -11,"LimitedAcceptance","Limited acceptance",1,0 -12,"BadPID","Bad PID",1,0 -13,"BadTracking","Bad Tracking",1,0 -14,"BadHadronPID","Bad Hadron PID",1,0 -15,"BadElectronPID","Bad Electron PID",1,0 -16,"BadEMCalorimetry","Bad EM Calorimetry",1,0 -17,"BadPhotonCalorimetry","Bad Photon Calorimetry",1,0 -65500,"ObsoleteFlagExample","Obsolete flag example",1,1 -65501,"NotBadFlagExample","Not bad flag example",0,0 -65535,"Invalid","Invalid",1,0 diff --git a/DataFormats/QualityControl/etc/flagTypes.csv b/DataFormats/QualityControl/etc/flagTypes.csv new file mode 100644 index 0000000000000..97107cd15a9e7 --- /dev/null +++ b/DataFormats/QualityControl/etc/flagTypes.csv @@ -0,0 +1,14 @@ +"id","method","name","bad","obsolete" +1,"UnknownQuality","Unknown Quality",1,0 +3,"NoDetectorData","No Detector Data",1,0 +4,"LimitedAcceptanceMCNotReproducible","Limited Acceptance MC Not Reproducible",1,0 +5,"LimitedAcceptanceMCReproducible","Limited Acceptance MC Reproducible",1,0 +6,"BadPID","Bad PID",1,0 +7,"BadTracking","Bad Tracking",1,0 +8,"BadHadronPID","Bad Hadron PID",1,0 +9,"Good","Good",0,0 +10,"Invalid","Invalid",1,0 +11,"BadElectronPID","Bad Electron PID",1,0 +12,"BadEMCalorimetry","Bad EM Calorimetry",1,0 +13,"BadPhotonCalorimetry","Bad Photon Calorimetry",1,0 +14,"Unknown","Unknown",1,0 diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in deleted file mode 100644 index 59bd7b4c5628f..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasonFactory.h.in +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FlagReasonFactory.h -/// \brief A class to create FlagReasons based on the predefined CSV list. -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -#ifndef O2_FLAGREASONFACTORY_H -#define O2_FLAGREASONFACTORY_H - -#include "DataFormatsQualityControl/FlagReasons.h" - -namespace o2::quality_control -{ - -class FlagReasonFactory { -public: -FlagReasonFactory() = delete; -@CSV_FLAG_REASONS@ -}; - -} // namespace o2::quality_control -#endif // O2_FLAGREASONFACTORY_H \ No newline at end of file diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h deleted file mode 100644 index 67f5b4cc27cde..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagReasons.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_FLAGREASONS -#define ALICEO2_ANALYSIS_FLAGREASONS - -/// \file FlagReasons.h -/// \brief classes keeping reasons for flagging time ranges -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -// STL -#include -#include - -// ROOT includes -#include - -namespace o2 -{ -namespace quality_control -{ - -class FlagReasonFactory; -class TimeRangeFlagCollection; - -class FlagReason -{ - private: - uint16_t mId; - std::string mName; - bool mBad; // if true, data should become bad by default - - // By making the constructor private and FlagReasons available only in the FlagReasonFactory - // we forbid to declare any flags in the user code. If you need a new FlagReason, please add it FlagReasonFactory. - private: - FlagReason(uint16_t id, const char* name, bool bad) : mId(id), mName(name), mBad(bad) {} - - public: - FlagReason(); - FlagReason& operator=(const FlagReason&) = default; - FlagReason(const FlagReason&) = default; - bool operator==(const FlagReason& rhs) const; - bool operator!=(const FlagReason& rhs) const; - bool operator<(const FlagReason& rhs) const; - bool operator>(const FlagReason& rhs) const; - - uint16_t getID() const { return mId; } - const std::string& getName() const { return mName; } - bool getBad() const { return mBad; } - - friend std::ostream& operator<<(std::ostream& os, FlagReason const& me); - friend class FlagReasonFactory; - friend class TimeRangeFlagCollection; - - ClassDefNV(FlagReason, 1); -}; - -} // namespace quality_control -} // namespace o2 - -// TODO: remove once we include it in QualityControl -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -#endif diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h new file mode 100644 index 0000000000000..94e4a301a1784 --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagType.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_FLAGTYPE_H +#define O2_QUALITYCONTROL_FLAGTYPE_H + +// STL +#include +#include + +// ROOT includes +#include + +namespace o2 +{ +namespace quality_control +{ + +class FlagTypeFactory; +class QualityControlFlagCollection; + +class FlagType +{ + private: + uint16_t mId; + std::string mName; + bool mBad; // if true, data should become bad by default + + // By making the constructor private and FlagTypes available only in the FlagTypeFactory + // we forbid to declare any flags in the user code. If you need a new FlagType, please add it FlagTypeFactory. + private: + FlagType(uint16_t id, const char* name, bool bad) : mId(id), mName(name), mBad(bad) {} + + public: + FlagType(); + FlagType& operator=(const FlagType&) = default; + FlagType(const FlagType&) = default; + bool operator==(const FlagType& rhs) const; + bool operator!=(const FlagType& rhs) const; + bool operator<(const FlagType& rhs) const; + bool operator>(const FlagType& rhs) const; + + uint16_t getID() const { return mId; } + const std::string& getName() const { return mName; } + bool getBad() const { return mBad; } + + friend std::ostream& operator<<(std::ostream& os, FlagType const& me); + friend class FlagTypeFactory; + friend class QualityControlFlagCollection; + + ClassDefNV(FlagType, 1); +}; + +} // namespace quality_control +} // namespace o2 +#endif // O2_QUALITYCONTROL_FLAGTYPE_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in new file mode 100644 index 0000000000000..38b6c463d239f --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/FlagTypeFactory.h.in @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FlagTypeFactory.h +/// \brief A class to create FlagTypes based on the predefined CSV list. +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +#ifndef O2_FLAGTYPEFACTORY_H +#define O2_FLAGTYPEFACTORY_H + +#include "DataFormatsQualityControl/FlagType.h" + +namespace o2::quality_control +{ + +class FlagTypeFactory { +public: +FlagTypeFactory() = delete; +@CSV_FLAG_TYPES@ +}; + +} // namespace o2::quality_control +#endif // O2_FLAGTYPEFACTORY_H \ No newline at end of file diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h new file mode 100644 index 0000000000000..9c38952562483 --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h @@ -0,0 +1,88 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_QCFLAG_H +#define O2_QUALITYCONTROL_QCFLAG_H + +/// \file QualityControlFlag.h +/// \brief Class to define a flag type with a time range and comments +/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de +/// \author Piotr Konopka, piotr.jan.konopka@cern.ch + +// System includes +#include +#include + +// ROOT includes +#include + +#include + +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +namespace o2 +{ +namespace quality_control +{ + +/// \class QualityControlFlag +/// A Class for associating a bit mask with a time range +class QualityControlFlag +{ + public: + using time_type = uint64_t; + using RangeInterval = o2::math_utils::detail::Bracket; + + QualityControlFlag() = default; + QualityControlFlag(QualityControlFlag const&) = default; + QualityControlFlag(time_type start, time_type end, FlagType flag, std::string comment = "", std::string source = "Unknown"); + + time_type getStart() const { return mInterval.getMin(); } + time_type getEnd() const { return mInterval.getMax(); } + RangeInterval& getInterval() { return mInterval; } + const RangeInterval getInterval() const { return mInterval; } + FlagType getFlag() const { return mFlag; } + const std::string& getComment() const { return mComment; } + const std::string& getSource() const { return mSource; } + + void setStart(time_type start) { mInterval.setMin(start); } + void setEnd(time_type end) { mInterval.setMax(end); } + void setInterval(RangeInterval interval) { mInterval = interval; } + void setFlag(FlagType flag) { mFlag = flag; } + void setComment(const std::string& comment) { mComment = comment; } + void setSource(const std::string& source) { mSource = source; } + + /// equal operator + bool operator==(const QualityControlFlag& rhs) const; + + /// comparison operators + bool operator<(const QualityControlFlag& rhs) const; + bool operator>(const QualityControlFlag& rhs) const; + + /// write data to ostream + void streamTo(std::ostream& output) const; + + /// overloading output stream operator + friend std::ostream& operator<<(std::ostream& output, const QualityControlFlag& data); + + private: + RangeInterval mInterval = {}; ///< time interval of the masked range + FlagType mFlag; ///< flag reason + std::string mComment = ""; ///< optional comment, which may extend the reason + std::string mSource = "Unknown"; ///< optional (but encouraged) source of the flag (e.g. Qc Check name) + + ClassDefNV(QualityControlFlag, 1); +}; + +} // namespace quality_control +} // namespace o2 +#endif // O2_QUALITYCONTROL_QCFLAG_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h new file mode 100644 index 0000000000000..120605f048cbe --- /dev/null +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlagCollection.h @@ -0,0 +1,102 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_QUALITYCONTROL_QCFLAGCOLLECTION_H +#define O2_QUALITYCONTROL_QCFLAGCOLLECTION_H +/// \file QualityControlFlagCollection.h +/// \brief classes for defining time ranges with a certain mask to be able to cut on +/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de + +// System includes +#include + +// ROOT includes +#include "Rtypes.h" + +// O2 includes +#include "MathUtils/detail/Bracket.h" +#include "DataFormatsQualityControl/QualityControlFlag.h" + +// STL +#include + +namespace o2 +{ +namespace quality_control +{ + +/// \class QualityControlFlagCollection +/// A Class for keeping several time ranges of type QualityControlFlag +class QualityControlFlagCollection +{ + public: + using collection_t = std::set; + using time_type = uint64_t; + using RangeInterval = o2::math_utils::detail::Bracket; + + explicit QualityControlFlagCollection(std::string name, std::string detector = "TST", RangeInterval validityRange = {}, + int runNumber = 0, std::string periodName = "Invalid", std::string passName = "Invalid", + std::string provenance = "qc"); + + void insert(QualityControlFlag&&); + void insert(const QualityControlFlag&); + + size_t size() const; + + // moves all non-repeating QualityControlFlags from other to this + void merge(QualityControlFlagCollection& other); + // add all non-repeating QualityControlFlags from other to this. + void merge(const QualityControlFlagCollection& other); + + collection_t::const_iterator begin() const; + collection_t::const_iterator end() const; + + const std::string& getName() const; + const std::string& getDetector() const; + int getRunNumber() const; + const std::string& getPeriodName() const; + const std::string& getPassName() const; + const std::string& getProvenance() const; + + time_type getStart() const { return mValidityRange.getMin(); } + time_type getEnd() const { return mValidityRange.getMax(); } + RangeInterval& getInterval() { return mValidityRange; } + + void setStart(time_type start) { mValidityRange.setMin(start); } + void setEnd(time_type end) { mValidityRange.setMax(end); } + void setInterval(RangeInterval interval) { mValidityRange = interval; } + + /// write data to ostream + void streamTo(std::ostream& output) const; + /// Read data from instream + void streamFrom(std::istream& input); + + /// overloading output stream operator + friend std::ostream& operator<<(std::ostream& output, const QualityControlFlagCollection& data); + + private: + std::string mDetID; // three letter detector code + std::string mName; // some description of the collection, e.g. "Raw data checks", "QA Expert masks" + // with std::set we can sort the flags in time and have merge() for granted. + collection_t mQualityControlFlags; + RangeInterval mValidityRange; // we need a validity range to e.g. state that there are no TRFs for given time interval + int mRunNumber; + std::string mPeriodName; + std::string mPassName; + std::string mProvenance; + + ClassDefNV(QualityControlFlagCollection, 1); +}; + +} // namespace quality_control +} // namespace o2 + +#endif // O2_QUALITYCONTROL_QCFLAGCOLLECTION_H diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h deleted file mode 100644 index b8e1efbce1f40..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlag.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_TIMERANGEFLAGS -#define ALICEO2_ANALYSIS_TIMERANGEFLAGS - -/// \file TimeRangeFlag.h -/// \brief Class to define a time range of a flag type -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de -/// \author Piotr Konopka, piotr.jan.konopka@cern.ch - -// System includes -#include -#include - -// ROOT includes -#include - -#include - -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -namespace o2 -{ -namespace quality_control -{ - -/// \class TimeRangeFlag -/// A Class for associating a bit mask with a time range -class TimeRangeFlag -{ - public: - using time_type = uint64_t; - using flag_type = FlagReason; - using RangeInterval = o2::math_utils::detail::Bracket; - - TimeRangeFlag() = default; - TimeRangeFlag(TimeRangeFlag const&) = default; - TimeRangeFlag(time_type start, time_type end, flag_type flag, std::string comment = "", std::string source = "Unknown"); - - time_type getStart() const { return mInterval.getMin(); } - time_type getEnd() const { return mInterval.getMax(); } - RangeInterval& getInterval() { return mInterval; } - flag_type getFlag() const { return mFlag; } - const std::string& getComment() const { return mComment; } - const std::string& getSource() const { return mSource; } - - void setStart(time_type start) { mInterval.setMin(start); } - void setEnd(time_type end) { mInterval.setMax(end); } - void setInterval(RangeInterval interval) { mInterval = interval; } - void setFlag(flag_type flag) { mFlag = flag; } - void setComment(const std::string& comment) { mComment = comment; } - void setSource(const std::string& source) { mSource = source; } - - /// equal operator - bool operator==(const TimeRangeFlag& rhs) const; - - /// comparison operators - bool operator<(const TimeRangeFlag& rhs) const; - bool operator>(const TimeRangeFlag& rhs) const; - - /// write data to ostream - void streamTo(std::ostream& output) const; - - /// overloading output stream operator - friend std::ostream& operator<<(std::ostream& output, const TimeRangeFlag& data); - - private: - RangeInterval mInterval = {}; ///< time interval of the masked range - flag_type mFlag; ///< flag reason - std::string mComment = ""; ///< optional comment, which may extend the reason - std::string mSource = "Unknown"; ///< optional (but encouraged) source of the flag (e.g. Qc Check name) - - ClassDefNV(TimeRangeFlag, 1); -}; - -} // namespace quality_control -} // namespace o2 -#endif diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h deleted file mode 100644 index 30a8230fc66e2..0000000000000 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/TimeRangeFlagCollection.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_ANALYSIS_TIMERANGEMASK -#define ALICEO2_ANALYSIS_TIMERANGEMASK - -/// \file TimeRangeFlagCollection.h -/// \brief classes for defining time ranges with a certain mask to be able to cut on -/// \author Jens Wiechula, jens.wiechula@ikf.uni-frankfurt.de - -// System includes -#include - -// ROOT includes -#include "Rtypes.h" - -// O2 includes -#include "MathUtils/detail/Bracket.h" -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -// STL -#include - -namespace o2 -{ -namespace quality_control -{ - -/// \class TimeRangeFlagCollection -/// A Class for keeping several time ranges of type TimeRangeFlag -class TimeRangeFlagCollection -{ - public: - using collection_t = std::set; - using time_type = uint64_t; - using RangeInterval = o2::math_utils::detail::Bracket; - - explicit TimeRangeFlagCollection(std::string name, std::string detector = "TST", RangeInterval validityRange = {}, - int runNumber = 0, std::string periodName = "Invalid", std::string passName = "Invalid", - std::string provenance = "qc"); - - void insert(TimeRangeFlag&&); - void insert(const TimeRangeFlag&); - - size_t size() const; - - // moves all non-repeating TimeRangeFlags from other to this - void merge(TimeRangeFlagCollection& other); - // add all non-repeating TimeRangeFlags from other to this. - void merge(const TimeRangeFlagCollection& other); - - collection_t::const_iterator begin() const; - collection_t::const_iterator end() const; - - const std::string& getName() const; - const std::string& getDetector() const; - int getRunNumber() const; - const std::string& getPeriodName() const; - const std::string& getPassName() const; - const std::string& getProvenance() const; - - time_type getStart() const { return mValidityRange.getMin(); } - time_type getEnd() const { return mValidityRange.getMax(); } - RangeInterval& getInterval() { return mValidityRange; } - - void setStart(time_type start) { mValidityRange.setMin(start); } - void setEnd(time_type end) { mValidityRange.setMax(end); } - void setInterval(RangeInterval interval) { mValidityRange = interval; } - - /// write data to ostream - void streamTo(std::ostream& output) const; - /// Read data from instream - void streamFrom(std::istream& input); - - /// overloading output stream operator - friend std::ostream& operator<<(std::ostream& output, const TimeRangeFlagCollection& data); - - private: - std::string mDetID; // three letter detector code - std::string mName; // some description of the collection, e.g. "Raw data checks", "QA Expert masks" - // with std::set we can sort the flags in time and have merge() for granted. - collection_t mTimeRangeFlags; - RangeInterval mValidityRange; // we need a validity range to e.g. state that there are no TRFs for given time interval - int mRunNumber; - std::string mPeriodName; - std::string mPassName; - std::string mProvenance; - - ClassDefNV(TimeRangeFlagCollection, 1); -}; - -} // namespace quality_control -} // namespace o2 -#endif diff --git a/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h b/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h index 8d77bccef4b7c..ab10c361b29f9 100644 --- a/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h +++ b/DataFormats/QualityControl/src/DataFormatsQualityControlLinkDef.h @@ -15,8 +15,8 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::quality_control::FlagReason + ; -#pragma link C++ class o2::quality_control::TimeRangeFlag + ; -#pragma link C++ class o2::quality_control::TimeRangeFlagCollection + ; +#pragma link C++ class o2::quality_control::FlagType + ; +#pragma link C++ class o2::quality_control::QualityControlFlag + ; +#pragma link C++ class o2::quality_control::QualityControlFlagCollection + ; #endif diff --git a/DataFormats/QualityControl/src/FlagReasons.cxx b/DataFormats/QualityControl/src/FlagReasons.cxx deleted file mode 100644 index e85a0d0c7faa7..0000000000000 --- a/DataFormats/QualityControl/src/FlagReasons.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -#include -#include - -namespace o2::quality_control -{ - -FlagReason::FlagReason() -{ - *this = FlagReasonFactory::Invalid(); -} - -std::ostream& operator<<(std::ostream& os, FlagReason const& my) -{ - os << "Flag Reason: id - " << my.mId << ", name - " << my.mName << ", bad - " << (my.mBad ? "true" : "false"); - return os; -} -bool FlagReason::operator==(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) == std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator!=(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) != std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator<(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) < std::tie(rhs.mId, rhs.mName, rhs.mBad); -} -bool FlagReason::operator>(const FlagReason& rhs) const -{ - return std::tie(mId, mName, mBad) > std::tie(rhs.mId, rhs.mName, rhs.mBad); -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/FlagType.cxx b/DataFormats/QualityControl/src/FlagType.cxx new file mode 100644 index 0000000000000..27e59911ed775 --- /dev/null +++ b/DataFormats/QualityControl/src/FlagType.cxx @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +#include +#include + +namespace o2::quality_control +{ + +FlagType::FlagType() +{ + *this = FlagTypeFactory::Invalid(); +} + +std::ostream& operator<<(std::ostream& os, FlagType const& my) +{ + os << "Flag Reason: id - " << my.mId << ", name - " << my.mName << ", bad - " << (my.mBad ? "true" : "false"); + return os; +} +bool FlagType::operator==(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) == std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator!=(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) != std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator<(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) < std::tie(rhs.mId, rhs.mName, rhs.mBad); +} +bool FlagType::operator>(const FlagType& rhs) const +{ + return std::tie(mId, mName, mBad) > std::tie(rhs.mId, rhs.mName, rhs.mBad); +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/QualityControlFlag.cxx b/DataFormats/QualityControl/src/QualityControlFlag.cxx new file mode 100644 index 0000000000000..ce749cad5315f --- /dev/null +++ b/DataFormats/QualityControl/src/QualityControlFlag.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DataFormatsQualityControl/QualityControlFlag.h" + +#include +#include + +namespace o2::quality_control +{ + +QualityControlFlag::QualityControlFlag(time_type start, time_type end, FlagType flag, std::string comment, std::string source) + : mInterval(start, end), mFlag(flag), mComment(comment), mSource(source) +{ + if (mInterval.isInvalid()) { + throw std::runtime_error("QualityControlFlag start time '" + std::to_string(mInterval.getMin()) + "' is larger than end time '" + std::to_string(mInterval.getMax()) + "'"); + } +} + +bool QualityControlFlag::operator==(const QualityControlFlag& rhs) const +{ + return std::tie(mInterval, mFlag, mComment, mSource) == std::tie(rhs.mInterval, rhs.mFlag, rhs.mComment, rhs.mSource); +} + +bool QualityControlFlag::operator<(const QualityControlFlag& rhs) const +{ + // We don't use the comparison mechanism in Bracket, + // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b. + // Using relation operators in Bracket would break insertion and merging. + return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) < std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); +} + +bool QualityControlFlag::operator>(const QualityControlFlag& rhs) const +{ + // we don't use the comparison mechanism in Bracket, + // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b + return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) > std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); +} + +void QualityControlFlag::streamTo(std::ostream& output) const +{ + output << "QualityControlFlag:\n"; + output << "- Start: " << mInterval.getMin() << "\n"; + output << "- End: " << mInterval.getMax() << "\n"; + output << "- " << mFlag << "\n"; + output << "- Comment: " << mComment << "\n"; + output << "- Source: " << mSource; +} + +std::ostream& operator<<(std::ostream& output, const QualityControlFlag& data) +{ + data.streamTo(output); + return output; +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx b/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx new file mode 100644 index 0000000000000..042ef2f218d09 --- /dev/null +++ b/DataFormats/QualityControl/src/QualityControlFlagCollection.cxx @@ -0,0 +1,217 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// O2 include +#include "DataFormatsQualityControl/QualityControlFlagCollection.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" +#include "Framework/Logger.h" + +#include +#include +#include +#include + +namespace o2::quality_control +{ + +constexpr const char* csvHeader = "start,end,flag_id,flag_name,flag_bad,comment,source"; +constexpr size_t csvColumns = 7; + +QualityControlFlagCollection::QualityControlFlagCollection(std::string name, std::string detector, RangeInterval validityRange, + int runNumber, std::string periodName, std::string passName, + std::string provenance) + : mName(std::move(name)), mDetID(std::move(detector)), mValidityRange(validityRange), mRunNumber(runNumber), mPeriodName(std::move(periodName)), mPassName(passName), mProvenance(std::move(provenance)) +{ +} + +void QualityControlFlagCollection::insert(QualityControlFlag&& trf) +{ + mQualityControlFlags.insert(std::move(trf)); +} + +void QualityControlFlagCollection::insert(const QualityControlFlag& trf) +{ + mQualityControlFlags.insert(trf); +} + +size_t QualityControlFlagCollection::size() const +{ + return mQualityControlFlags.size(); +} + +void QualityControlFlagCollection::merge(QualityControlFlagCollection& other) +{ + if (mDetID != other.mDetID) { + // We assume that one QualityControlFlagCollection should correspond to one detector at most. + // However, if this becomes annoying, we can reconsider it. + throw std::runtime_error( + "The detector ID of the target collection '" + mDetID + "' is different than the other '" + mDetID); + } + mQualityControlFlags.merge(other.mQualityControlFlags); +} + +void QualityControlFlagCollection::merge(const QualityControlFlagCollection& other) +{ + QualityControlFlagCollection otherCopy{other}; + merge(otherCopy); +} + +QualityControlFlagCollection::collection_t::const_iterator QualityControlFlagCollection::begin() const +{ + return mQualityControlFlags.begin(); +} + +QualityControlFlagCollection::collection_t::const_iterator QualityControlFlagCollection::end() const +{ + return mQualityControlFlags.end(); +} + +void QualityControlFlagCollection::streamTo(std::ostream& output) const +{ + auto escapeComma = [](const std::string& str) { + return boost::algorithm::replace_all_copy(str, ",", "\\,"); + }; + output << csvHeader << '\n'; + for (const auto& trf : *this) { + output << fmt::format("{},{},{},\"{}\",{:d},\"{}\",\"{}\"\n", + trf.getStart(), trf.getEnd(), + trf.getFlag().getID(), escapeComma(trf.getFlag().getName()), trf.getFlag().getBad(), + escapeComma(trf.getComment()), escapeComma(trf.getSource())); + } +} + +void QualityControlFlagCollection::streamFrom(std::istream& input) +{ + std::string line; + std::getline(input, line); + if (line != csvHeader) { + throw std::runtime_error( + "Unsupported TRFCollection format, the first line is \"" + line + "\" instead of \"" + csvHeader + "\""); + } + + while (std::getline(input, line)) { + boost::tokenizer> tok(line); + + QualityControlFlag::time_type start = 0; + QualityControlFlag::time_type end = 0; + FlagType flag = FlagTypeFactory::Invalid(); + std::string comment; + std::string source; + auto it = tok.begin(); + bool valid = true; + size_t pos = 0; + for (; it != tok.end() && valid; pos++, it++) { + switch (pos) { + case 0: { + if (it->empty()) { + LOG(error) << "Invalid line, empty start time of a flag, skipping..."; + valid = false; + } else { + start = static_cast(std::stoull(*it)); + } + break; + } + case 1: { + if (it->empty()) { + LOG(error) << "Invalid line, empty end time of a flag, skipping..."; + valid = false; + } else { + end = static_cast(std::stoull(*it)); + } + break; + } + case 2: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag id, skipping..."; + valid = false; + } else { + flag.mId = std::stoul(*it); + } + break; + } + case 3: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag name, skipping..."; + valid = false; + } else { + flag.mName = *it; + } + break; + } + case 4: { + if (it->empty()) { + LOG(error) << "Invalid line, empty flag 'bad' field, skipping..."; + valid = false; + } else { + flag.mBad = static_cast(std::stoul(*it)); + } + break; + } + case 5: { + comment = *it; + break; + } + case 6: { + source = *it; + break; + } + default: { + LOG(error) << "More columns (" << pos + 1 << ") than expected (" << csvColumns + << ") in this line, skipping..."; + valid = false; + break; + } + } + } + if (valid && pos < csvColumns) { + LOG(error) << "Less columns (" << pos << ") than expected (" << csvColumns << ") in this line, skipping..."; + } else if (valid) { + insert({start, end, flag, comment, source}); + } + } +} + +std::ostream& operator<<(std::ostream& output, const QualityControlFlagCollection& data) +{ + data.streamTo(output); + return output; +} + +const std::string& QualityControlFlagCollection::getName() const +{ + return mName; +} + +const std::string& QualityControlFlagCollection::getDetector() const +{ + return mDetID; +} + +int QualityControlFlagCollection::getRunNumber() const +{ + return mRunNumber; +} + +const std::string& QualityControlFlagCollection::getPeriodName() const +{ + return mPeriodName; +} + +const std::string& QualityControlFlagCollection::getPassName() const +{ + return mPassName; +} +const std::string& QualityControlFlagCollection::getProvenance() const +{ + return mProvenance; +} + +} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/TimeRangeFlag.cxx b/DataFormats/QualityControl/src/TimeRangeFlag.cxx deleted file mode 100644 index f96d5bc9b7b72..0000000000000 --- a/DataFormats/QualityControl/src/TimeRangeFlag.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -#include -#include - -namespace o2::quality_control -{ - -TimeRangeFlag::TimeRangeFlag(time_type start, time_type end, flag_type flag, std::string comment, std::string source) - : mInterval(start, end), mFlag(flag), mComment(comment), mSource(source) -{ - if (mInterval.isInvalid()) { - throw std::runtime_error("TimeRangeFlag start time '" + std::to_string(mInterval.getMin()) + "' is larger than end time '" + std::to_string(mInterval.getMax()) + "'"); - } -} - -bool TimeRangeFlag::operator==(const TimeRangeFlag& rhs) const -{ - return std::tie(mInterval, mFlag, mComment, mSource) == std::tie(rhs.mInterval, rhs.mFlag, rhs.mComment, rhs.mSource); -} - -bool TimeRangeFlag::operator<(const TimeRangeFlag& rhs) const -{ - // We don't use the comparison mechanism in Bracket, - // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b. - // Using relation operators in Bracket would break insertion and merging. - return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) < std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); -} - -bool TimeRangeFlag::operator>(const TimeRangeFlag& rhs) const -{ - // we don't use the comparison mechanism in Bracket, - // because std::set which is used in TRFCollection assumes that a < b, a > b <=> a == b - return std::tie(static_cast(mInterval.getMin()), static_cast(mInterval.getMax()), mFlag, mComment, mSource) > std::tie(static_cast(rhs.mInterval.getMin()), static_cast(rhs.mInterval.getMax()), rhs.mFlag, rhs.mComment, rhs.mSource); -} - -void TimeRangeFlag::streamTo(std::ostream& output) const -{ - output << "TimeRangeFlag:\n"; - output << "- Start: " << mInterval.getMin() << "\n"; - output << "- End: " << mInterval.getMax() << "\n"; - output << "- " << mFlag << "\n"; - output << "- Comment: " << mComment << "\n"; - output << "- Source: " << mSource; -} - -std::ostream& operator<<(std::ostream& output, const TimeRangeFlag& data) -{ - data.streamTo(output); - return output; -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx b/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx deleted file mode 100644 index ee3adc8dc7f2f..0000000000000 --- a/DataFormats/QualityControl/src/TimeRangeFlagCollection.cxx +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// O2 include -#include "DataFormatsQualityControl/TimeRangeFlagCollection.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" -#include "Framework/Logger.h" - -#include -#include -#include -#include - -namespace o2::quality_control -{ - -constexpr const char* csvHeader = "start,end,flag_id,flag_name,flag_bad,comment,source"; -constexpr size_t csvColumns = 7; - -TimeRangeFlagCollection::TimeRangeFlagCollection(std::string name, std::string detector, RangeInterval validityRange, - int runNumber, std::string periodName, std::string passName, - std::string provenance) - : mName(std::move(name)), mDetID(std::move(detector)), mValidityRange(validityRange), mRunNumber(runNumber), mPeriodName(std::move(periodName)), mPassName(passName), mProvenance(std::move(provenance)) -{ -} - -void TimeRangeFlagCollection::insert(TimeRangeFlag&& trf) -{ - mTimeRangeFlags.insert(std::move(trf)); -} - -void TimeRangeFlagCollection::insert(const TimeRangeFlag& trf) -{ - mTimeRangeFlags.insert(trf); -} - -size_t TimeRangeFlagCollection::size() const -{ - return mTimeRangeFlags.size(); -} - -void TimeRangeFlagCollection::merge(TimeRangeFlagCollection& other) -{ - if (mDetID != other.mDetID) { - // We assume that one TimeRangeFlagCollection should correspond to one detector at most. - // However, if this becomes annoying, we can reconsider it. - throw std::runtime_error( - "The detector ID of the target collection '" + mDetID + "' is different than the other '" + mDetID); - } - mTimeRangeFlags.merge(other.mTimeRangeFlags); -} - -void TimeRangeFlagCollection::merge(const TimeRangeFlagCollection& other) -{ - TimeRangeFlagCollection otherCopy{other}; - merge(otherCopy); -} - -TimeRangeFlagCollection::collection_t::const_iterator TimeRangeFlagCollection::begin() const -{ - return mTimeRangeFlags.begin(); -} - -TimeRangeFlagCollection::collection_t::const_iterator TimeRangeFlagCollection::end() const -{ - return mTimeRangeFlags.end(); -} - -void TimeRangeFlagCollection::streamTo(std::ostream& output) const -{ - auto escapeComma = [](const std::string& str) { - return boost::algorithm::replace_all_copy(str, ",", "\\,"); - }; - output << csvHeader << '\n'; - for (const auto& trf : *this) { - output << fmt::format("{},{},{},\"{}\",{:d},\"{}\",\"{}\"\n", - trf.getStart(), trf.getEnd(), - trf.getFlag().getID(), escapeComma(trf.getFlag().getName()), trf.getFlag().getBad(), - escapeComma(trf.getComment()), escapeComma(trf.getSource())); - } -} - -void TimeRangeFlagCollection::streamFrom(std::istream& input) -{ - std::string line; - std::getline(input, line); - if (line != csvHeader) { - throw std::runtime_error( - "Unsupported TRFCollection format, the first line is \"" + line + "\" instead of \"" + csvHeader + "\""); - } - - while (std::getline(input, line)) { - boost::tokenizer> tok(line); - - TimeRangeFlag::time_type start = 0; - TimeRangeFlag::time_type end = 0; - FlagReason flag = FlagReasonFactory::Invalid(); - std::string comment; - std::string source; - auto it = tok.begin(); - bool valid = true; - size_t pos = 0; - for (; it != tok.end() && valid; pos++, it++) { - switch (pos) { - case 0: { - if (it->empty()) { - LOG(error) << "Invalid line, empty start time of a flag, skipping..."; - valid = false; - } else { - start = static_cast(std::stoull(*it)); - } - break; - } - case 1: { - if (it->empty()) { - LOG(error) << "Invalid line, empty end time of a flag, skipping..."; - valid = false; - } else { - end = static_cast(std::stoull(*it)); - } - break; - } - case 2: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag id, skipping..."; - valid = false; - } else { - flag.mId = std::stoul(*it); - } - break; - } - case 3: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag name, skipping..."; - valid = false; - } else { - flag.mName = *it; - } - break; - } - case 4: { - if (it->empty()) { - LOG(error) << "Invalid line, empty flag 'bad' field, skipping..."; - valid = false; - } else { - flag.mBad = static_cast(std::stoul(*it)); - } - break; - } - case 5: { - comment = *it; - break; - } - case 6: { - source = *it; - break; - } - default: { - LOG(error) << "More columns (" << pos + 1 << ") than expected (" << csvColumns - << ") in this line, skipping..."; - valid = false; - break; - } - } - } - if (valid && pos < csvColumns) { - LOG(error) << "Less columns (" << pos << ") than expected (" << csvColumns << ") in this line, skipping..."; - } else if (valid) { - insert({start, end, flag, comment, source}); - } - } -} - -std::ostream& operator<<(std::ostream& output, const TimeRangeFlagCollection& data) -{ - data.streamTo(output); - return output; -} - -const std::string& TimeRangeFlagCollection::getName() const -{ - return mName; -} - -const std::string& TimeRangeFlagCollection::getDetector() const -{ - return mDetID; -} - -int TimeRangeFlagCollection::getRunNumber() const -{ - return mRunNumber; -} - -const std::string& TimeRangeFlagCollection::getPeriodName() const -{ - return mPeriodName; -} - -const std::string& TimeRangeFlagCollection::getPassName() const -{ - return mPassName; -} -const std::string& TimeRangeFlagCollection::getProvenance() const -{ - return mProvenance; -} - -} // namespace o2::quality_control diff --git a/DataFormats/QualityControl/test/testFlagReasons.cxx b/DataFormats/QualityControl/test/testFlagReasons.cxx deleted file mode 100644 index 9e7ff15383674..0000000000000 --- a/DataFormats/QualityControl/test/testFlagReasons.cxx +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control FlagReasons class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -#include - -#include -#include - -// o2 includes -#include "DataFormatsQualityControl/FlagReasons.h" -#include "DataFormatsQualityControl/FlagReasonFactory.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(FlagReasons) -{ - static_assert(std::is_constructible::value == false, - "FlagReason should not be constructible outside of its static methods and the factory."); - - FlagReason rDefault; - BOOST_CHECK_EQUAL(rDefault, FlagReasonFactory::Invalid()); - - auto r1 = FlagReasonFactory::Unknown(); - BOOST_CHECK_EQUAL(r1.getID(), 1); - BOOST_CHECK_EQUAL(r1.getName(), "Unknown"); - BOOST_CHECK_EQUAL(r1.getBad(), true); - - std::cout << r1 << std::endl; - - auto r2 = r1; - BOOST_CHECK_EQUAL(r2.getID(), 1); - BOOST_CHECK_EQUAL(r1.getName(), r2.getName()); - BOOST_CHECK_EQUAL(r2.getName(), "Unknown"); - BOOST_CHECK_EQUAL(r2.getBad(), true); - - BOOST_CHECK_EQUAL(r1, r2); - BOOST_CHECK((r1 != r2) == false); - BOOST_CHECK(!(r1 < r2)); - BOOST_CHECK(!(r1 > r2)); - - auto r3 = FlagReasonFactory::LimitedAcceptance(); - BOOST_CHECK(r3 > r1); - BOOST_CHECK(!(r3 < r1)); -} diff --git a/DataFormats/QualityControl/test/testFlagTypes.cxx b/DataFormats/QualityControl/test/testFlagTypes.cxx new file mode 100644 index 0000000000000..2fe904aa1c167 --- /dev/null +++ b/DataFormats/QualityControl/test/testFlagTypes.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control FlagTypes class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include + +#include +#include + +// o2 includes +#include "DataFormatsQualityControl/FlagType.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(FlagTypes) +{ + static_assert(std::is_constructible::value == false, + "FlagType should not be constructible outside of its static methods and the factory."); + + FlagType fDefault; + BOOST_CHECK_EQUAL(fDefault, FlagTypeFactory::Invalid()); + + auto f1 = FlagTypeFactory::Unknown(); + BOOST_CHECK_EQUAL(f1.getID(), 14); + BOOST_CHECK_EQUAL(f1.getName(), "Unknown"); + BOOST_CHECK_EQUAL(f1.getBad(), true); + + BOOST_CHECK_NO_THROW(std::cout << f1 << std::endl); + + auto f2 = f1; + BOOST_CHECK_EQUAL(f2.getID(), 14); + BOOST_CHECK_EQUAL(f1.getName(), f2.getName()); + BOOST_CHECK_EQUAL(f2.getName(), "Unknown"); + BOOST_CHECK_EQUAL(f2.getBad(), true); + + BOOST_CHECK_EQUAL(f1, f2); + BOOST_CHECK((f1 != f2) == false); + BOOST_CHECK(!(f1 < f2)); + BOOST_CHECK(!(f1 > f2)); + + auto f3 = FlagTypeFactory::LimitedAcceptanceMCNotReproducible(); + BOOST_CHECK(f3 < f1); + BOOST_CHECK(!(f3 > f1)); +} diff --git a/DataFormats/QualityControl/test/testQualityControlFlag.cxx b/DataFormats/QualityControl/test/testQualityControlFlag.cxx new file mode 100644 index 0000000000000..2990b3bbd355f --- /dev/null +++ b/DataFormats/QualityControl/test/testQualityControlFlag.cxx @@ -0,0 +1,40 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control QualityControlFlag class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +// boost includes +#include + +// o2 includes +#include "DataFormatsQualityControl/QualityControlFlag.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(test_QualityControlFlag) +{ + QualityControlFlag qcFlag1{12, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + BOOST_CHECK_EQUAL(qcFlag1.getStart(), 12); + BOOST_CHECK_EQUAL(qcFlag1.getEnd(), 34); + BOOST_CHECK_EQUAL(qcFlag1.getFlag(), FlagTypeFactory::BadTracking()); + BOOST_CHECK_EQUAL(qcFlag1.getComment(), "comment"); + BOOST_CHECK_EQUAL(qcFlag1.getSource(), "source"); + + BOOST_CHECK_THROW((QualityControlFlag{12, 0, FlagTypeFactory::BadTracking()}), std::runtime_error); + + QualityControlFlag qcFlag2{10, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + BOOST_CHECK(qcFlag1 > qcFlag2); + BOOST_CHECK(!(qcFlag1 < qcFlag2)); +} \ No newline at end of file diff --git a/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx b/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx new file mode 100644 index 0000000000000..4eabfc5908741 --- /dev/null +++ b/DataFormats/QualityControl/test/testQualityControlFlagCollection.cxx @@ -0,0 +1,136 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test quality_control QualityControlFlagCollection class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +// boost includes +#include +// STL +#include +// o2 includes +#include "DataFormatsQualityControl/QualityControlFlagCollection.h" +#include "DataFormatsQualityControl/QualityControlFlag.h" +#include "DataFormatsQualityControl/FlagTypeFactory.h" + +using namespace o2::quality_control; + +BOOST_AUTO_TEST_CASE(test_QualityControlFlagCollection_Methods) +{ + QualityControlFlag flag1{12, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + QualityControlFlag flag2{10, 34, FlagTypeFactory::BadTracking(), "comment", "source"}; + + QualityControlFlagCollection qcfc1{"Raw data checks", "TOF", {10, 20000}, 12345, "LHC22k5", "passMC", "qc_mc"}; + qcfc1.insert(flag1); // by copy + qcfc1.insert(flag2); + qcfc1.insert({50, 77, FlagTypeFactory::Invalid()}); // by move + BOOST_CHECK_EQUAL(qcfc1.size(), 3); + BOOST_CHECK_EQUAL(qcfc1.getName(), "Raw data checks"); + BOOST_CHECK_EQUAL(qcfc1.getDetector(), "TOF"); + BOOST_CHECK_EQUAL(qcfc1.getStart(), 10); + BOOST_CHECK_EQUAL(qcfc1.getEnd(), 20000); + BOOST_CHECK_EQUAL(qcfc1.getRunNumber(), 12345); + BOOST_CHECK_EQUAL(qcfc1.getPeriodName(), "LHC22k5"); + BOOST_CHECK_EQUAL(qcfc1.getPassName(), "passMC"); + BOOST_CHECK_EQUAL(qcfc1.getProvenance(), "qc_mc"); + + QualityControlFlagCollection qcfc2{"Reco checks", "TOF"}; + qcfc2.insert({50, 77, FlagTypeFactory::Invalid()}); // this is a duplicate to an entry in qcfc1 + qcfc2.insert({51, 77, FlagTypeFactory::Invalid()}); + qcfc2.insert({1234, 3434, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc2.insert({50, 77, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + BOOST_CHECK_EQUAL(qcfc2.size(), 4); + + // Try merging. Duplicate entries should be left in the 'other' objects. + // Notice that we merge the two partial TRFCs into the third, which covers all cases + QualityControlFlagCollection qcfc3{"ALL", "TOF"}; + qcfc3.merge(qcfc1); + qcfc3.merge(qcfc2); + BOOST_CHECK_EQUAL(qcfc1.size(), 0); + BOOST_CHECK_EQUAL(qcfc2.size(), 1); + BOOST_CHECK_EQUAL(qcfc3.size(), 6); + + // Try const merging. It should copy the elements and keep the 'other' intact. + QualityControlFlagCollection qcfc4{"ALL", "TOF"}; + const auto& constTrfc3 = qcfc3; + qcfc4.merge(constTrfc3); + BOOST_CHECK_EQUAL(qcfc3.size(), 6); + BOOST_CHECK_EQUAL(qcfc4.size(), 6); + + // Try merging different detectors - it should throw. + QualityControlFlagCollection qcfc5{"ALL", "TPC"}; + BOOST_CHECK_THROW(qcfc5.merge(qcfc3), std::runtime_error); + BOOST_CHECK_THROW(qcfc5.merge(constTrfc3), std::runtime_error); + + // try printing + std::cout << qcfc3 << std::endl; + + // iterating + for (const auto& flag : qcfc3) { + (void)flag; + } +} + +BOOST_AUTO_TEST_CASE(test_QualityControlFlagCollection_IO) +{ + { + QualityControlFlagCollection qcfc1{"xyz", "TST"}; + + std::stringstream store; + qcfc1.streamTo(store); + + QualityControlFlagCollection qcfc2{"xyz", "TST"}; + qcfc2.streamFrom(store); + + BOOST_CHECK_EQUAL(qcfc2.size(), 0); + } + { + QualityControlFlagCollection qcfc1{"xyz", "TST"}; + qcfc1.insert({50, 77, FlagTypeFactory::Invalid(), "a comment", "a source"}); + qcfc1.insert({51, 77, FlagTypeFactory::Invalid()}); + qcfc1.insert({1234, 3434, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc1.insert({50, 77, FlagTypeFactory::LimitedAcceptanceMCNotReproducible()}); + qcfc1.insert({43434, 63421, FlagTypeFactory::Good()}); + + std::stringstream store; + qcfc1.streamTo(store); + + QualityControlFlagCollection qcfc2{"xyz", "TST"}; + qcfc2.streamFrom(store); + + BOOST_REQUIRE_EQUAL(qcfc1.size(), qcfc2.size()); + for (auto it1 = qcfc1.begin(), it2 = qcfc2.begin(); it1 != qcfc1.end() && it2 != qcfc2.end(); ++it1, ++it2) { + BOOST_CHECK_EQUAL(*it1, *it2); + } + } + { + std::stringstream store; + store << "start,end,flag_id,invalid,header,format\n"; + store << R"(123,345,11,"fdsa",1,"comment","source")"; + QualityControlFlagCollection qcfc1{"A", "TST"}; + BOOST_CHECK_THROW(qcfc1.streamFrom(store), std::runtime_error); + } + { + std::stringstream store; + store << "start,end,flag_id,flag_name,flag_bad,comment,source\n"; + store << R"(123,345,11,"fdsa",1,"comment","source","toomanycolumns")" << '\n'; + store << R"(123,345,11,"fdsa",1)" << '\n'; + store << R"(123,,11,"fdsa",1,"comment","source")" << '\n'; + store << R"(,345,11,"fdsa",1,"comment","source")" << '\n'; + store << R"(123,345,,"fdsa",1,"comment","source")" << '\n'; + store << R"(123,345,11,"",1,"comment","source")" << '\n'; + store << R"(123,345,11,"fdsa",,"comment","source")" << '\n'; + QualityControlFlagCollection qcfc1{"A", "TST"}; + BOOST_CHECK_NO_THROW(qcfc1.streamFrom(store)); + BOOST_CHECK_EQUAL(qcfc1.size(), 0); + } +} diff --git a/DataFormats/QualityControl/test/testTimeRangeFlag.cxx b/DataFormats/QualityControl/test/testTimeRangeFlag.cxx deleted file mode 100644 index 47eef61199103..0000000000000 --- a/DataFormats/QualityControl/test/testTimeRangeFlag.cxx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control TimeRangeFlag class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -// boost includes -#include - -// o2 includes -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlag) -{ - TimeRangeFlag trf1{12, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - BOOST_CHECK_EQUAL(trf1.getStart(), 12); - BOOST_CHECK_EQUAL(trf1.getEnd(), 34); - BOOST_CHECK_EQUAL(trf1.getFlag(), FlagReasonFactory::BadTracking()); - BOOST_CHECK_EQUAL(trf1.getComment(), "comment"); - BOOST_CHECK_EQUAL(trf1.getSource(), "source"); - - BOOST_CHECK_THROW((TimeRangeFlag{12, 0, FlagReasonFactory::BadTracking()}), std::runtime_error); - - TimeRangeFlag trf2{10, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - BOOST_CHECK(trf1 > trf2); - BOOST_CHECK(!(trf1 < trf2)); -} \ No newline at end of file diff --git a/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx b/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx deleted file mode 100644 index 9f5468ce00865..0000000000000 --- a/DataFormats/QualityControl/test/testTimeRangeFlagCollection.cxx +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test quality_control TimeRangeFlagCollection class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK - -// boost includes -#include -// STL -#include -// o2 includes -#include "DataFormatsQualityControl/TimeRangeFlagCollection.h" -#include "DataFormatsQualityControl/TimeRangeFlag.h" - -using namespace o2::quality_control; - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlagCollection_Methods) -{ - TimeRangeFlag trf1{12, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - TimeRangeFlag trf2{10, 34, FlagReasonFactory::BadTracking(), "comment", "source"}; - - TimeRangeFlagCollection trfc1{"Raw data checks", "TOF", {10, 20000}, 12345, "LHC22k5", "passMC", "qc_mc"}; - trfc1.insert(trf1); // by copy - trfc1.insert(trf2); - trfc1.insert({50, 77, FlagReasonFactory::Invalid()}); // by move - BOOST_CHECK_EQUAL(trfc1.size(), 3); - BOOST_CHECK_EQUAL(trfc1.getName(), "Raw data checks"); - BOOST_CHECK_EQUAL(trfc1.getDetector(), "TOF"); - BOOST_CHECK_EQUAL(trfc1.getStart(), 10); - BOOST_CHECK_EQUAL(trfc1.getEnd(), 20000); - BOOST_CHECK_EQUAL(trfc1.getRunNumber(), 12345); - BOOST_CHECK_EQUAL(trfc1.getPeriodName(), "LHC22k5"); - BOOST_CHECK_EQUAL(trfc1.getPassName(), "passMC"); - BOOST_CHECK_EQUAL(trfc1.getProvenance(), "qc_mc"); - - TimeRangeFlagCollection trfc2{"Reco checks", "TOF"}; - trfc2.insert({50, 77, FlagReasonFactory::Invalid()}); // this is a duplicate to an entry in trfc1 - trfc2.insert({51, 77, FlagReasonFactory::Invalid()}); - trfc2.insert({1234, 3434, FlagReasonFactory::LimitedAcceptance()}); - trfc2.insert({50, 77, FlagReasonFactory::LimitedAcceptance()}); - BOOST_CHECK_EQUAL(trfc2.size(), 4); - - // Try merging. Duplicate entries should be left in the 'other' objects. - // Notice that we merge the two partial TRFCs into the third, which covers all cases - TimeRangeFlagCollection trfc3{"ALL", "TOF"}; - trfc3.merge(trfc1); - trfc3.merge(trfc2); - BOOST_CHECK_EQUAL(trfc1.size(), 0); - BOOST_CHECK_EQUAL(trfc2.size(), 1); - BOOST_CHECK_EQUAL(trfc3.size(), 6); - - // Try const merging. It should copy the elements and keep the 'other' intact. - TimeRangeFlagCollection trfc4{"ALL", "TOF"}; - const auto& constTrfc3 = trfc3; - trfc4.merge(constTrfc3); - BOOST_CHECK_EQUAL(trfc3.size(), 6); - BOOST_CHECK_EQUAL(trfc4.size(), 6); - - // Try merging different detectors - it should throw. - TimeRangeFlagCollection trfc5{"ALL", "TPC"}; - BOOST_CHECK_THROW(trfc5.merge(trfc3), std::runtime_error); - BOOST_CHECK_THROW(trfc5.merge(constTrfc3), std::runtime_error); - - // try printing - std::cout << trfc3 << std::endl; - - // iterating - for (const auto& trf : trfc3) { - (void)trf; - } -} - -BOOST_AUTO_TEST_CASE(test_TimeRangeFlagCollection_IO) -{ - { - TimeRangeFlagCollection trfc1{"xyz", "TST"}; - - std::stringstream store; - trfc1.streamTo(store); - - TimeRangeFlagCollection trfc2{"xyz", "TST"}; - trfc2.streamFrom(store); - - BOOST_CHECK_EQUAL(trfc2.size(), 0); - } - { - TimeRangeFlagCollection trfc1{"xyz", "TST"}; - trfc1.insert({50, 77, FlagReasonFactory::Invalid(), "a comment", "a source"}); - trfc1.insert({51, 77, FlagReasonFactory::Invalid()}); - trfc1.insert({1234, 3434, FlagReasonFactory::LimitedAcceptance()}); - trfc1.insert({50, 77, FlagReasonFactory::LimitedAcceptance()}); - trfc1.insert({43434, 63421, FlagReasonFactory::NotBadFlagExample()}); - - std::stringstream store; - trfc1.streamTo(store); - - TimeRangeFlagCollection trfc2{"xyz", "TST"}; - trfc2.streamFrom(store); - - BOOST_REQUIRE_EQUAL(trfc1.size(), trfc2.size()); - for (auto it1 = trfc1.begin(), it2 = trfc2.begin(); it1 != trfc1.end() && it2 != trfc2.end(); ++it1, ++it2) { - BOOST_CHECK_EQUAL(*it1, *it2); - } - } - { - std::stringstream store; - store << "start,end,flag_id,invalid,header,format\n"; - store << R"(123,345,11,"fdsa",1,"comment","source")"; - TimeRangeFlagCollection trfc1{"A", "TST"}; - BOOST_CHECK_THROW(trfc1.streamFrom(store), std::runtime_error); - } - { - std::stringstream store; - store << "start,end,flag_id,flag_name,flag_bad,comment,source\n"; - store << R"(123,345,11,"fdsa",1,"comment","source","toomanycolumns")" << '\n'; - store << R"(123,345,11,"fdsa",1)" << '\n'; - store << R"(123,,11,"fdsa",1,"comment","source")" << '\n'; - store << R"(,345,11,"fdsa",1,"comment","source")" << '\n'; - store << R"(123,345,,"fdsa",1,"comment","source")" << '\n'; - store << R"(123,345,11,"",1,"comment","source")" << '\n'; - store << R"(123,345,11,"fdsa",,"comment","source")" << '\n'; - TimeRangeFlagCollection trfc1{"A", "TST"}; - BOOST_CHECK_NO_THROW(trfc1.streamFrom(store)); - BOOST_CHECK_EQUAL(trfc1.size(), 0); - } -} diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 9c2063b87e74d..d3ca8fdc70ad6 100644 --- a/DataFormats/Reconstruction/CMakeLists.txt +++ b/DataFormats/Reconstruction/CMakeLists.txt @@ -8,6 +8,7 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) o2_add_library(ReconstructionDataFormats SOURCES src/TrackParametrization.cxx @@ -17,6 +18,7 @@ o2_add_library(ReconstructionDataFormats src/TrackTPCITS.cxx src/Vertex.cxx src/PrimaryVertex.cxx + src/PrimaryVertexExt.cxx src/MatchInfoTOF.cxx src/MatchInfoTOFReco.cxx src/TrackLTIntegral.cxx @@ -24,7 +26,8 @@ o2_add_library(ReconstructionDataFormats src/DCA.cxx src/V0.cxx src/Cascade.cxx - src/DecayNbody.cxx + src/Decay3Body.cxx + src/DecayNBodyIndex.cxx src/StrangeTrack.cxx src/GlobalTrackID.cxx src/VtxTrackIndex.cxx @@ -32,12 +35,14 @@ o2_add_library(ReconstructionDataFormats src/TrackTPCTOF.cxx src/TrackCosmics.cxx src/TrackMCHMID.cxx + src/TrackHMP.cxx src/MatchInfoHMP.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon O2::GPUUtils O2::FrameworkLogger O2::DetectorsCommonDataFormats - O2::CommonDataFormat) + O2::CommonDataFormat + O2::Field) o2_target_root_dictionary( ReconstructionDataFormats @@ -48,6 +53,7 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/GlobalFwdTrack.h include/ReconstructionDataFormats/Vertex.h include/ReconstructionDataFormats/PrimaryVertex.h + include/ReconstructionDataFormats/PrimaryVertexExt.h include/ReconstructionDataFormats/MatchInfoTOF.h include/ReconstructionDataFormats/MatchInfoTOFReco.h include/ReconstructionDataFormats/TrackLTIntegral.h @@ -55,7 +61,8 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/DCA.h include/ReconstructionDataFormats/V0.h include/ReconstructionDataFormats/Cascade.h - include/ReconstructionDataFormats/DecayNbody.h + include/ReconstructionDataFormats/DecayNBodyIndex.h + include/ReconstructionDataFormats/Decay3Body.h include/ReconstructionDataFormats/StrangeTrack.h include/ReconstructionDataFormats/GlobalTrackID.h include/ReconstructionDataFormats/VtxTrackIndex.h @@ -65,7 +72,9 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/TrackMCHMID.h include/ReconstructionDataFormats/MatchInfoFwd.h include/ReconstructionDataFormats/BCRange.h + include/ReconstructionDataFormats/TrackHMP.h include/ReconstructionDataFormats/MatchInfoHMP.h + include/ReconstructionDataFormats/HelixHelper.h ) o2_add_test(Vertex diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h index 77047357c092f..7744036247f85 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Cascade.h @@ -27,16 +27,12 @@ namespace dataformats class Cascade : public V0 { public: + using V0::V0; Cascade() = default; + Cascade(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor, - int v0ID, GIndex bachelorID, o2::track::PID pid = o2::track::PID::XiMinus); - - GIndex getBachelorID() const { return mProngIDs[1]; } - void setBachelorID(GIndex gid) { mProngIDs[1] = gid; } - - int getV0ID() const { return int(mProngIDs[0].getRaw()); } - void setV0ID(int vid) { mProngIDs[0].setRaw(GIndex::Base_t(vid)); } + o2::track::PID pid = o2::track::PID::XiMinus) : V0(xyz, pxyz, covxyz, v0, bachelor, pid) {} const Track& getV0Track() const { return mProngs[0]; } Track& getV0Track() { return mProngs[0]; } @@ -47,10 +43,10 @@ class Cascade : public V0 void setV0Track(const Track& t) { mProngs[0] = t; } void setBachelorTrack(const Track& t) { mProngs[1] = t; } - protected: - GIndex getProngID(int i) const = delete; + float calcMass2AsXiMinus() const { return calcMass2PID(PID::Lambda, PID::Pion); } + float calcMass2AsOmegaMinus() const { return calcMass2PID(PID::Lambda, PID::Kaon); } - ClassDefNV(Cascade, 1); + ClassDefNV(Cascade, 2); }; } // namespace dataformats diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h index 922470f8992f5..6eb41b798e101 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DCA.h @@ -14,10 +14,10 @@ #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" #ifndef GPUCA_GPUCODE_DEVICE #include +#include #endif /// \author ruben.shahoyan@cern.ch @@ -67,7 +67,7 @@ class DCA private: float mY = 0.f; float mZ = 0.f; - gpu::gpustd::array mCov; ///< s2y, syz, s2z + std::array mCov; ///< s2y, syz, s2z ClassDefNV(DCA, 1); }; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h new file mode 100644 index 0000000000000..f7f18976fa508 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h @@ -0,0 +1,60 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_3BODY_H +#define ALICEO2_3BODY_H + +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/PID.h" +#include +#include + +namespace o2 +{ +namespace dataformats +{ + +class Decay3Body : public o2::track::TrackParCov /// TO BE DONE: extend to generic N body vertex +{ + public: + using Track = o2::track::TrackParCov; + using PID = o2::track::PID; + + Decay3Body() = default; + Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid = o2::track::PID::HyperTriton); + + const Track& getProng(int i) const { return mProngs[i]; } + Track& getProng(int i) { return mProngs[i]; } + void setProng(int i, const Track& t) { mProngs[i] = t; } + + float getCosPA() const { return mCosPA; } + void setCosPA(float c) { mCosPA = c; } + + float getDCA() const { return mDCA; } + void setDCA(float d) { mDCA = d; } + + float calcMass2() const { return calcMass2PID(mProngs[0].getPID(), mProngs[1].getPID(), mProngs[2].getPID()); } + float calcMass2PID(int pid0, int pid1, int pid2) const { return calcMass2(PID::getMass2(pid0), PID::getMass2(pid1), PID::getMass2(pid2)); } + float calcMass2(float mass0, float mass1, float mass2) const; + + float calcR2() const { return getX() * getX() + getY() * getY(); } + + protected: + std::array mProngs; // prongs kinematics at vertex + float mCosPA = 0; // cos of pointing angle + float mDCA = 9990; // distance of closest approach of prongs + + ClassDefNV(Decay3Body, 1); +}; + +} // namespace dataformats +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h new file mode 100644 index 0000000000000..5a5a8a9e64cca --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h @@ -0,0 +1,108 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_NBODY_INDEX_H +#define ALICEO2_NBODY_INDEX_H + +#include "ReconstructionDataFormats/VtxTrackIndex.h" + +namespace o2::dataformats +{ + +template +class DecayNBodyIndex +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + DecayNBodyIndex() = default; + DecayNBodyIndex(int v, const std::array& arr) : mVertexID(v), mProngIDs(arr) {} + DecayNBodyIndex(int v, std::initializer_list l) : mVertexID(v) + { + assert(l.size() == N); + int i = 0; + for (auto e : l) { + mProngIDs[i++] = e; + } + } + GIndex getProngID(int i) const { return mProngIDs[i]; } + void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; } + int getVertexID() const { return mVertexID; } + void setVertexID(int id) { mVertexID = id; } + uint8_t getBits() const { return mBits; } + bool testBit(int i) const { return (mBits & (0x1 << i)) != 0; } + void setBit(int i) { mBits |= (0x1 << i); } + void resetBit(int i) { mBits &= ~(0x1 << i); } + + const std::array& getProngs() const { return mProngIDs; } + static constexpr int getNProngs() { return N; } + + protected: + int mVertexID = -1; // id of parent vertex + std::array mProngIDs{}; // global IDs of prongs + uint8_t mBits = 0; // user defined bits + + ClassDefNV(DecayNBodyIndex, 2); +}; + +class V0Index : public DecayNBodyIndex<2> +{ + public: + enum V0Type : uint8_t { + kStandaloneV0 = 0, + kPhotonOnly, + kCollinear, + }; + using DecayNBodyIndex<2>::DecayNBodyIndex; + V0Index(int v, GIndex p, GIndex n) : DecayNBodyIndex<2>(v, {p, n}) {} + bool isStandaloneV0() const { return testBit(kStandaloneV0); } + bool isPhotonOnly() const { return testBit(kPhotonOnly); } + bool isCollinear() const { return testBit(kCollinear); } + void setStandaloneV0() { setBit(kStandaloneV0); } + void setPhotonOnly() { setBit(kPhotonOnly); } + void setCollinear() { setBit(kCollinear); } + ClassDefNV(V0Index, 1); +}; + +class Decay3BodyIndex : public DecayNBodyIndex<3> +{ + public: + using DecayNBodyIndex<3>::DecayNBodyIndex; + Decay3BodyIndex(int v, GIndex p0, GIndex p1, GIndex p2) : DecayNBodyIndex<3>(v, {p0, p1, p2}) {} + ClassDefNV(Decay3BodyIndex, 1); +}; + +class CascadeIndex +{ + public: + using GIndex = o2::dataformats::VtxTrackIndex; + CascadeIndex() = default; + CascadeIndex(int v, int v0id, GIndex bachelorID) : mVertexID(v), mV0ID(v0id), mBach(bachelorID) {} + + GIndex getBachelorID() const { return mBach; } + void setBachelorID(GIndex gid) { mBach = gid; } + + int getV0ID() const { return mV0ID; } + void setV0ID(int vid) { mV0ID = vid; } + + int getVertexID() const { return mVertexID; } + void setVertexID(int id) { mVertexID = id; } + + protected: + int mVertexID = -1; + int mV0ID = -1; + GIndex mBach{}; + + ClassDefNV(CascadeIndex, 1); +}; + +} // namespace o2::dataformats + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h deleted file mode 100644 index a249aa87fc900..0000000000000 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNbody.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_NBODY_H -#define ALICEO2_NBODY_H - -#include "ReconstructionDataFormats/VtxTrackIndex.h" -#include "ReconstructionDataFormats/Track.h" -#include "ReconstructionDataFormats/PID.h" -#include -#include - -namespace o2 -{ -namespace dataformats -{ - -class DecayNbody : public o2::track::TrackParCov /// TO BE DONE: extend to generic N body vertex -{ - public: - using GIndex = o2::dataformats::VtxTrackIndex; - using Track = o2::track::TrackParCov; - using PID = o2::track::PID; - - DecayNbody() = default; - DecayNbody(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, GIndex trID0, GIndex trID1, GIndex trID2); - - GIndex getProngID(int i) const { return mProngIDs[i]; } - void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; } - - const Track& getProng(int i) const { return mProngs[i]; } - Track& getProng(int i) { return mProngs[i]; } - void setProng(int i, const Track& t) { mProngs[i] = t; } - - float getCosPA() const { return mCosPA; } - void setCosPA(float c) { mCosPA = c; } - - float getDCA() const { return mDCA; } - void setDCA(float d) { mDCA = d; } - - int getVertexID() const { return mVertexID; } - void setVertexID(int id) { mVertexID = id; } - - float calcMass2() const { return calcMass2(mProngs[0].getPID(), mProngs[1].getPID(), mProngs[2].getPID()); } - float calcMass2(PID pid0, PID pid1, PID pid2) const { return calcMass2(pid0.getMass2(), pid1.getMass2(), pid2.getMass2()); } - float calcMass2(float mass0, float mass1, float mass2) const; - - float calcR2() const { return getX() * getX() + getY() * getY(); } - - protected: - std::array mProngIDs; // global IDs of prongs - std::array mProngs; // prongs kinematics at vertex - float mCosPA = 0; // cos of pointing angle - float mDCA = 9990; // distance of closest approach of prongs - int mVertexID = -1; // id of parent vertex - - ClassDefNV(DecayNbody, 1); -}; - -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h index 15d816b7b7727..5d13a216316ef 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalFwdTrack.h @@ -31,6 +31,7 @@ class GlobalFwdTrack : public o2::track::TrackParCovFwd, public o2::dataformats: public: GlobalFwdTrack() = default; GlobalFwdTrack(const GlobalFwdTrack& t) = default; + GlobalFwdTrack(o2::track::TrackParCovFwd const& t) { *this = t; } ~GlobalFwdTrack() = default; SMatrix5 computeResiduals2Cov(const o2::track::TrackParCovFwd& t) const diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h new file mode 100644 index 0000000000000..d197cba256c0e --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/HelixHelper.h @@ -0,0 +1,307 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HelixHelper.h +/// \brief Helper classes for helical tracks manipulations +/// \author ruben.shahoyan@cern.ch + +#ifndef _ALICEO2_HELIX_HELPER_ +#define _ALICEO2_HELIX_HELPER_ + +#include "CommonConstants/MathConstants.h" +#include "MathUtils/Utils.h" +#include "MathUtils/Primitive2D.h" + +namespace o2 +{ +namespace track +{ + +///__________________________________________________________________________ +//< precalculated track radius, center, alpha sin,cos and their combinations +struct TrackAuxPar : public o2::math_utils::CircleXYf_t { + float c, s, cc, ss, cs; // cos ans sin of track alpha and their products + + GPUdDefault() TrackAuxPar() = default; + + template + GPUd() TrackAuxPar(const T& trc, float bz) + { + set(trc, bz); + } + GPUd() float cosDif(const TrackAuxPar& t) const { return c * t.c + s * t.s; } // cos(alpha_this - alha_t) + GPUd() float sinDif(const TrackAuxPar& t) const { return s * t.c - c * t.s; } // sin(alpha_this - alha_t) + + template + GPUd() void set(const T& trc, float bz) + { + trc.getCircleParams(bz, *this, s, c); + cc = c * c; + ss = s * s; + cs = c * s; + } + ClassDefNV(TrackAuxPar, 1); +}; + +//__________________________________________________________ +//< crossing coordinates of 2 circles +struct CrossInfo { + static constexpr float MaxDistXYDef = 10.; + float xDCA[2] = {}; + float yDCA[2] = {}; + int nDCA = 0; + + GPUd() int circlesCrossInfo(const TrackAuxPar& trax0, const TrackAuxPar& trax1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + const auto& trcA = trax0.rC > trax1.rC ? trax0 : trax1; // designate the largest circle as A + const auto& trcB = trax0.rC > trax1.rC ? trax1 : trax0; + nDCA = 0; + float xDist = trcB.xC - trcA.xC, yDist = trcB.yC - trcA.yC; + float dist2 = xDist * xDist + yDist * yDist, dist = o2::gpu::GPUCommonMath::Sqrt(dist2), rsum = trcA.rC + trcB.rC; + if (dist < 1e-12) { + return nDCA; // circles are concentric? + } + if (dist > rsum) { // circles don't touch, chose a point in between + // the parametric equation of lines connecting the centers is + // x = x0 + t/dist * (x1-x0), y = y0 + t/dist * (y1-y0) + if (dist - rsum > maxDistXY) { // too large distance + return nDCA; + } + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC, isCollinear); + } else if (auto dfr = dist + trcB.rC - trcA.rC; dfr < 0.) { // the small circle is nestled into large one w/o touching + if (dfr > -maxDistXY) { + // select the point of closest approach of 2 circles + notTouchingXY(dist, xDist, yDist, trcA, -trcB.rC, isCollinear); + } else { + return nDCA; + } + } else { // 2 intersection points + if (isCollinear) { + /// collinear tracks, e.g. electrons from photon conversion + /// if there are 2 crossings of the circle it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + trcB.rC; + float r1_r = trcA.rC / r2r; + float r2_r = trcB.rC / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * trcB.xC; + yDCA[0] = r2_r * trcA.yC + r1_r * trcB.yC; + nDCA = 1; + } else if (o2::gpu::GPUCommonMath::Abs(xDist) < o2::gpu::GPUCommonMath::Abs(yDist)) { + // to simplify calculations, we move to new frame x->x+Xc0, y->y+Yc0, so that + // the 1st one is centered in origin + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * yDist), b = -xDist / yDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + xDCA[0] = (-ab + det) / (1. + b * b); + yDCA[0] = a + b * xDCA[0] + trcA.yC; + xDCA[0] += trcA.xC; + xDCA[1] = (-ab - det) / (1. + b * b); + yDCA[1] = a + b * xDCA[1] + trcA.yC; + xDCA[1] += trcA.xC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } else { + float a = (trcA.rC * trcA.rC - trcB.rC * trcB.rC + dist2) / (2. * xDist), b = -yDist / xDist, ab = a * b, bb = b * b; + float det = ab * ab - (1. + bb) * (a * a - trcA.rC * trcA.rC); + if (det > 0.) { + det = o2::gpu::GPUCommonMath::Sqrt(det); + yDCA[0] = (-ab + det) / (1. + bb); + xDCA[0] = a + b * yDCA[0] + trcA.xC; + yDCA[0] += trcA.yC; + yDCA[1] = (-ab - det) / (1. + bb); + xDCA[1] = a + b * yDCA[1] + trcA.xC; + yDCA[1] += trcA.yC; + nDCA = 2; + } else { // due to the finite precision the det<=0, i.e. the circles are barely touching, fall back to this special case + notTouchingXY(dist, xDist, yDist, trcA, trcB.rC); + } + } + } + return nDCA; + } + + GPUd() void notTouchingXY(float dist, float xDist, float yDist, const TrackAuxPar& trcA, float rBSign, bool isCollinear = false) + { + if (isCollinear) { + /// for collinear tracks it is better to take + /// a weighted average of the crossing points as a radius + float r2r = trcA.rC + rBSign; + float r1_r = trcA.rC / r2r; + float r2_r = rBSign / r2r; + xDCA[0] = r2_r * trcA.xC + r1_r * (xDist + trcA.xC); + yDCA[0] = r2_r * trcA.yC + r1_r * (yDist + trcA.yC); + } else { + // fast method to calculate DCA between 2 circles, assuming that they don't touch each outer: + // the parametric equation of lines connecting the centers is x = xA + t/dist * xDist, y = yA + t/dist * yDist + // with xA,yY being the center of the circle A ( = trcA.xC, trcA.yC ), xDist = trcB.xC = trcA.xC ... + // There are 2 special cases: + // (a) small circle is inside the large one: provide rBSign as -trcB.rC + // (b) circle are side by side: provide rBSign as trcB.rC + auto t2d = (dist + trcA.rC - rBSign) / dist; + xDCA[0] = trcA.xC + 0.5 * (xDist * t2d); + yDCA[0] = trcA.yC + 0.5 * (yDist * t2d); + } + nDCA = 1; + } + + template + GPUd() int linesCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of 2 straight lines + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 + Kz^2 = (1+tgl^2) / csp^2 + nDCA = 0; + float dx = trax1.xC - trax0.xC; // for straight line TrackAuxPar stores lab coordinates at referene point!!! + float dy = trax1.yC - trax0.yC; // + float dz = tr1.getZ() - tr0.getZ(); + auto csp0i2 = 1. / tr0.getCsp2(); // 1 / csp^2 + auto csp0i = o2::gpu::GPUCommonMath::Sqrt(csp0i2); + auto tgp0 = tr0.getSnp() * csp0i; + float kx0 = trax0.c - trax0.s * tgp0; + float ky0 = trax0.s + trax0.c * tgp0; + float kz0 = tr0.getTgl() * csp0i; + auto csp1i2 = 1. / tr1.getCsp2(); // 1 / csp^2 + auto csp1i = o2::gpu::GPUCommonMath::Sqrt(csp1i2); + auto tgp1 = tr1.getSnp() * o2::gpu::GPUCommonMath::Sqrt(csp1i2); + float kx1 = trax1.c - trax1.s * tgp1; + float ky1 = trax1.s + trax1.c * tgp1; + float kz1 = tr1.getTgl() * csp1i; + /// Minimize |vecL1 - vecL0|^2 wrt t0 and t1: point of closest approach + /// Leads to system + /// A Dx = B with Dx = {dx0, dx1} + /// with A = + /// | kx0^2+ky0^2+kz0^2 -(kx0*kx1+ky0*ky1+kz0*kz1) | = (1+tgl0^2) / csp0^2 .... + /// | -(kx0*kx1+ky0*ky1+kz0*kz1) kx0^2+ky0^2+kz0^2 | ..... (1+tgl1^2) / csp1^2 + /// and B = {(dx Kx0 + dy Ky0 + dz Kz0), -(dx Kx1 + dy Ky1 + dz Kz1) } + /// + float a00 = (1.f + tr0.getTgl() * tr0.getTgl()) * csp0i2, a11 = (1.f + tr1.getTgl() * tr1.getTgl()) * csp1i2, a01 = -(kx0 * kx1 + ky0 * ky1 + kz0 * kz1); + float b0 = dx * kx0 + dy * ky0 + dz * kz0, b1 = -(dx * kx1 + dy * ky1 + dz * kz1); + float det = a00 * a11 - a01 * a01, det0 = b0 * a11 - b1 * a01, det1 = a00 * b1 - a01 * b0; + if (o2::gpu::GPUCommonMath::Sqrt(det) > o2::constants::math::Almost0) { + auto detI = 1. / det; + auto t0 = det0 * detI; + auto t1 = det1 * detI; + float addx0 = kx0 * t0, addy0 = ky0 * t0, addx1 = kx1 * t1, addy1 = ky1 * t1; + dx += addx1 - addx0; // recalculate XY distance at DCA + dy += addy1 - addy0; + if (dx * dx + dy * dy > maxDistXY * maxDistXY) { + return nDCA; + } + xDCA[0] = (trax0.xC + addx0 + trax1.xC + addx1) * 0.5; + yDCA[0] = (trax0.yC + addy0 + trax1.yC + addy1) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int circleLineCrossInfo(const TrackAuxPar& trax0, const T& tr0, + const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef) + { + /// closest approach of line and circle + /// TrackParam propagation can be parameterized in lab in a form + /// xLab(t) = (x*cosAlp - y*sinAlp) + t*(cosAlp - sinAlp* snp/csp) = xLab0 + t*(cosAlp - sinAlp* snp/csp) + /// yLab(t) = (x*sinAlp + y*cosAlp) + t*(sinAlp + cosAlp* snp/csp) = yLab0 + t*(sinAlp + cosAlp* snp/csp) + /// zLab(t) = z + t * tgl / csp = zLab0 + t * tgl / csp + /// where t is the x-step in the track alpha-frame, xLab,yLab,zLab are reference track coordinates in lab + /// frame (filled by TrackAuxPar for straight line tracks). + /// + /// Therefore, for the parametric track equation in lab 3D we have (wrt tracking-X increment t) + /// xL(t) = xL + t Kx; Kx = (cosAlp - sinAlp* snp/csp) + /// yL(t) = yL + t Ky; Ky = (sinAlp + cosAlp* snp/csp) + /// zL(t) = zL + t Kz; Kz = tgl / csp + /// Note that Kx^2 + Ky^2 = 1 / csp^2 + + const auto& traxH = trax0.rC > trax1.rC ? trax0 : trax1; // circle (for the line rC is set to 0) + const auto& traxL = trax0.rC > trax1.rC ? trax1 : trax0; // line + const auto& trcL = trax0.rC > trax1.rC ? tr1 : tr0; // track of the line + + // solve quadratic equation of line crossing the circle + float dx = traxL.xC - traxH.xC; // X distance between the line lab reference and circle center + float dy = traxL.yC - traxH.yC; // Y... + // t^2(kx^2+ky^2) + 2t(dx*kx+dy*ky) + dx^2 + dy^2 - r^2 = 0 + auto cspi2 = 1. / trcL.getCsp2(); // 1 / csp^2 == kx^2 + ky^2 + auto cspi = o2::gpu::GPUCommonMath::Sqrt(cspi2); + auto tgp = trcL.getSnp() * cspi; + float kx = traxL.c - traxL.s * tgp; + float ky = traxL.s + traxL.c * tgp; + double dk = dx * kx + dy * ky; + double det = dk * dk - cspi2 * (dx * dx + dy * dy - traxH.rC * traxH.rC); + if (det > 0) { // 2 crossings + det = o2::gpu::GPUCommonMath::Sqrt(det); + float t0 = (-dk + det) * cspi2; + float t1 = (-dk - det) * cspi2; + xDCA[0] = traxL.xC + kx * t0; + yDCA[0] = traxL.yC + ky * t0; + xDCA[1] = traxL.xC + kx * t1; + yDCA[1] = traxL.yC + ky * t1; + nDCA = 2; + } else { + // there is no crossing, find the point of the closest approach on the line which is closest to the circle center + float t = -dk * cspi2; + float xL = traxL.xC + kx * t, yL = traxL.yC + ky * t; // point on the line, need to average with point on the circle + float dxc = xL - traxH.xC, dyc = yL - traxH.yC, dist = o2::gpu::GPUCommonMath::Sqrt(dxc * dxc + dyc * dyc); + if (dist - traxH.rC > maxDistXY) { // too large distance + return nDCA; + } + float drcf = traxH.rC / dist; // radius / distance to circle center + float xH = traxH.xC + dxc * drcf, yH = traxH.yC + dyc * drcf; + xDCA[0] = (xL + xH) * 0.5; + yDCA[0] = (yL + yH) * 0.5; + nDCA = 1; + } + return nDCA; + } + + template + GPUd() int set(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + // calculate up to 2 crossings between 2 circles + nDCA = 0; + if (trax0.rC > o2::constants::math::Almost0 && trax1.rC > o2::constants::math::Almost0) { // both are not straight lines + nDCA = circlesCrossInfo(trax0, trax1, maxDistXY, isCollinear); + } else if (trax0.rC < o2::constants::math::Almost0 && trax1.rC < o2::constants::math::Almost0) { // both are straigt lines + nDCA = linesCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } else { + nDCA = circleLineCrossInfo(trax0, tr0, trax1, tr1, maxDistXY); + } + // + return nDCA; + } + + GPUdDefault() CrossInfo() = default; + + template + GPUd() CrossInfo(const TrackAuxPar& trax0, const T& tr0, const TrackAuxPar& trax1, const T& tr1, float maxDistXY = MaxDistXYDef, bool isCollinear = false) + { + set(trax0, tr0, trax1, tr1, maxDistXY, isCollinear); + } + ClassDefNV(CrossInfo, 1); +}; + +} // namespace track +} // namespace o2 + +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h index 2d614201c2220..e065b3d7dfe40 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoHMP.h @@ -28,22 +28,43 @@ class MatchInfoHMP using GTrackID = o2::dataformats::GlobalTrackID; public: - MatchInfoHMP(int idxHMPClus, GTrackID idxTrack, float angle = 0, float q = 0, float size = 0, int idxPhotClus = 0) : mIdxHMPClus(idxHMPClus), mIdxTrack(idxTrack), mCkovAngle(angle), mMipCluQ(q), mMipCluSize(size), mIdxPhotClus(idxPhotClus){}; + MatchInfoHMP(int idxHMPClus, GTrackID idxTrack, float xmip = 0, float ymip = 0, float xtrk = 0, float ytrk = 0, float theta = 0, float phi = 0, float angle = 0, float size = 0, int idxPhotClus = 0, int hmpqn = 0, float hmpMom = 0) : mIdxHMPClus(idxHMPClus), mIdxTrack(idxTrack), mCkovAngle(angle), mMipX(xmip), mMipY(ymip), mTrkX(xtrk), mTrkY(ytrk), mTrkTheta(theta), mTrkPhi(phi), mMipCluSize(size), mIdxPhotClus(idxPhotClus), mHMPqn(hmpqn), mHmpMom(hmpMom) + { // Initialize the mPhotCharge array + for (int i = 0; i < 10; i++) { + mPhotCharge[i] = 0.0; + } + }; MatchInfoHMP() = default; - void setIdxHMPClus(int index) { mIdxHMPClus = index; } + void setIdxHMPClus(int ch, int idx) { mIdxHMPClus = ch * 1000000 + idx; } int getIdxHMPClus() const { return mIdxHMPClus; } void setIdxTrack(GTrackID index) { mIdxTrack = index; } + int getTrackIndex() const { return mIdxTrack.getIndex(); } + GTrackID getTrackRef() const { return mIdxTrack; } - int getTrackIndex() const { return mIdxTrack.getIndex(); } + void setMipX(float x) { mMipX = x; } + float getMipX() const { return mMipX; } - void setCkovAngle(float angle) { mCkovAngle = angle; } - float getCkovAngle() const { return mCkovAngle; } + void setMipY(float y) { mMipY = y; } + float getMipY() const { return mMipY; } - void setMipClusQ(float q) { mMipCluQ = q; } - float getMipClusQ() const { return mMipCluQ; } + void setTrkX(float x) { mTrkX = x; } + float getTrkX() const { return mTrkX; } + + void setTrkY(float y) { mTrkY = y; } + float getTrkY() const { return mTrkY; } + + void setHMPsignal(float angle) { mCkovAngle = angle; } + float getHMPsignal() const + { + if (mCkovAngle > 0) { + return mCkovAngle - (Int_t)mCkovAngle; + } else { + return mCkovAngle; + } + } void setMipClusSize(int size) { mMipCluSize = size; } int getMipClusSize() const { return mMipCluSize; } @@ -54,18 +75,71 @@ class MatchInfoHMP void setPhotIndex(int idx) { mIdxPhotClus = idx; } int getPhotIndex() const { return mIdxPhotClus; } + float getOccupancy() const { return (Int_t)mCkovAngle / 10.0; } + + void setHMPIDtrk(float x, float y, float th, float ph) + { + mTrkX = x; + mTrkY = y; + mTrkTheta = th; + mTrkPhi = ph; + } + + void getHMPIDtrk(float& x, float& y, float& th, float& ph) const + { + x = mTrkX; + y = mTrkY; + th = mTrkTheta; + ph = mTrkPhi; + } + + void setHMPIDmip(float x, float y, int q, int nph = 0) + { + mMipX = x; + mMipY = y; + mHMPqn = 1000000 * nph + q; + } + + void getHMPIDmip(float& x, float& y, int& q, int& nph) const + { + x = mMipX; + y = mMipY; + q = mHMPqn % 1000000; + nph = mHMPqn / 1000000; + } + + void setHmpMom(float p) { mHmpMom = p; } + float getHmpMom() const { return mHmpMom; } + + void setPhotCharge(float* chargeArray) + { + for (int i = 0; i < 10; i++) { + mPhotCharge[i] = chargeArray[i]; + } + } + + const float* getPhotCharge() const { return mPhotCharge; } + void print() const; private: int mIdxHMPClus; // Idx for HMP cluster GTrackID mIdxTrack; // Idx for track + float mMipX; // local x coordinate of macthed cluster + float mMipY; // local y coordinate of macthed cluster + float mTrkX; // local x coordinate of extrapolated track intersection point + float mTrkY; // local y coordinate of extrapolated track intersection point + float mTrkTheta; // theta track + float mTrkPhi; // phi track float mCkovAngle; // emission angle value - float mMipCluQ = 0.0; // MIP cluster charge int mMipCluSize = 0.0; // MIP cluster size int mNPhots = 0.0; // number of candidate photo-electrons int mIdxPhotClus; // index of the first photo + int mHMPqn; // 1000000*number of photon clusters + QDC + float mHmpMom; // track momentum at HMPID chambers + float mPhotCharge[10] = {}; - ClassDefNV(MatchInfoHMP, 1); + ClassDefNV(MatchInfoHMP, 2); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h index 9f6b2f00bb37c..7bcfd7af0911a 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h @@ -28,7 +28,7 @@ class MatchInfoTOF using GTrackID = o2::dataformats::GlobalTrackID; public: - MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz){}; + MatchInfoTOF(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0, float geanttime = 0.0, double t0 = 0.0) : mIdLocal(idLocal), mIdxTOFCl(idxTOFCl), mSignal(time), mChi2(chi2), mIntLT(trkIntLT), mIdxTrack(idxTrack), mDeltaT(dt), mZatTOF(z), mDXatTOF(dx), mDZatTOF(dz), mDYatTOF(dy), mTgeant(geanttime), mT0true(t0){}; MatchInfoTOF() = default; void setIdxTOFCl(int index) { mIdxTOFCl = index; } void setIdxTrack(GTrackID index) { mIdxTrack = index; } @@ -38,9 +38,15 @@ class MatchInfoTOF int getTOFClIndex() const { return mIdxTOFCl; } int getTrackIndex() const { return mIdxTrack.getIndex(); } - void setChi2(int chi2) { mChi2 = chi2; } + void setChi2(float chi2) { mChi2 = chi2; } float getChi2() const { return mChi2; } + void setHitPatternUpDown(bool v) { mHitUpDown = v; } + bool getHitPatternUpDown() const { return mHitUpDown; } + + void setHitPatternLeftRight(bool v) { mHitLeftRight = v; } + bool getHitPatternLeftRight() const { return mHitLeftRight; } + o2::track::TrackLTIntegral& getLTIntegralOut() { return mIntLT; } const o2::track::TrackLTIntegral& getLTIntegralOut() const { return mIntLT; } void print() const; @@ -53,11 +59,41 @@ class MatchInfoTOF float getDZatTOF() const { return mDZatTOF; } void setDXatTOF(float val) { mDXatTOF = val; } float getDXatTOF() const { return mDXatTOF; } + void setDYatTOF(float val) { mDYatTOF = val; } + float getDYatTOF() const { return mDYatTOF; } void setSignal(double time) { mSignal = time; } double getSignal() const { return mSignal; } int getIdLocal() const { return mIdLocal; } + float getVz() const { return mVz; } + void setVz(float val) { mVz = val; } + int getChannel() const { return mChannel; } + void setChannel(int val) { mChannel = val; } + float getTgeant() const { return mTgeant; } + void setTgeant(float val) { mTgeant = val; } + double getT0true() const { return mT0true; } + void setT0true(double val) { mT0true = val; } + + enum QualityFlags { isMultiHitX = 0x1 << 0, + isMultiHitZ = 0x1 << 1, + badDy = 0x1 << 2, + isMultiStrip = 0x1 << 3, + isNotInPad = 0x1 << 4, + chiGT3 = 0x1 << 5, + chiGT5 = 0x1 << 6, + hasT0sameBC = 0x1 << 7, + hasT0_1BCbefore = 0x1 << 8, + hasT0_2BCbefore = 0x1 << 9 }; + + void setFT0Best(double val, float res = 200.) + { + mFT0Best = val; + mFT0BestRes = res; + } + double getFT0Best() const { return mFT0Best; } + float getFT0BestRes() const { return mFT0BestRes; } + private: int mIdLocal; // track id in sector of the pair track-TOFcluster float mChi2; // chi2 of the pair track-TOFcluster @@ -67,10 +103,21 @@ class MatchInfoTOF float mZatTOF = 0.0; ///< Z position at TOF float mDXatTOF = 0.0; ///< DX position at TOF float mDZatTOF = 0.0; ///< DZ position at TOF + float mDYatTOF = 0.0; ///< DY position at TOF float mDeltaT = 0.0; ///< tTOF - TPC (microsec) double mSignal = 0.0; ///< TOF time in ps + float mVz = 0.0; ///< Vz from TOF match + int mChannel = -1; ///< channel + // Hit pattern information + bool mHitUpDown = false; ///< hit pattern in TOF up-down + bool mHitLeftRight = false; ///< hit pattern in TOF left-right + float mTgeant = 0.0; ///< geant time in MC + double mT0true = 0.0; ///< t0true + + double mFT0Best = 0.0; //< best info for collision time + float mFT0BestRes = 200.0; //< resolution (in ps) of the best info for collision time - ClassDefNV(MatchInfoTOF, 5); + ClassDefNV(MatchInfoTOF, 9); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h index b1d3830e8083c..f1b555301bf80 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h @@ -35,13 +35,22 @@ class MatchInfoTOFReco : public MatchInfoTOF ITSTPCTRD, SIZEALL }; - MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz), mTrackType(trkType){}; + MatchInfoTOFReco(int idLocal, int idxTOFCl, double time, float chi2, o2::track::TrackLTIntegral trkIntLT, GTrackID idxTrack, TrackType trkType, float dt = 0, float z = 0, float dx = 0, float dz = 0, float dy = 0) : MatchInfoTOF(idLocal, idxTOFCl, time, chi2, trkIntLT, idxTrack, dt, z, dx, dz, dy), mTrackType(trkType){}; MatchInfoTOFReco() = default; void setFakeMatch() { mFakeMC = true; } void resetFakeMatch() { mFakeMC = false; } bool isFake() const { return mFakeMC; } + float pt() const { return mPt; } + void setPt(float pt) { mPt = pt; } + + void setResX(float val) { mResX = val; } + void setResZ(float val) { mResZ = val; } + void setResT(float val) { mResT = val; } + float getResX() const { return mResX; } + float getResZ() const { return mResZ; } + float getResT() const { return mResT; } void setTrackType(TrackType value) { mTrackType = value; } TrackType getTrackType() const { return mTrackType; } @@ -49,7 +58,11 @@ class MatchInfoTOFReco : public MatchInfoTOF private: TrackType mTrackType; ///< track type (TPC, ITSTPC, TPCTRD, ITSTPCTRD) bool mFakeMC = false; - ClassDefNV(MatchInfoTOFReco, 3); + float mPt = 0; + float mResX = 1; + float mResZ = 1; + float mResT = 1; + ClassDefNV(MatchInfoTOFReco, 5); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h index f7124f12902d3..ce70e69aa6ddd 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h @@ -16,6 +16,10 @@ #ifndef ALICEO2_track_PID_H_ #define ALICEO2_track_PID_H_ +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif + #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" #include "CommonConstants/PhysicsConstants.h" @@ -29,16 +33,19 @@ namespace o2cp = o2::constants::physics; namespace pid_constants // GPUs currently cannot have static constexpr array members { typedef uint8_t ID; -static constexpr ID NIDsTot = 17; +static constexpr ID NIDsTot = 19; + +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) GPUconstexpr() const char* sNames[NIDsTot + 1] = ///< defined particle names {"Electron", "Muon", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "He3", "Alpha", - "Pion0", "Photon", "K0", "Lambda", "HyperTriton", "Hyperhydrog4", "XiMinus", "OmegaMinus", nullptr}; + "Pion0", "Photon", "K0", "Lambda", "HyperTriton", "Hyperhydrog4", "XiMinus", "OmegaMinus", "HyperHelium4", "HyperHelium5", nullptr}; +#endif GPUconstexpr() const float sMasses[NIDsTot] = ///< defined particle masses {o2cp::MassElectron, o2cp::MassMuon, o2cp::MassPionCharged, o2cp::MassKaonCharged, o2cp::MassProton, o2cp::MassDeuteron, o2cp::MassTriton, o2cp::MassHelium3, o2cp::MassAlpha, o2cp::MassPionNeutral, o2cp::MassPhoton, - o2cp::MassKaonNeutral, o2cp::MassLambda, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, o2cp::MassXiMinus, o2cp::MassOmegaMinus}; + o2cp::MassKaonNeutral, o2cp::MassLambda, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, o2cp::MassXiMinus, o2cp::MassOmegaMinus, o2cp::MassHyperHelium4, o2cp::MassHyperHelium5}; GPUconstexpr() const float sMasses2[NIDsTot] = ///< defined particle masses^2 {o2cp::MassElectron * o2cp::MassElectron, @@ -57,7 +64,9 @@ GPUconstexpr() const float sMasses2[NIDsTot] = ///< defined particle masses^2 o2cp::MassHyperTriton* o2cp::MassHyperTriton, o2cp::MassHyperhydrog4* o2cp::MassHyperhydrog4, o2cp::MassXiMinus* o2cp::MassXiMinus, - o2cp::MassOmegaMinus* o2cp::MassOmegaMinus}; + o2cp::MassOmegaMinus* o2cp::MassOmegaMinus, + o2cp::MassHyperHelium4* o2cp::MassHyperHelium4, + o2cp::MassHyperHelium5* o2cp::MassHyperHelium5}; GPUconstexpr() const float sMasses2Z[NIDsTot] = ///< defined particle masses / Z {o2cp::MassElectron, o2cp::MassMuon, @@ -66,12 +75,14 @@ GPUconstexpr() const float sMasses2Z[NIDsTot] = ///< defined particle masses / Z o2cp::MassTriton, o2cp::MassHelium3 / 2., o2cp::MassAlpha / 2., 0, 0, 0, 0, o2cp::MassHyperTriton, o2cp::MassHyperhydrog4, - o2cp::MassXiMinus, o2cp::MassOmegaMinus}; + o2cp::MassXiMinus, o2cp::MassOmegaMinus, + o2cp::MassHyperHelium4 / 2., o2cp::MassHyperHelium5 / 2.}; GPUconstexpr() const int sCharges[NIDsTot] = ///< defined particle charges {1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, - 1, 1}; + 1, 1, + 2, 2}; } // namespace pid_constants class PID @@ -103,8 +114,10 @@ class PID static constexpr ID Hyperhydrog4 = 14; static constexpr ID XiMinus = 15; static constexpr ID OmegaMinus = 16; + static constexpr ID HyperHelium4 = 17; + static constexpr ID HyperHelium5 = 18; static constexpr ID FirstExt = PI0; - static constexpr ID LastExt = OmegaMinus; + static constexpr ID LastExt = HyperHelium5; static constexpr ID NIDsTot = pid_constants::NIDsTot; ///< total number of defined IDs static_assert(NIDsTot == LastExt + 1, "Incorrect NIDsTot, please update!"); @@ -112,7 +125,7 @@ class PID GPUd() PID(ID id) : mID(id) {} GPUd() PID(const char* name); GPUdDefault() PID(const PID& src) = default; - GPUdDefault() PID& operator=(const PID& src) = default; + GPUhdDefault() PID& operator=(const PID& src) = default; GPUd() ID getID() const { return mID; } GPUd() operator ID() const { return getID(); } @@ -126,7 +139,7 @@ class PID GPUd() static float getMass2(ID id) { return pid_constants::sMasses2[id]; } GPUd() static float getMass2Z(ID id) { return pid_constants::sMasses2Z[id]; } GPUd() static int getCharge(ID id) { return pid_constants::sCharges[id]; } -#ifndef GPUCA_GPUCODE_DEVICE +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) GPUd() const char* getName() const { return getName(mID); @@ -137,13 +150,13 @@ class PID private: ID mID = Pion; +#if !defined(GPUCA_GPUCODE_DEVICE) || defined(GPUCA_GPU_DEBUG_PRINT) // are 2 strings equal ? (trick from Giulio) GPUdi() static constexpr bool sameStr(char const* x, char const* y) { return !*x && !*y ? true : /* default */ (*x == *y && sameStr(x + 1, y + 1)); } -#ifndef GPUCA_GPUCODE_DEVICE GPUdi() static constexpr ID nameToID(char const* name, ID id) { return id > LastExt ? id : sameStr(name, pid_constants::sNames[id]) ? id : nameToID(name, id + 1); diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h index ce470361034f5..5343d26ec5ce5 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertex.h @@ -37,6 +37,11 @@ class PrimaryVertex : public Vertex> void setIR(const InteractionRecord& ir) { mIRMin = mIRMax = ir; } bool hasUniqueIR() const { return !mIRMin.isDummy() && (mIRMin == mIRMax); } + float getTMAD() const { return mTMAD; } + void setTMAD(float v) { mTMAD = v; } + float getZMAD() const { return mZMAD; } + void setZMAD(float v) { mZMAD = v; } + #ifndef GPUCA_ALIGPUCODE void print() const; std::string asString() const; @@ -45,8 +50,10 @@ class PrimaryVertex : public Vertex> protected: InteractionRecord mIRMin{}; ///< by default not assigned! InteractionRecord mIRMax{}; ///< by default not assigned! + float mTMAD = -1; ///< MAD estimator for Tsigma + float mZMAD = -1; ///< MAD estimator for Zsigma - ClassDefNV(PrimaryVertex, 1); + ClassDefNV(PrimaryVertex, 2); }; #ifndef GPUCA_ALIGPUCODE diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h new file mode 100644 index 0000000000000..bf47ed03f3b39 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_PRIMARYVERTEX_EXT_H +#define ALICEO2_PRIMARYVERTEX_EXT_H + +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace dataformats +{ + +// extended primary vertex info + +struct PrimaryVertexExt : public PrimaryVertex { + using PrimaryVertex::PrimaryVertex; + std::array nSrc{}; // N contributors for each source type + std::array nSrcA{}; // N associated and passing cuts for each source type + std::array nSrcAU{}; // N ambgous associated and passing cuts for each source type + double FT0Time = -1.; // time of closest FT0 trigger + float FT0A = -1; // amplitude of closest FT0 A side + float FT0C = -1; // amplitude of closest FT0 C side + int VtxID = -1; // original vtx ID + + int getNSrc(int i) const { return nSrc[i]; } + int getNSrcA(int i) const { return nSrcA[i]; } + int getNSrcAU(int i) const { return nSrcAU[i]; } + +#ifndef GPUCA_ALIGPUCODE + void print() const; + std::string asString() const; +#endif + + ClassDefNV(PrimaryVertexExt, 6); +}; + +#ifndef GPUCA_ALIGPUCODE +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v); +#endif + +} // namespace dataformats + +/// Defining PrimaryVertexExt explicitly as messageable +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h index c2af20fc93fa2..12e376f10f3d8 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/StrangeTrack.h @@ -35,10 +35,49 @@ struct StrangeTrack { unsigned int mDecayRef = -1; std::array mDecayVtx; std::array mDecayMom; - std::array mMasses; // V0: hypertriton and hyperhydrongen4, cascade: Xi and Omega. - float mITSClusSize; + std::array mMasses; // V0: hypertriton and hyperhydrongen4, cascade: Xi and Omega. + unsigned int mClusterSizes = 0u; // same encoding used for the ITS track float mMatchChi2; float mTopoChi2; + + void setClusterSize(int l, int size) + { + if (l >= 8) { + return; + } + if (size > 15) { + size = 15; + } + mClusterSizes &= ~(0xf << (l * 4)); + mClusterSizes |= (size << (l * 4)); + } + + int getClusterSize(int l) const + { + if (l >= 7) { + return 0; + } + return (mClusterSizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return mClusterSizes; + } + + float getAverageClusterSize() const + { + int nClusters = 0; + int nClustersSize = 0; + for (int i = 0; i < 7; ++i) { + int size = getClusterSize(i); + if (size > 0) { + nClustersSize += size; + nClusters++; + } + } + return nClusters > 0 ? static_cast(nClustersSize) / nClusters : 0.f; + } }; } // namespace dataformats diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h index 00138920e8b0e..50ed36d466d25 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h @@ -161,6 +161,7 @@ class TrackParCovFwd : public TrackParFwd void propagateToZquadratic(double zEnd, double zField); void propagateToZhelix(double zEnd, double zField); void propagateToZ(double zEnd, double zField); // Parameters: helix; errors: quadratic + void propagateToDCAhelix(double zField, const std::array& p, std::array& dca); // Add Multiple Coulomb Scattering effects void addMCSEffect(double x2X0); @@ -172,6 +173,8 @@ class TrackParCovFwd : public TrackParFwd bool propagateToVtxhelixWithMCS(double z, const std::array& p, const std::array& cov, double field, double x_over_X0); bool propagateToVtxlinearWithMCS(double z, const std::array& p, const std::array& cov, double x_over_X0); + bool getCovXYZPxPyPzGlo(std::array& cv) const; + private: /// Covariance matrix of track parameters, ordered as follows:
   ///                                     
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h
new file mode 100644
index 0000000000000..8bef3a40b603a
--- /dev/null
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackHMP.h
@@ -0,0 +1,54 @@
+// Copyright 2020-2022 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#ifndef ALICEO2_TRACKHMP_H
+#define ALICEO2_TRACKHMP_H
+
+#include "TMath.h"
+#include "TVector2.h"
+
+#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
+#include "ReconstructionDataFormats/TrackParametrization.h"
+#include "ReconstructionDataFormats/Track.h"
+#include "ReconstructionDataFormats/TrackLTIntegral.h"
+#include "ReconstructionDataFormats/GlobalTrackID.h"
+
+// #include "HMPIDBase/Param.h"                 // for param
+
+namespace o2
+{
+namespace dataformats
+{
+
+class TrackHMP : public o2::track::TrackParCov
+{
+ public:
+  TrackHMP();
+  TrackHMP(const TrackHMP& t) = default;
+  TrackHMP(const o2::track::TrackParCov& t) : o2::track::TrackParCov{t} {};
+  //  TrackHMP(const o2::track::TrackParCov& t);
+  TrackHMP& operator=(const o2::track::TrackParCov& t);
+  ~TrackHMP() = default;
+
+  Bool_t intersect(Double_t pnt[3], Double_t norm[3], double bz) const; //
+  void propagate(Double_t len, std::array& x, std::array& p, double bz) const;
+
+  // protected:
+  //    Bool_t   Update(const AliCluster */*c*/, Double_t /*chi2*/, Int_t /*idx*/) {return 0;}
+  //    Double_t GetPredictedChi2(const AliCluster */*c*/) const {return 0.;}
+ private:
+  ClassDef(TrackHMP, 1) // HMPID reconstructed tracks
+};
+
+} // namespace dataformats
+} // namespace o2
+
+#endif
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
index 6cf9ceda8e195..e799804805972 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackLTIntegral.h
@@ -63,7 +63,7 @@ class TrackLTIntegral
     }
   }
 
-  GPUd() void addStep(float dL, float p2Inv);
+  GPUd() void addStep(float dL, float q2p2);
   GPUd() void addX2X0(float d) { mX2X0 += d; }
   GPUd() void addXRho(float d) { mXRho += d; }
 
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
index 312fd89e321a2..1d6c4d9f0e4ea 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h
@@ -29,7 +29,6 @@
 #include "GPUCommonDef.h"
 #include "GPUCommonRtypes.h"
 #include "GPUCommonMath.h"
-#include "GPUCommonArray.h"
 #include "GPUROOTCartesianFwd.h"
 
 #ifndef GPUCA_GPUCODE_DEVICE
@@ -39,6 +38,7 @@
 #include 
 #include 
 #include 
+#include 
 #endif
 
 #ifndef GPUCA_ALIGPUCODE // Used only by functions that are hidden on the GPU
@@ -115,6 +115,15 @@ GPUconstexpr() int CovarMap[kNParams][kNParams] = {{0, 1, 3, 6, 10},
 GPUconstexpr() int DiagMap[kNParams] = {0, 2, 5, 9, 14};
 
 constexpr float HugeF = o2::constants::math::VeryBig;
+constexpr float MaxPT = 100000.;                  // do not allow pTs exceeding this value (to avoid NANs)
+constexpr float MinPTInv = 1. / MaxPT;            // do not allow q/pTs less this value (to avoid NANs)
+constexpr float ELoss2EKinThreshInv = 1. / 0.025; // do not allow E.Loss correction step with dE/Ekin above the inverse of this value
+constexpr int MaxELossIter = 50;                  // max number of iteration for the ELoss to account for BB dependence on beta*gamma
+constexpr float DefaultDCA = 999.f;               // default DCA value
+constexpr float DefaultDCACov = 999.f;            // default DCA cov value
+
+// uncomment this to enable correction for BB dependence on beta*gamma via BB derivative
+// #define _BB_NONCONST_CORR_
 
 template 
 class TrackParametrization
@@ -122,9 +131,9 @@ class TrackParametrization
 
  public:
   using value_t = value_T;
-  using dim2_t = gpu::gpustd::array;
-  using dim3_t = gpu::gpustd::array;
-  using params_t = gpu::gpustd::array;
+  using dim2_t = std::array;
+  using dim3_t = std::array;
+  using params_t = std::array;
 
   struct yzerr_t { // 2 measurement with error
     dim2_t yz;
@@ -140,8 +149,8 @@ class TrackParametrization
   GPUd() TrackParametrization(const dim3_t& xyz, const dim3_t& pxpypz, int charge, bool sectorAlpha = true, const PID pid = PID::Pion);
   GPUdDefault() TrackParametrization(const TrackParametrization&) = default;
   GPUdDefault() TrackParametrization(TrackParametrization&&) = default;
-  GPUdDefault() TrackParametrization& operator=(const TrackParametrization& src) = default;
-  GPUdDefault() TrackParametrization& operator=(TrackParametrization&& src) = default;
+  GPUhdDefault() TrackParametrization& operator=(const TrackParametrization& src) = default;
+  GPUhdDefault() TrackParametrization& operator=(TrackParametrization&& src) = default;
   GPUdDefault() ~TrackParametrization() = default;
 
   GPUd() void set(value_t x, value_t alpha, const params_t& par, int charge = 1, const PID pid = PID::Pion);
@@ -154,11 +163,11 @@ class TrackParametrization
   GPUd() value_t getZ() const;
   GPUd() value_t getSnp() const;
   GPUd() value_t getTgl() const;
-  GPUd() value_t getQ2Pt() const;
+  GPUhd() value_t getQ2Pt() const;
   GPUd() value_t getCharge2Pt() const;
   GPUd() int getAbsCharge() const;
   GPUd() PID getPID() const;
-  GPUd() void setPID(const PID pid);
+  GPUd() void setPID(const PID pid, bool passCharge = false);
 
   /// calculate cos^2 and cos of track direction in rphi-tracking
   GPUd() value_t getCsp2() const;
@@ -185,19 +194,25 @@ class TrackParametrization
   GPUd() value_t getPhi() const;
   GPUd() value_t getPhiPos() const;
 
+  GPUd() value_t getQ2P2() const;
   GPUd() value_t getPtInv() const;
   GPUd() value_t getP2Inv() const;
   GPUd() value_t getP2() const;
   GPUd() value_t getPInv() const;
   GPUd() value_t getP() const;
   GPUd() value_t getPt() const;
+  GPUd() value_t getE2() const;
+  GPUd() value_t getE() const;
+  GPUdi() static value_t getdEdxBB(value_t betagamma) { return BetheBlochSolid(betagamma); }
+  GPUdi() static value_t getdEdxBBOpt(value_t betagamma) { return BetheBlochSolidOpt(betagamma); }
+  GPUdi() static value_t getBetheBlochSolidDerivativeApprox(value_T dedx, value_T bg) { return BetheBlochSolidDerivative(dedx, bg); }
 
   GPUd() value_t getTheta() const;
   GPUd() value_t getEta() const;
   GPUd() math_utils::Point3D getXYZGlo() const;
   GPUd() void getXYZGlo(dim3_t& xyz) const;
   GPUd() bool getPxPyPzGlo(dim3_t& pxyz) const;
-  GPUd() bool getPosDirGlo(gpu::gpustd::array& posdirp) const;
+  GPUd() bool getPosDirGlo(std::array& posdirp) const;
 
   // methods for track params estimate at other point
   GPUd() bool getYZAt(value_t xk, value_t b, value_t& y, value_t& z) const;
@@ -205,11 +220,16 @@ class TrackParametrization
   GPUd() value_t getYAt(value_t xk, value_t b) const;
   GPUd() value_t getSnpAt(value_t xk, value_t b) const;
   GPUd() value_t getSnpAt(value_t alpha, value_t xk, value_t b) const;
+  GPUd() value_t getPhiAt(value_t xk, value_t b) const;
+  GPUd() value_t getPhiPosAt(value_t xk, value_t b) const;
+  GPUd() value_t getDCAYtoMV(value_t b, value_t xmv = 0.f, value_t ymv = 0.f, value_t zmv = 0.f) const;
+  GPUd() value_t getDCAZtoMV(value_t b, value_t xmv = 0.f, value_t ymv = 0.f, value_t zmv = 0.f) const;
   GPUd() math_utils::Point3D getXYZGloAt(value_t xk, value_t b, bool& ok) const;
 
   // parameters manipulation
-  GPUd() bool correctForELoss(value_t xrho, bool anglecorr = false, value_t dedx = kCalcdEdxAuto);
+  GPUd() bool correctForELoss(value_t xrho, bool anglecorr = false);
   GPUd() bool rotateParam(value_t alpha);
+  GPUd() bool rotateParam(value_t& alpha, value_t& ca, value_t& sa);
   GPUd() bool propagateParamTo(value_t xk, value_t b);
   GPUd() bool propagateParamTo(value_t xk, const dim3_t& b);
   GPUd() void invertParam();
@@ -228,8 +248,12 @@ class TrackParametrization
   GPUhd() void setUserField(uint16_t v);
 
   GPUd() void printParam() const;
+  GPUd() void printParamHexadecimal();
 #ifndef GPUCA_ALIGPUCODE
   std::string asString() const;
+  std::string asStringHexadecimal();
+  size_t hash() const { return hash(getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt()); }
+  static size_t hash(float x, float alp, float y, float z, float snp, float tgl, float q2pt);
 #endif
 
   GPUd() void updateParam(value_t delta, int i);
@@ -257,6 +281,7 @@ GPUdi() TrackParametrization::TrackParametrization(value_t x, value_t a
   : mX{x}, mAlpha{alpha}, mAbsCharge{char(gpu::CAMath::Abs(charge))}, mPID{pid}
 {
   // explicit constructor
+  math_utils::detail::bringToPMPi(mAlpha);
   for (int i = 0; i < kNParams; i++) {
     mP[i] = par[i];
   }
@@ -275,6 +300,7 @@ GPUdi() void TrackParametrization::set(value_t x, value_t alpha, const
 {
   mX = x;
   mAlpha = alpha;
+  math_utils::detail::bringToPMPi(mAlpha);
   mAbsCharge = char(gpu::CAMath::Abs(charge));
   for (int i = 0; i < kNParams; i++) {
     mP[i] = par[i];
@@ -340,7 +366,7 @@ GPUdi() auto TrackParametrization::getTgl() const -> value_t
 
 //____________________________________________________________
 template 
-GPUdi() auto TrackParametrization::getQ2Pt() const -> value_t
+GPUhdi() auto TrackParametrization::getQ2Pt() const -> value_t
 {
   return mP[kQ2Pt];
 }
@@ -368,10 +394,12 @@ GPUdi() PID TrackParametrization::getPID() const
 
 //____________________________________________________________
 template 
-GPUdi() void TrackParametrization::setPID(const PID pid)
+GPUdi() void TrackParametrization::setPID(const PID pid, bool passCharge)
 {
   mPID = pid;
-  //  setAbsCharge(pid.getCharge()); // If needed, user should change the charge via corr. setter
+  if (passCharge) {
+    setAbsCharge(pid.getCharge()); // If needed, user should change the charge via corr. setter
+  }
 }
 
 //____________________________________________________________
@@ -408,6 +436,7 @@ template 
 GPUdi() void TrackParametrization::setAlpha(value_t v)
 {
   mAlpha = v;
+  math_utils::detail::bringToPMPi(mAlpha);
 }
 
 //____________________________________________________________
@@ -536,12 +565,27 @@ GPUdi() auto TrackParametrization::getPhiPos() const -> value_t
   return phi;
 }
 
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getQ2P2() const -> value_t
+{
+  // return the (q/p)^2
+  value_t q2pt2 = mP[kQ2Pt] * mP[kQ2Pt];
+  if (q2pt2 < MinPTInv * MinPTInv) {
+    q2pt2 = MinPTInv * MinPTInv;
+  }
+  return q2pt2 / (1.f + getTgl() * getTgl());
+}
+
 //____________________________________________________________
 template 
 GPUdi() auto TrackParametrization::getPtInv() const -> value_t
 {
   // return the inverted track pT
-  const value_t ptInv = gpu::CAMath::Abs(mP[kQ2Pt]);
+  value_t ptInv = gpu::CAMath::Abs(mP[kQ2Pt]);
+  if (ptInv < MinPTInv) {
+    ptInv = MinPTInv;
+  }
   return (mAbsCharge > 1) ? ptInv / mAbsCharge : ptInv;
 }
 
@@ -550,8 +594,8 @@ template 
 GPUdi() auto TrackParametrization::getP2Inv() const -> value_t
 {
   // return the inverted track momentum^2
-  const value_t p2 = mP[kQ2Pt] * mP[kQ2Pt] / (1.f + getTgl() * getTgl());
-  return (mAbsCharge > 1) ? p2 / (mAbsCharge * mAbsCharge) : p2;
+  value_t p2 = getPtInv();
+  return p2 * p2 / (1.f + getTgl() * getTgl());
 }
 
 //____________________________________________________________
@@ -559,17 +603,15 @@ template 
 GPUdi() auto TrackParametrization::getP2() const -> value_t
 {
   // return the track momentum^2
-  const value_t p2inv = getP2Inv();
-  return (p2inv > o2::constants::math::Almost0) ? 1.f / p2inv : o2::constants::math::VeryBig;
+  return 1.f / getP2Inv(); // getP2Inv is protected against being 0, full charge accounted
 }
 
 //____________________________________________________________
 template 
 GPUdi() auto TrackParametrization::getPInv() const -> value_t
 {
-  // return the inverted track momentum^2
-  const value_t pInv = gpu::CAMath::Abs(mP[kQ2Pt]) / gpu::CAMath::Sqrt(1.f + getTgl() * getTgl());
-  return (mAbsCharge > 1) ? pInv / mAbsCharge : pInv;
+  // return the inverted track momentum
+  return getPtInv() / gpu::CAMath::Sqrt(1.f + getTgl() * getTgl()); // getPtInv() is protected against being 0, full charge accounted
 }
 
 //____________________________________________________________
@@ -577,8 +619,23 @@ template 
 GPUdi() auto TrackParametrization::getP() const -> value_t
 {
   // return the track momentum
-  const value_t pInv = getPInv();
-  return (pInv > o2::constants::math::Almost0) ? 1.f / pInv : o2::constants::math::VeryBig;
+  return 1.f / getPInv(); // getPInv is already protected against being 0
+}
+
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getE2() const -> value_t
+{
+  // return the track energy^2
+  return getP2() + getPID().getMass2();
+}
+
+//____________________________________________________________
+template 
+GPUdi() auto TrackParametrization::getE() const -> value_t
+{
+  // return the track energy
+  return gpu::CAMath::Sqrt(getE2());
 }
 
 //____________________________________________________________
@@ -586,11 +643,7 @@ template 
 GPUdi() auto TrackParametrization::getPt() const -> value_t
 {
   // return the track transverse momentum
-  value_t ptI = gpu::CAMath::Abs(mP[kQ2Pt]);
-  if (mAbsCharge > 1) {
-    ptI /= mAbsCharge;
-  }
-  return (ptI > o2::constants::math::Almost0) ? 1.f / ptI : o2::constants::math::VeryBig;
+  return 1.f / getPtInv(); // getPtInv is already protected against being 0
 }
 
 //____________________________________________________________
@@ -616,7 +669,7 @@ GPUdi() auto TrackParametrization::getXYZGlo() const -> math_utils::Poi
 #else // mockup on GPU without ROOT
   float sina, cosa;
   gpu::CAMath::SinCos(getAlpha(), sina, cosa);
-  return math_utils::Point3D(cosa * getX() + sina * getY(), cosa * getY() - sina * getX(), getZ());
+  return math_utils::Point3D(cosa * getX() - sina * getY(), cosa * getY() + sina * getX(), getZ());
 #endif
 }
 
@@ -646,7 +699,7 @@ GPUdi() auto TrackParametrization::getXYZGloAt(value_t xk, value_t b, b
 #else // mockup on GPU without ROOT
     float sina, cosa;
     gpu::CAMath::SinCos(getAlpha(), sina, cosa);
-    return math_utils::Point3D(cosa * xk + sina * y, cosa * y - sina * xk, z);
+    return math_utils::Point3D(cosa * xk - sina * y, cosa * y + sina * xk, z);
 #endif
   } else {
     return math_utils::Point3D();
@@ -708,6 +761,21 @@ GPUdi() void TrackParametrization::updateParams(const value_t* delta)
   }
 }
 
+#ifndef GPUCA_ALIGPUCODE
+template 
+size_t TrackParametrization::hash(float x, float alp, float y, float z, float snp, float tgl, float q2pt)
+{
+  size_t h = std::hash{}(o2::math_utils::detail::truncateFloatFraction(x, 0xFFFFFFF0));
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(alp, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(y, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(z, 0xFFFFFFF0)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(snp, 0xFFFFFF00)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(tgl, 0xFFFFFF00)) << 1;
+  h ^= std::hash{}(o2::math_utils::detail::truncateFloatFraction(q2pt, 0xFFFFFC00)) << 1;
+  return h;
+}
+#endif
+
 } // namespace track
 } // namespace o2
 
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
index bfda22eef334b..0fc01e6db61a2 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h
@@ -18,6 +18,7 @@
 #define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKPARAMETRIZATIONWITHERROR_H_
 
 #include "ReconstructionDataFormats/TrackParametrization.h"
+#include 
 
 namespace o2
 {
@@ -37,26 +38,26 @@ class TrackParametrizationWithError : public TrackParametrization
   static_assert(std::is_floating_point_v);
 #endif
 
-  using covMat_t = gpu::gpustd::array;
-  using MatrixDSym5 = ROOT::Math::SMatrix>;
-  using MatrixD5 = ROOT::Math::SMatrix>;
+  using covMat_t = std::array;
+  using MatrixDSym5 = o2::math_utils::SMatrix>;
+  using MatrixD5 = o2::math_utils::SMatrix>;
 
-  GPUd() TrackParametrizationWithError();
+  GPUhd() TrackParametrizationWithError();
   GPUd() TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1, const PID pid = PID::Pion);
   GPUd() TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz,
-                                       const gpu::gpustd::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
+                                       const std::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
 
-  GPUdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default;
+  GPUhdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default;
   GPUdDefault() TrackParametrizationWithError(TrackParametrizationWithError&& src) = default;
-  GPUdDefault() TrackParametrizationWithError& operator=(const TrackParametrizationWithError& src) = default;
-  GPUdDefault() TrackParametrizationWithError& operator=(TrackParametrizationWithError&& src) = default;
+  GPUhdDefault() TrackParametrizationWithError& operator=(const TrackParametrizationWithError& src) = default;
+  GPUhdDefault() TrackParametrizationWithError& operator=(TrackParametrizationWithError&& src) = default;
   GPUdDefault() ~TrackParametrizationWithError() = default;
   using TrackParametrization::TrackParametrization;
 
   using TrackParametrization::set;
   GPUd() void set(value_t x, value_t alpha, const params_t& par, const covMat_t& cov, int charge = 1, const PID pid = PID::Pion);
   GPUd() void set(value_t x, value_t alpha, const value_t* par, const value_t* cov, int charge = 1, const PID pid = PID::Pion);
-  GPUd() void set(const dim3_t& xyz, const dim3_t& pxpypz, const gpu::gpustd::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
+  GPUd() void set(const dim3_t& xyz, const dim3_t& pxpypz, const std::array& cv, int sign, bool sectorAlpha = true, const PID pid = PID::Pion);
   GPUd() const covMat_t& getCov() const;
   GPUd() value_t getSigmaY2() const;
   GPUd() value_t getSigmaZY() const;
@@ -76,31 +77,43 @@ class TrackParametrizationWithError : public TrackParametrization
   GPUd() value_t getCovarElem(int i, int j) const;
   GPUd() value_t getDiagError2(int i) const;
 
-  GPUd() bool getCovXYZPxPyPzGlo(gpu::gpustd::array& c) const;
+  GPUd() bool getCovXYZPxPyPzGlo(std::array& c) const;
 
   GPUd() void print() const;
+  GPUd() void printHexadecimal();
 #ifndef GPUCA_ALIGPUCODE
   std::string asString() const;
+  std::string asStringHexadecimal();
 #endif
 
   // parameters + covmat manipulation
+  GPUd() bool testRotate(value_t alpha) const;
   GPUd() bool rotate(value_t alpha);
-  GPUd() bool propagateTo(value_t xk, value_t b);
+  GPUd() bool rotate(value_t alpha, TrackParametrization& linRef, value_t bz);
+  GPUd() bool propagateTo(value_t xk, value_t bz);
+  GPUd() bool propagateTo(value_t xk, TrackParametrization& linRef, value_t bz);
+  GPUd() bool propagateTo(value_t xk, value_t bz, TrackParametrization* linRef) { return linRef ? propagateTo(xk, *linRef, bz) : propagateTo(xk, bz); }
   GPUd() bool propagateTo(value_t xk, const dim3_t& b);
-  GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t b, o2::dataformats::DCA* dca = nullptr, value_t maxD = 999.f);
+  GPUd() bool propagateTo(value_t xk, TrackParametrization& linRef, const dim3_t& b);
+  GPUd() bool propagateTo(value_t xk, const dim3_t& b, TrackParametrization* linRef) { return linRef ? propagateTo(xk, *linRef, b) : propagateTo(xk, b); }
+  GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t bz, o2::dataformats::DCA* dca = nullptr, value_t maxD = 999.f);
   GPUd() void invert();
-
   GPUd() value_t getPredictedChi2(const dim2_t& p, const dim3_t& cov) const;
+  GPUd() value_t getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const;
   GPUd() value_t getPredictedChi2(const value_t* p, const value_t* cov) const;
+  GPUd() value_t getPredictedChi2Quiet(const value_t* p, const value_t* cov) const;
 
   template 
   GPUd() value_t getPredictedChi2(const BaseCluster& p) const;
+  template 
+  GPUd() value_t getPredictedChi2Quiet(const BaseCluster& p) const;
 
-  void buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const;
-  value_t getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const;
-  value_t getPredictedChi2(const TrackParametrizationWithError& rhs) const;
-  bool update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv);
-  bool update(const TrackParametrizationWithError& rhs);
+  GPUd() void buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const;
+  GPUd() value_t getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const;
+  GPUd() value_t getPredictedChi2(const TrackParametrizationWithError& rhs) const;
+  GPUd() value_t getPredictedChi2Quiet(const TrackParametrizationWithError& rhs) const;
+  GPUd() bool update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv);
+  GPUd() bool update(const TrackParametrizationWithError& rhs);
 
   GPUd() bool update(const dim2_t& p, const dim3_t& cov);
   GPUd() bool update(const value_t* p, const value_t* cov);
@@ -109,8 +122,8 @@ class TrackParametrizationWithError : public TrackParametrization
   template 
   GPUd() bool update(const BaseCluster& p);
 
-  GPUd() bool correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr = false, value_t dedx = kCalcdEdxAuto);
-
+  GPUd() bool correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr = false);
+  GPUd() bool correctForMaterial(TrackParametrization& linRef, value_t x2x0, value_t xrho, bool anglecorr = false);
   GPUd() void resetCovariance(value_t s2 = 0);
   GPUd() void checkCovariance();
   GPUd() void checkCorrelations();
@@ -139,7 +152,7 @@ class TrackParametrizationWithError : public TrackParametrization
 
 //__________________________________________________________________________
 template 
-GPUdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{}
+GPUhdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{}
 {
 }
 
@@ -147,7 +160,8 @@ GPUdi() TrackParametrizationWithError::TrackParametrizationWithError()
 template 
 GPUdi() TrackParametrizationWithError::TrackParametrizationWithError(value_t x, value_t alpha, const params_t& par,
                                                                               const covMat_t& cov, int charge, const PID pid)
-  : TrackParametrization{x, alpha, par, charge, pid}
+  : TrackParametrization{
+      x, alpha, par, charge, pid}
 {
   // explicit constructor
   for (int i = 0; i < kCovMatSize; i++) {
@@ -303,11 +317,21 @@ template 
 template 
 GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const BaseCluster& p) const -> value_t
 {
-  const dim2_t pyz = {p.getY(), p.getZ()};
-  const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()};
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
   return getPredictedChi2(pyz, cov);
 }
 
+//__________________________________________________________________________
+template 
+template 
+GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const BaseCluster& p) const -> value_t
+{
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
+  return getPredictedChi2Quiet(pyz, cov);
+}
+
 //______________________________________________
 template 
 GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2_t& p, const dim3_t& cov) const -> value_t
@@ -315,6 +339,13 @@ GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2
   return getPredictedChi2(p.data(), cov.data());
 }
 
+//______________________________________________
+template 
+GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const -> value_t
+{
+  return getPredictedChi2Quiet(p.data(), cov.data());
+}
+
 //______________________________________________
 template 
 GPUdi() bool TrackParametrizationWithError::update(const dim2_t& p, const dim3_t& cov)
@@ -327,8 +358,8 @@ template 
 template 
 GPUdi() bool TrackParametrizationWithError::update(const BaseCluster& p)
 {
-  const dim2_t pyz = {p.getY(), p.getZ()};
-  const dim3_t cov = {p.getSigmaY2(), p.getSigmaYZ(), p.getSigmaZ2()};
+  const dim2_t pyz = {value_T(p.getY()), value_T(p.getZ())};
+  const dim3_t cov = {value_T(p.getSigmaY2()), value_T(p.getSigmaYZ()), value_T(p.getSigmaZ2())};
   return update(pyz, cov);
 }
 
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
index 8e8dd9033bdf8..8a79130d64eda 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h
@@ -18,9 +18,9 @@
 #define INCLUDE_RECONSTRUCTIONDATAFORMATS_TRACKUTILS_H_
 
 #include "GPUCommonRtypes.h"
-#include "GPUCommonArray.h"
 
 #ifndef GPUCA_GPUCODE_DEVICE
+#include 
 #include 
 #endif
 
@@ -33,14 +33,17 @@ namespace track
 {
 // helper function
 template 
-GPUd() value_T BetheBlochSolid(value_T bg, value_T rho = 2.33, value_T kp1 = 0.20, value_T kp2 = 3.00, value_T meanI = 173e-9,
-                               value_T meanZA = 0.49848);
+GPUd() value_T BetheBlochSolid(value_T bg, value_T rho = 2.33, value_T kp1 = 0.20, value_T kp2 = 3.00, value_T meanI = 173e-9, value_T meanZA = 0.49848);
+
+template 
+GPUd() value_T BetheBlochSolidOpt(value_T bg);
+
 template 
-GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array& vect);
+GPUd() void g3helx3(value_T qfield, value_T step, std::array& vect);
 
 //____________________________________________________
 template 
-GPUd() void g3helx3(value_T qfield, value_T step, gpu::gpustd::array& vect)
+GPUd() void g3helx3(value_T qfield, value_T step, std::array& vect)
 {
   /******************************************************************
    *                                                                *
@@ -121,11 +124,11 @@ GPUd() value_T BetheBlochSolid(value_T bg, value_T rho, value_T kp1, value_T kp2
   static_assert(std::is_floating_point_v);
 #endif
 
-  constexpr value_T mK = 0.307075e-3f; // [GeV*cm^2/g]
-  constexpr value_T me = 0.511e-3f;    // [GeV/c^2]
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T me = 0.511e-3;    // [GeV/c^2]
   kp1 *= 2.303f;
   kp2 *= 2.303f;
-  value_T bg2 = bg * bg;
+  value_T bg2 = bg * bg, beta2 = bg2 / (1 + bg2);
   value_T maxT = 2.f * me * bg2; // neglecting the electron mass
 
   //*** Density effect
@@ -138,7 +141,75 @@ GPUd() value_T BetheBlochSolid(value_T bg, value_T rho, value_T kp1, value_T kp2
     double r = (kp2 - x) / (kp2 - kp1);
     d2 = lhwI + x - 0.5f + (0.5f - lhwI - kp1) * r * r * r;
   }
-  return mK * meanZA * (1 + bg2) / bg2 * (0.5f * gpu::CAMath::Log(2 * me * bg2 * maxT / (meanI * meanI)) - bg2 / (1 + bg2) - d2);
+  auto dedx = mK * meanZA / beta2 * (0.5f * gpu::CAMath::Log(2 * me * bg2 * maxT / (meanI * meanI)) - beta2 - d2);
+  return dedx > 0. ? dedx : 0.;
+}
+
+//____________________________________________________
+template 
+GPUd() value_T BetheBlochSolidOpt(value_T bg)
+{
+  //
+  // This is the parameterization of the Bethe-Bloch formula inspired by Geant with hardcoded constants and better optimization
+  //
+  // bg  - beta*gamma
+  // rho - density [g/cm^3]
+  // kp1 - density effect first junction point
+  // kp2 - density effect second junction point
+  // meanI - mean excitation energy [GeV]
+  // meanZA - mean Z/A
+  //
+  // The default values for the kp* parameters are for silicon.
+  // The returned value is in [GeV/(g/cm^2)].
+  //
+  //  constexpr value_T rho = 2.33;
+  //  constexpr value_T meanI = 173e-9;
+  //  constexpr value_T me = 0.511e-3;    // [GeV/c^2]
+
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T kp1 = 0.20 * 2.303;
+  constexpr value_T kp2 = 3.00 * 2.303;
+  constexpr value_T meanZA = 0.49848;
+  constexpr value_T lhwI = -1.7175226;         // gpu::CAMath::Log(28.816 * 1e-9 * gpu::CAMath::Sqrt(rho * meanZA) / meanI);
+  constexpr value_T log2muTomeanI = 8.6839805; // gpu::CAMath::Log( 2. * me / meanI);
+
+  value_T bg2 = bg * bg, beta2 = bg2 / (1. + bg2);
+
+  //*** Density effect
+  value_T d2 = 0.;
+  const value_T x = gpu::CAMath::Log(bg);
+  if (x > kp2) {
+    d2 = lhwI - 0.5f + x;
+  } else if (x > kp1) {
+    value_T r = (kp2 - x) / (kp2 - kp1);
+    d2 = lhwI - 0.5 + x + (0.5 - lhwI - kp1) * r * r * r;
+  }
+  auto dedx = mK * meanZA / beta2 * (log2muTomeanI + x + x - beta2 - d2);
+  return dedx > 0. ? dedx : 0.;
+}
+
+//____________________________________________________
+template 
+GPUdi() value_T BetheBlochSolidDerivative(value_T dedx, value_T bg)
+{
+  //
+  // This is approximate derivative of the BB over betagamm, NO check for the consistency of the provided dedx and bg is done
+  // Charge 1 particle is assumed for the provied dedx. For charge > 1 particles dedx/q^2 should be provided and obtained value must be scaled by q^2
+  // The call should be usually done as
+  // auto dedx = BetheBlochSolidOpt(bg);
+  // // if derivative needed
+  // auto ddedx = BetheBlochSolidDerivative(dedx, bg, bg*bg)
+  //
+  // dedx - precalculate dedx for bg
+  // bg  - beta*gamma
+  //
+  constexpr value_T mK = 0.307075e-3; // [GeV*cm^2/g]
+  constexpr value_T meanZA = 0.49848;
+  auto bg2 = bg * bg;
+  auto t1 = 1 + bg2;
+  //  auto derH = (mK * meanZA * (t1+bg2) - dedx*bg2)/(bg*t1);
+  auto derH = (mK * meanZA * (t1 + 1. / bg2) - dedx) / (bg * t1);
+  return derH + derH;
 }
 
 } // namespace track
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
index 9686a7af98056..a96ba1dd1e5a0 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/V0.h
@@ -15,6 +15,7 @@
 #include "ReconstructionDataFormats/VtxTrackIndex.h"
 #include "ReconstructionDataFormats/Track.h"
 #include "ReconstructionDataFormats/PID.h"
+#include "ReconstructionDataFormats/DecayNBodyIndex.h" // RS Remove after dropping indices in O2Physics
 #include 
 #include 
 
@@ -31,12 +32,9 @@ class V0 : public o2::track::TrackParCov
   using PID = o2::track::PID;
 
   V0() = default;
-  V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-     const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg,
-     GIndex trPosID, GIndex trNegID, o2::track::PID pid = o2::track::PID::K0);
 
-  GIndex getProngID(int i) const { return mProngIDs[i]; }
-  void setProngID(int i, GIndex gid) { mProngIDs[i] = gid; }
+  V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
+     const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, o2::track::PID pid = o2::track::PID::K0);
 
   const Track& getProng(int i) const { return mProngs[i]; }
   Track& getProng(int i) { return mProngs[i]; }
@@ -48,23 +46,28 @@ class V0 : public o2::track::TrackParCov
   float getDCA() const { return mDCA; }
   void setDCA(float d) { mDCA = d; }
 
-  int getVertexID() const { return mVertexID; }
-  void setVertexID(int id) { mVertexID = id; }
-
-  float calcMass2() const { return calcMass2(mProngs[0].getPID(), mProngs[1].getPID()); }
-  float calcMass2(PID pidPos, PID pidNeg) const { return calcMass2(pidPos.getMass2(), pidNeg.getMass2()); }
+  float calcMass2() const { return calcMass2PID(mProngs[0].getPID(), mProngs[1].getPID()); }
+  float calcMass2PID(PID pidPos, PID pidNeg) const { return calcMass2(PID::getMass2(pidPos), PID::getMass2(pidNeg)); }
   float calcMass2(float massPos2, float massNeg2) const;
 
+  float calcMass2AsPhoton() const { return calcMass2PID(PID::Electron, PID::Electron); }
+  float calcMass2AsK0() const { return calcMass2PID(PID::Pion, PID::Pion); }
+  float calcMass2AsLambda() const { return calcMass2PID(PID::Proton, PID::Pion); }
+  float calcMass2AsAntiLambda() const { return calcMass2PID(PID::Pion, PID::Proton); }
+  float calcMass2AsHyperTriton() const { return calcMass2PID(PID::Helium3, PID::Pion); }
+  float calcMass2AsAntiHyperTriton() const { return calcMass2PID(PID::Pion, PID::Helium3); }
+  float calcMass2AsHyperhydrog4() const { return calcMass2PID(PID::Alpha, PID::Pion); }
+  float calcMass2AsAntiHyperhydrog4() const { return calcMass2PID(PID::Pion, PID::Alpha); }
+  float calcAPQt() const;
+  float calcAPAlpha() const;
   float calcR2() const { return getX() * getX() + getY() * getY(); }
 
  protected:
-  std::array mProngIDs; // global IDs of prongs
   std::array mProngs;    // prongs kinematics at vertex
   float mCosPA = 0;                // cos of pointing angle
   float mDCA = 9990;               // distance of closest approach of prongs
-  int mVertexID = -1;              // id of parent vertex
 
-  ClassDefNV(V0, 1);
+  ClassDefNV(V0, 2);
 };
 
 } // namespace dataformats
diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
index fd7ea85b7bbbe..cb1c9d5d87c7f 100644
--- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
+++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h
@@ -14,14 +14,17 @@
 
 #include "GPUCommonDef.h"
 #include "GPUCommonMath.h"
-#include "GPUCommonArray.h"
 #include 
 
 #include "CommonDataFormat/TimeStamp.h"
 #ifndef GPUCA_GPUCODE_DEVICE
-#include 
-#include 
 #include 
+#include 
+#ifndef GPUCA_NO_FMT
+#include 
+#include 
+#include 
+#endif
 #endif
 
 namespace o2
@@ -42,9 +45,17 @@ class VertexBase
   static constexpr int kNCov = 6;
   GPUhdDefault() VertexBase() = default;
   GPUhdDefault() ~VertexBase() = default;
-  GPUhd() VertexBase(const math_utils::Point3D& pos, const gpu::gpustd::array& cov) : mPos(pos), mCov(cov)
+  GPUhd() VertexBase(const float* pos, const float* cov)
   {
+    mPos = math_utils::Point3D(pos[0], pos[1], pos[2]);
+    mCov[kCovXX] = cov[kCovXX];
+    mCov[kCovXY] = cov[kCovXY];
+    mCov[kCovXZ] = cov[kCovXZ];
+    mCov[kCovYY] = cov[kCovYY];
+    mCov[kCovYZ] = cov[kCovYZ];
+    mCov[kCovZZ] = cov[kCovZZ];
   }
+  GPUhd() VertexBase(const math_utils::Point3D& pos, const std::array& cov) : mPos(pos), mCov(cov) {}
 
 #if !defined(GPUCA_NO_FMT) && !defined(GPUCA_GPUCODE_DEVICE)
   void print() const;
@@ -55,6 +66,7 @@ class VertexBase
   GPUhd() float getX() const { return mPos.X(); }
   GPUhd() float getY() const { return mPos.Y(); }
   GPUhd() float getZ() const { return mPos.Z(); }
+  GPUhd() float getR() const { return gpu::CAMath::Hypot(mPos.X(), mPos.Y()); }
   GPUd() float getSigmaX2() const { return mCov[kCovXX]; }
   GPUd() float getSigmaY2() const { return mCov[kCovYY]; }
   GPUd() float getSigmaZ2() const { return mCov[kCovZZ]; }
@@ -65,7 +77,8 @@ class VertexBase
   GPUd() float getSigmaY() const { return gpu::CAMath::Sqrt(getSigmaY2()); }
   GPUd() float getSigmaZ() const { return gpu::CAMath::Sqrt(getSigmaZ2()); }
 
-  GPUd() const gpu::gpustd::array& getCov() const { return mCov; }
+  GPUd() const std::array& getCov() const { return mCov; }
+  GPUd() float getCov(int e) const { return mCov[e]; }
 
   GPUd() math_utils::Point3D getXYZ() const { return mPos; }
   GPUd() math_utils::Point3D& getXYZ() { return mPos; }
@@ -101,14 +114,15 @@ class VertexBase
     setSigmaXZ(sxz);
     setSigmaYZ(syz);
   }
-  GPUd() void setCov(const gpu::gpustd::array& cov) { mCov = cov; }
+  GPUd() void setCov(const std::array& cov) { mCov = cov; }
+  GPUd() void setCov(float c, int e) { mCov[e] = c; }
 
   bool operator==(const VertexBase& other) const;
   bool operator!=(const VertexBase& other) const { return !(*this == other); }
 
  protected:
   math_utils::Point3D mPos{0., 0., 0.}; ///< cartesian position
-  gpu::gpustd::array mCov{};     ///< errors, see CovElems enum
+  std::array mCov{};             ///< errors, see CovElems enum
 
   ClassDefNV(VertexBase, 1);
 };
@@ -124,15 +138,19 @@ class Vertex : public VertexBase
   using ushort = unsigned short;
   enum Flags : ushort {
     TimeValidated = 0x1 << 0, // Flag that the vertex was validated by external time measurement (e.g. FIT)
+    UPCMode = 0x1 << 1,       // vertex is found in the UPC mode ITS ROF
     FlagsMask = 0xffff
   };
 
   GPUhdDefault() Vertex() = default;
   GPUhdDefault() ~Vertex() = default;
-  GPUhd() Vertex(const math_utils::Point3D& pos, const gpu::gpustd::array& cov, ushort nCont, float chi2)
-    : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont)
-  {
-  }
+  GPUhd() Vertex(const float* pos, const float* cov, ushort nCont, float chi2) : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont) {}
+  GPUhd() Vertex(const math_utils::Point3D& pos, const std::array& cov, ushort nCont, float chi2) : VertexBase(pos, cov), mChi2(chi2), mNContributors(nCont) {}
+
+#if !defined(GPUCA_NO_FMT) && !defined(GPUCA_GPUCODE_DEVICE)
+  void print() const;
+  std::string asString() const;
+#endif
 
   GPUd() ushort getNContributors() const { return mNContributors; }
   GPUd() void setNContributors(ushort v) { mNContributors = v; }
@@ -141,13 +159,13 @@ class Vertex : public VertexBase
   GPUd() ushort getFlags() const { return mBits; }
   GPUd() bool isFlagSet(uint f) const { return mBits & (FlagsMask & f); }
   GPUd() void setFlags(ushort f) { mBits |= FlagsMask & f; }
-  GPUd() void resetFrags(ushort f = FlagsMask) { mBits &= ~(FlagsMask & f); }
+  GPUd() void resetFlags(ushort f = FlagsMask) { mBits &= ~(FlagsMask & f); }
 
   GPUd() void setChi2(float v) { mChi2 = v; }
   GPUd() float getChi2() const { return mChi2; }
 
-  GPUd() const Stamp& getTimeStamp() const { return mTimeStamp; }
-  GPUd() Stamp& getTimeStamp() { return mTimeStamp; }
+  GPUhd() const Stamp& getTimeStamp() const { return mTimeStamp; }
+  GPUhd() Stamp& getTimeStamp() { return mTimeStamp; }
   GPUd() void setTimeStamp(const Stamp& v) { mTimeStamp = v; }
 
  protected:
@@ -161,6 +179,49 @@ class Vertex : public VertexBase
 
 #if !defined(GPUCA_GPUCODE_DEVICE) && !defined(GPUCA_NO_FMT)
 std::ostream& operator<<(std::ostream& os, const o2::dataformats::VertexBase& v);
+
+namespace detail
+{
+template 
+concept Streamable = requires(std::ostream& os, const T& a) {
+  { os << a } -> std::same_as;
+};
+
+template 
+concept HasFormattableTimeStamp = requires(const T& t) {
+  { fmt::format("{}", t.getTimeStamp()) } -> std::convertible_to;
+};
+} // namespace detail
+
+template 
+inline std::string Vertex::asString() const
+{
+  const std::string stamp = [&]() -> std::string {
+    if constexpr (detail::Streamable) {
+      std::ostringstream oss;
+      oss << mTimeStamp;
+      return oss.str();
+    } else if constexpr (detail::HasFormattableTimeStamp) {
+      return fmt::format("{}", mTimeStamp.getTimeStamp());
+    } else {
+      return "X";
+    }
+  }();
+  return fmt::format("{} NContrib:{} Chi2:{:.2f} Flags:{:b} Stamp:{}", VertexBase::asString(), mNContributors, mChi2, mBits, stamp);
+}
+
+template 
+inline std::ostream& operator<<(std::ostream& os, const o2::dataformats::Vertex& v)
+{
+  os << v.asString();
+  return os;
+}
+
+template 
+inline void Vertex::print() const
+{
+  std::cout << *this << '\n';
+}
 #endif
 
 } // namespace dataformats
@@ -178,9 +239,6 @@ template <>
 struct is_messageable>> : std::true_type {
 };
 template <>
-struct is_messageable>> : std::true_type {
-};
-template <>
 struct is_messageable>> : std::true_type {
 };
 } // namespace framework
diff --git a/DataFormats/Reconstruction/src/Cascade.cxx b/DataFormats/Reconstruction/src/Cascade.cxx
index dbc2c4cbae011..f5d7a017a89f8 100644
--- a/DataFormats/Reconstruction/src/Cascade.cxx
+++ b/DataFormats/Reconstruction/src/Cascade.cxx
@@ -12,23 +12,21 @@
 #include "ReconstructionDataFormats/Cascade.h"
 
 using namespace o2::dataformats;
-
+/*
 Cascade::Cascade(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-                 const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor,
-                 int v0ID, GIndex bachelorID, o2::track::PID pid)
+                 const o2::track::TrackParCov& v0, const o2::track::TrackParCov& bachelor, o2::track::PID pid) : mProngs{v0, bachelor}
 {
-  std::array covV{}, covB{};
+  std::array covC{0.}, covV{}, covB{};
   v0.getCovXYZPxPyPzGlo(covV);
   bachelor.getCovXYZPxPyPzGlo(covB);
-  for (int i = 0; i < 21; i++) {
-    covV[i] += covB[i];
-  }
+  constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
   for (int i = 0; i < 6; i++) {
-    covV[i] = covxyz[i];
+    covC[i] = covxyz[i];
+    covC[MomInd[i]] = covV[MomInd[i]] + covB[MomInd[i]];
   }
-  this->set(xyz, pxyz, covV, v0.getCharge() + bachelor.getCharge(), true, pid);
-  setV0ID(v0ID);
-  setBachelorID(bachelorID);
+  this->set(xyz, pxyz, covC, v0.getCharge() + bachelor.getCharge(), true, pid);
+  this->checkCorrelations();
   setV0Track(v0);
   setBachelorTrack(bachelor);
 }
+*/
diff --git a/DataFormats/Reconstruction/src/Decay3Body.cxx b/DataFormats/Reconstruction/src/Decay3Body.cxx
new file mode 100644
index 0000000000000..aa071cea675cd
--- /dev/null
+++ b/DataFormats/Reconstruction/src/Decay3Body.cxx
@@ -0,0 +1,37 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/Decay3Body.h"
+
+using namespace o2::dataformats;
+
+Decay3Body::Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid)
+  : mProngs{tr0, tr1, tr2}
+{
+  std::array cov{}, cov1{}, cov2{};
+  tr0.getCovXYZPxPyPzGlo(cov);
+  tr1.getCovXYZPxPyPzGlo(cov1);
+  tr2.getCovXYZPxPyPzGlo(cov2);
+  for (int i = 0; i < 21; i++) {
+    cov[i] += cov1[i] + cov2[i];
+  }
+  for (int i = 0; i < 6; i++) {
+    cov[i] = covxyz[i];
+  }
+  this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid);
+}
+
+float Decay3Body::calcMass2(float mass0, float mass1, float mass2) const
+{
+  auto p2 = getP2();
+  auto energy = std::sqrt(mass0 + mProngs[0].getP2()) + std::sqrt(mass1 + mProngs[1].getP2()) + std::sqrt(mass1 + mProngs[2].getP2());
+  return energy * energy - p2;
+}
diff --git a/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx b/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx
new file mode 100644
index 0000000000000..bd012091aad3f
--- /dev/null
+++ b/DataFormats/Reconstruction/src/DecayNBodyIndex.cxx
@@ -0,0 +1,18 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/DecayNBodyIndex.h"
+
+namespace o2::dataformats
+{
+template class DecayNBodyIndex<2>;
+template class DecayNBodyIndex<3>;
+} // namespace o2::dataformats
diff --git a/DataFormats/Reconstruction/src/DecayNbody.cxx b/DataFormats/Reconstruction/src/DecayNbody.cxx
deleted file mode 100644
index 701c710e7ab46..0000000000000
--- a/DataFormats/Reconstruction/src/DecayNbody.cxx
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-#include "ReconstructionDataFormats/DecayNbody.h"
-
-using namespace o2::dataformats;
-
-DecayNbody::DecayNbody(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, GIndex trID0, GIndex trID1, GIndex trID2)
-  : mProngIDs{trID0, trID1, trID2}, mProngs{tr0, tr1, tr2}
-{
-  std::array cov{}, cov1{}, cov2{};
-  tr0.getCovXYZPxPyPzGlo(cov);
-  tr1.getCovXYZPxPyPzGlo(cov1);
-  tr2.getCovXYZPxPyPzGlo(cov2);
-  for (int i = 0; i < 21; i++) {
-    cov[i] += cov1[i] + cov2[i];
-  }
-  for (int i = 0; i < 6; i++) {
-    cov[i] = covxyz[i];
-  }
-  this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid);
-}
-
-float DecayNbody::calcMass2(float mass0, float mass1, float mass2) const
-{
-  auto p2 = getP2();
-  auto energy = std::sqrt(mass0 + mProngs[0].getP2()) + std::sqrt(mass1 + mProngs[1].getP2()) + std::sqrt(mass1 + mProngs[2].getP2());
-  return energy * energy - p2;
-}
diff --git a/DataFormats/Reconstruction/src/MatchInfoHMP.cxx b/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
index 1058d39752ed7..77983c4355584 100644
--- a/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
+++ b/DataFormats/Reconstruction/src/MatchInfoHMP.cxx
@@ -20,5 +20,6 @@ ClassImp(o2::dataformats::MatchInfoHMP);
 
 void MatchInfoHMP::print() const
 {
-  printf("Match of GlobalID %s and HMPID cl %d ", getTrackRef().asString().c_str(), getIdxHMPClus());
+  // printf("Match of GlobalID %s and HMPID cl %d ", getTrackRef().asString().c_str(), getIdxHMPClus());
+  printf("Match of GlobalID and HMPID cl");
 }
diff --git a/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx
new file mode 100644
index 0000000000000..6065f04a3bc1a
--- /dev/null
+++ b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx
@@ -0,0 +1,51 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/PrimaryVertexExt.h"
+#include 
+#include 
+#include "CommonUtils/StringUtils.h"
+
+namespace o2
+{
+namespace dataformats
+{
+
+#ifndef GPUCA_ALIGPUCODE
+using GTrackID = o2::dataformats::GlobalTrackID;
+
+std::string PrimaryVertexExt::asString() const
+{
+  auto str = o2::utils::Str::concat_string(PrimaryVertex::asString(), fmt::format("VtxID={} FT0A/C={}/{} FT0T={}", VtxID, FT0A, FT0C, FT0Time));
+  for (int i = 0; i < GTrackID::Source::NSources; i++) {
+    if (getNSrc(i) > 0) {
+      str += fmt::format(" {}={}", GTrackID::getSourceName(i), getNSrc(i));
+    }
+  }
+  return str;
+}
+
+std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v)
+{
+  // stream itself
+  os << v.asString();
+  return os;
+}
+
+void PrimaryVertexExt::print() const
+{
+  std::cout << *this << std::endl;
+}
+
+#endif
+
+} // namespace dataformats
+} // namespace o2
diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
index 4a7d426c62ea5..b386830d9872d 100644
--- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
+++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h
@@ -30,6 +30,7 @@
 #pragma link C++ class o2::track::TrackParFwd + ;
 #pragma link C++ class o2::track::PID + ;
 #pragma link C++ class o2::track::TrackLTIntegral + ;
+#pragma link C++ class std::vector < o2::track::TrackLTIntegral> + ;
 
 #pragma link C++ class o2::track::TrackParCovFwd + ;
 #pragma link C++ class std::vector < o2::track::TrackParCovFwd> + ;
@@ -46,6 +47,9 @@
 #pragma link C++ class o2::dataformats::TrackTPCTOF + ;
 #pragma link C++ class std::vector < o2::dataformats::TrackTPCTOF> + ;
 
+#pragma link C++ class o2::dataformats::TrackHMP + ;
+#pragma link C++ class std::vector < o2::dataformats::TrackHMP> + ;
+
 #pragma link C++ class o2::dataformats::TrackCosmics + ;
 #pragma link C++ class std::vector < o2::dataformats::TrackCosmics> + ;
 
@@ -70,10 +74,12 @@
 #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>> + ;
 #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>> + ;
 #pragma link C++ class o2::dataformats::PrimaryVertex + ;
+#pragma link C++ class o2::dataformats::PrimaryVertexExt + ;
 
 #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>>> + ;
 #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>>> + ;
 #pragma link C++ class std::vector < o2::dataformats::PrimaryVertex> + ;
+#pragma link C++ class std::vector < o2::dataformats::PrimaryVertexExt> + ;
 
 #pragma link C++ class o2::dataformats::GlobalTrackID + ;
 #pragma link C++ class std::vector < o2::dataformats::GlobalTrackID> + ;
@@ -87,16 +93,31 @@
 
 #pragma link C++ class o2::dataformats::DCA + ;
 
+#pragma link C++ class o2::dataformats::DecayNBodyIndex < 2> + ;
+#pragma link C++ class o2::dataformats::DecayNBodyIndex < 3> + ;
+#pragma link C++ class o2::dataformats::Decay3BodyIndex + ;
+#pragma link C++ class o2::dataformats::V0Index + ;
+#pragma link C++ class o2::dataformats::CascadeIndex + ;
+
+#pragma link C++ class std::vector < o2::dataformats::DecayNBodyIndex < 2>> + ;
+#pragma link C++ class std::vector < o2::dataformats::DecayNBodyIndex < 3>> + ;
+#pragma link C++ class std::vector < o2::dataformats::Decay3BodyIndex> + ;
+#pragma link C++ class std::vector < o2::dataformats::V0Index> + ;
+#pragma link C++ class std::vector < o2::dataformats::CascadeIndex> + ;
+
 #pragma link C++ class o2::dataformats::V0 + ;
 #pragma link C++ class std::vector < o2::dataformats::V0> + ;
 
 #pragma link C++ class o2::dataformats::Cascade + ;
 #pragma link C++ class std::vector < o2::dataformats::Cascade> + ;
 
-#pragma link C++ class o2::dataformats::DecayNbody + ;
-#pragma link C++ class std::vector < o2::dataformats::DecayNbody> + ;
+#pragma link C++ class o2::dataformats::Decay3Body + ;
+#pragma link C++ class std::vector < o2::dataformats::Decay3Body> + ;
 
 #pragma link C++ class o2::dataformats::StrangeTrack + ;
 #pragma link C++ class std::vector < o2::dataformats::StrangeTrack> + ;
 
+#pragma link C++ class o2::track::TrackAuxPar + ;
+#pragma link C++ class o2::track::CrossInfo + ;
+
 #endif
diff --git a/DataFormats/Reconstruction/src/TrackFwd.cxx b/DataFormats/Reconstruction/src/TrackFwd.cxx
index eed4eece9296c..dfe72c5b2ccc4 100644
--- a/DataFormats/Reconstruction/src/TrackFwd.cxx
+++ b/DataFormats/Reconstruction/src/TrackFwd.cxx
@@ -11,6 +11,7 @@
 
 #include "ReconstructionDataFormats/TrackFwd.h"
 #include "Math/MatrixFunctions.h"
+#include 
 
 namespace o2
 {
@@ -244,6 +245,12 @@ void TrackParCovFwd::propagateToZhelix(double zEnd, double zField)
 //__________________________________________________________________________
 void TrackParCovFwd::propagateToZ(double zEnd, double zField)
 {
+  // Security for zero B field
+  if (zField == 0.0) {
+    propagateToZlinear(zEnd);
+    return;
+  }
+
   // Extrapolate track parameters and covariances matrix to "zEnd"
   // Parameters: helix track model; Error propagation: Quadratic
 
@@ -450,5 +457,120 @@ bool TrackParCovFwd::propagateToVtxlinearWithMCS(double z, const std::array& cv) const
+{
+  //---------------------------------------------------------------------
+  // This function returns the global covariance matrix of the fwdtrack params
+  //
+  // Cov(x,x) ... :   cv[0]
+  // Cov(y,x) ... :   cv[1]  cv[2]
+  // Cov(z,x) ... :   cv[3]  cv[4]  cv[5]
+  // Cov(px,x)... :   cv[6]  cv[7]  cv[8]  cv[9]
+  // Cov(py,x)... :   cv[10] cv[11] cv[12] cv[13] cv[14]
+  // Cov(pz,x)... :   cv[15] cv[16] cv[17] cv[18] cv[19] cv[20]
+  //---------------------------------------------------------------------
+  auto pt = getPt();
+  auto cp = std::sqrt((1. - getSnp()) * (1. + getSnp()));
+  auto sp = getSnp();
+  auto tgl = getTgl();
+
+  auto px = pt * std::sqrt((1. - getSnp()) * (1. + getSnp()));
+  auto py = pt * getSnp();
+  auto pz = pt * getTgl();
+  auto q = getCharge();
+
+  cv[0] = mCovariances(0, 0);
+  cv[1] = mCovariances(1, 0);
+  cv[2] = mCovariances(1, 1);
+  cv[3] = 0;
+  cv[4] = 0;
+  cv[5] = 0;
+  cv[6] = -mCovariances(0, 2) * py - mCovariances(0, 4) * px * pt * q;
+  cv[7] = -mCovariances(1, 2) * py - mCovariances(1, 4) * px * pt * q;
+  cv[8] = 0;
+  cv[9] = 2 * mCovariances(2, 4) * px * py * q * pt + mCovariances(2, 2) * py * py + mCovariances(4, 4) * px * px * pt * pt;
+  cv[10] = mCovariances(0, 2) * px - mCovariances(0, 4) * py * pt * q;
+  cv[11] = mCovariances(1, 2) * px - mCovariances(1, 4) * py * pt * q;
+  cv[12] = 0;
+  cv[13] = mCovariances(2, 4) * (py * py - px * px) * q * pt - mCovariances(2, 2) * px * py + mCovariances(4, 4) * px * py * pt * pt;
+  cv[14] = -2 * mCovariances(2, 4) * px * py * q * pt + mCovariances(2, 2) * px * px + mCovariances(4, 4) * py * py * pt * pt;
+  cv[15] = mCovariances(0, 3) * pt - mCovariances(0, 4) * pt * pz * q;
+  cv[16] = mCovariances(1, 3) * pt - mCovariances(1, 4) * pt * pz * q;
+  cv[17] = 0;
+  cv[18] = -mCovariances(2, 3) * py * pt - mCovariances(3, 4) * px * q * pt * pt + mCovariances(2, 4) * py * pz * q * pt + mCovariances(4, 4) * px * pz * pt * pt;
+  cv[19] = mCovariances(2, 3) * px * pt - mCovariances(3, 4) * q * pt * pt * py - mCovariances(2, 4) * px * pz * q * pt + mCovariances(4, 4) * py * pz * pt * pt;
+  cv[20] = -2 * mCovariances(3, 4) * pz * q * pt * pt + mCovariances(3, 3) * pt * pt + mCovariances(4, 4) * pz * pz * pt * pt;
+
+  return true;
+}
+
+//________________________________________________________________
+
+void TrackParCovFwd::propagateToDCAhelix(double zField, const std::array& p, std::array& dca)
+{
+  // Computing DCA of fwd track w.r.t vertex in helix track model, using Newton-Raphson minimization
+
+  auto x0 = mParameters(0);
+  auto y0 = mParameters(1);
+  auto z0 = mZ;
+  auto phi0 = mParameters(2);
+  auto tanl = mParameters(3);
+  auto qOverPt = mParameters(4);
+  auto k = TMath::Abs(o2::constants::math::B2C * zField);
+  auto qpt = 1.0 / qOverPt;
+  auto qR = qpt / std::fabs(k);
+  auto invtanl = 1.0 / tanl;
+  auto Hz = std::copysign(1, zField);
+
+  auto xPV = p[0];
+  auto yPV = p[1];
+  auto zPV = p[2];
+
+  auto qRtanl = qR * tanl;
+  auto invqRtanl = 1.0 / qRtanl;
+  auto [sinp, cosp] = o2::math_utils::sincosd(phi0);
+
+  auto z = zPV;
+  double tol = 1e-4;
+  int max_iter = 10;
+  int iter = 0;
+
+  while (iter++ < max_iter) {
+    double theta = (z0 - z) * invqRtanl;
+    double phi_theta = phi0 + Hz * theta;
+    double sin_phi_theta = sin(phi_theta);
+    double cos_phi_theta = cos(phi_theta);
+
+    double DX = x0 - Hz * qR * (sin_phi_theta - sinp) - xPV;
+    double DY = y0 + Hz * qR * (cos_phi_theta - cosp) - yPV;
+    double DZ = z - zPV;
+
+    double dD2_dZ =
+      2 * DX * cos_phi_theta * invtanl +
+      2 * DY * sin_phi_theta * invtanl +
+      2 * DZ;
+
+    double d2D2_dZ2 =
+      2 * invtanl * invtanl +
+      2 * invtanl * (DX * Hz * sin_phi_theta - DY * Hz * cos_phi_theta) * invqRtanl +
+      2;
+
+    double z_new = z - dD2_dZ / d2D2_dZ2;
+
+    if (std::abs(z_new - z) < tol) {
+      z = z_new;
+      this->propagateToZhelix(z, zField);
+      dca[0] = this->getX() - xPV;
+      dca[1] = this->getY() - yPV;
+      dca[2] = this->getZ() - zPV;
+      LOG(debug) << "Converged after " << iter << " iterations for vertex X=" << p[0] << ", Y=" << p[1] << ", Z = " << p[2];
+      return;
+    }
+    z = z_new;
+  }
+  LOG(debug) << "Failed to converge after " << iter << " iterations for vertex X=" << p[0] << ", Y=" << p[1] << ", Z = " << p[2];
+  return;
+}
+
 } // namespace track
 } // namespace o2
diff --git a/DataFormats/Reconstruction/src/TrackHMP.cxx b/DataFormats/Reconstruction/src/TrackHMP.cxx
new file mode 100644
index 0000000000000..dd5865d5bc363
--- /dev/null
+++ b/DataFormats/Reconstruction/src/TrackHMP.cxx
@@ -0,0 +1,146 @@
+// Copyright 2020-2022 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "ReconstructionDataFormats/TrackParametrizationWithError.h"
+#include "ReconstructionDataFormats/TrackParametrization.h"
+#include "ReconstructionDataFormats/Track.h"
+#include "ReconstructionDataFormats/TrackLTIntegral.h"
+#include "ReconstructionDataFormats/GlobalTrackID.h"
+#include "ReconstructionDataFormats/TrackHMP.h"
+#include "Field/MagneticField.h"
+// #include "DetectorsBase/Propagator.h"
+
+// #include "Field/MagFieldFast.h"
+
+#include 
+#include 
+
+#include  //fields
+// #include "Math/Vector3D.h" //fields
+
+const Double_t kB2C = -0.299792458e-3;
+
+ClassImp(o2::dataformats::TrackHMP)
+
+  using XYZVector = ROOT::Math::XYZVector;
+
+namespace o2
+{
+namespace dataformats
+{
+
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+TrackHMP::TrackHMP() = default;
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+TrackHMP& TrackHMP::operator=(const o2::track::TrackParCov& t)
+{
+  // ass. op.
+
+  return *this;
+}
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+bool TrackHMP::intersect(Double_t pnt[3], Double_t norm[3], double bz) const
+{
+  //+++++++++++++++++++++++++++++++++++++++++
+  // Origin: K. Shileev (Kirill.Shileev@cern.ch)
+  // Finds point of intersection (if exists) of the helix with the plane.
+  // Stores result in fX and fP.
+  // Arguments: planePoint,planeNorm - the plane defined by any plane's point
+  // and vector, normal to the plane
+  // Returns: kTrue if helix intersects the plane, kFALSE otherwise.
+  //+++++++++++++++++++++++++++++++++++++++++
+
+  std::array x0;
+  getXYZGlo(x0); // get track position in MARS
+
+  // estimates initial helix length up to plane
+  Double_t s = (pnt[0] - x0[0]) * norm[0] + (pnt[1] - x0[1]) * norm[1] + (pnt[2] - x0[2]) * norm[2];
+
+  Double_t dist = 99999, distPrev = dist;
+  // Double_t p[3];
+
+  std::array x, p;
+
+  while (TMath::Abs(dist) > 0.00001) {
+
+    // calculates helix at the distance s from x0 ALONG the helix
+
+    propagate(s, x, p, bz);
+    // distance between current helix position and plane
+
+    dist = (x[0] - pnt[0]) * norm[0] + (x[1] - pnt[1]) * norm[1] + (x[2] - pnt[2]) * norm[2];
+    if (TMath::Abs(dist) >= TMath::Abs(distPrev)) { /*Printf("***********************dist > distPrev******************");*/
+      return kFALSE;
+    }
+    distPrev = dist;
+    s -= dist;
+  }
+  // on exit pnt is intersection point,norm is track vector at that point,
+  // all in MARS
+  for (Int_t i = 0; i < 3; i++) {
+    pnt[i] = x.at(i);
+    norm[i] = p.at(i);
+  }
+
+  return kTRUE;
+} // Intersect()
+//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+void TrackHMP::propagate(Double_t len, std::array& x, std::array& p, double bz) const
+{
+  //+++++++++++++++++++++++++++++++++++++++++
+  // Origin: K. Shileev (Kirill.Shileev@cern.ch)
+  // Extrapolate track along simple helix in magnetic field
+  // Arguments: len -distance alogn helix, [cm]
+  //            bz  - mag field, [kGaus]
+  // Returns: x and p contain extrapolated positon and momentum
+  // The momentum returned for straight-line tracks is meaningless !
+  //+++++++++++++++++++++++++++++++++++++++++
+
+  std::array x0;
+  getXYZGlo(x0); // get track position in MARS
+
+  x.at(0) = x0.at(0);
+  x.at(1) = x0.at(1);
+  x.at(2) = x0.at(2);
+
+  if (getPtInv() < o2::constants::math::Almost0 || TMath::Abs(bz) < o2::constants::math::Almost0) { // straight-line tracks
+
+    TVector3 trackDirection(1., 1., 1.);
+    trackDirection.SetMag(1);
+    trackDirection.SetTheta(getTheta());
+    trackDirection.SetPhi(getPhi());
+
+    // Double_t unit[3]; GetDirection(unit);
+    x.at(0) += trackDirection.X() * len;
+    x.at(1) += trackDirection.Y() * len;
+    x.at(2) += trackDirection.Z() * len;
+
+    p.at(0) = trackDirection.X() / o2::constants::math::Almost0;
+    p.at(1) = trackDirection.Y() / o2::constants::math::Almost0;
+    p.at(2) = trackDirection.Z() / o2::constants::math::Almost0;
+  } else {
+
+    getPxPyPzGlo(p);
+    Double_t pp = getP();
+
+    Double_t a = -kB2C * bz * getSign(); ////////// what is kB2C
+    Double_t rho = a / pp;
+    x.at(0) += p.at(0) * TMath::Sin(rho * len) / a - p.at(1) * (1 - TMath::Cos(rho * len)) / a;
+    x.at(1) += p.at(1) * TMath::Sin(rho * len) / a + p.at(0) * (1 - TMath::Cos(rho * len)) / a;
+    x.at(2) += p.at(2) * len / pp;
+    Double_t p0 = p.at(0);
+    p.at(0) = p0 * TMath::Cos(rho * len) - p.at(1) * TMath::Sin(rho * len);
+    p.at(1) = p.at(1) * TMath::Cos(rho * len) + p0 * TMath::Sin(rho * len);
+  }
+
+} // Propagate()
+} // namespace dataformats
+} // namespace o2
diff --git a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
index 52a77ce92a4da..426c3da04726c 100644
--- a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
+++ b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx
@@ -9,6 +9,10 @@
 // granted to it by virtue of its status as an Intergovernmental Organization
 // or submit itself to any jurisdiction.
 
+#ifndef GPUCA_GPUCODE_DEVICE
+#include 
+#endif
+
 #include "ReconstructionDataFormats/TrackLTIntegral.h"
 #include "CommonConstants/PhysicsConstants.h"
 #include "MathUtils/Utils.h"
@@ -35,9 +39,9 @@ GPUd() void TrackLTIntegral::print() const
 }
 
 //_____________________________________________________
-GPUd() void TrackLTIntegral::addStep(float dL, float p2Inv)
+GPUd() void TrackLTIntegral::addStep(float dL, float q2p2)
 {
-  ///< add step in cm to integrals
+  ///< add step in cm to integrals, q2p2 is (q/p)^2.
   mL += dL;
   if (isTimeNotNeeded()) {
     return;
@@ -45,7 +49,7 @@ GPUd() void TrackLTIntegral::addStep(float dL, float p2Inv)
   const float dTns = dL * 1000.f / o2::constants::physics::LightSpeedCm2NS; // time change in ps for beta = 1 particle
   for (int id = 0; id < getNTOFs(); id++) {
     const float m2z = track::PID::getMass2Z(id);
-    const float betaInv = math_utils::sqrt(1.f + m2z * m2z * p2Inv);
+    const float betaInv = math_utils::sqrt(1.f + m2z * m2z * q2p2);
     mT[id] += dTns * betaInv;
   }
 }
diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx
index 24229dd493541..7fe677a6e1c7a 100644
--- a/DataFormats/Reconstruction/src/TrackParametrization.cxx
+++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx
@@ -14,17 +14,6 @@
 /// @since  Oct 1, 2020
 /// @brief
 
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
 #include "ReconstructionDataFormats/TrackParametrization.h"
 #include "ReconstructionDataFormats/Vertex.h"
 #include "ReconstructionDataFormats/DCA.h"
@@ -141,12 +130,12 @@ GPUd() bool TrackParametrization::getPxPyPzGlo(dim3_t& pxyz) const
 
 //____________________________________________________
 template 
-GPUd() bool TrackParametrization::getPosDirGlo(gpu::gpustd::array& posdirp) const
+GPUd() bool TrackParametrization::getPosDirGlo(std::array& posdirp) const
 {
   // fill vector with lab x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha
-  value_t ptI = gpu::CAMath::Abs(getQ2Pt());
+  value_t ptI = getPtInv();
   value_t snp = getSnp();
-  if (ptI < constants::math::Almost0 || gpu::CAMath::Abs(snp) > constants::math::Almost1) {
+  if (gpu::CAMath::Abs(snp) > constants::math::Almost1) {
     return false;
   }
   value_t &sn = posdirp[7], &cs = posdirp[8];
@@ -199,6 +188,38 @@ GPUd() bool TrackParametrization::rotateParam(value_t alpha)
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrization::rotateParam(value_t& alpha, value_t& ca, value_t& sa)
+{
+  // rotate to alpha frame
+  if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) {
+    LOGP(debug, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", getSnp());
+    return false;
+  }
+  //
+  math_utils::detail::bringToPMPi(alpha);
+  math_utils::detail::sincos(alpha - getAlpha(), sa, ca);
+  value_t snp = getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)); // Improve precision
+  // RS: check if rotation does no invalidate track model (cos(local_phi)>=0, i.e. particle direction in local frame is along the X axis
+  if ((csp * ca + snp * sa) < 0) {
+    // LOGF(warning,"Rotation failed: local cos(phi) would become {:.2f}", csp * ca + snp * sa);
+    return false;
+  }
+  //
+  value_t tmp = snp * ca - csp * sa;
+  if (gpu::CAMath::Abs(tmp) > constants::math::Almost1) {
+    LOGP(debug, "Rotation failed: new snp {:.2f}", tmp);
+    return false;
+  }
+  value_t xold = getX(), yold = getY();
+  mAlpha = alpha;
+  mX = xold * ca + yold * sa;
+  mP[kY] = -xold * sa + yold * ca;
+  mP[kSnp] = tmp;
+  return true;
+}
+
 //____________________________________________________________
 template 
 GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const dim3_t& b)
@@ -242,7 +263,7 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di
   step *= gpu::CAMath::Sqrt(1.f + getTgl() * getTgl());
   //
   // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
-  gpu::gpustd::array vecLab{0.f};
+  std::array vecLab{0.f};
   if (!getPosDirGlo(vecLab)) {
     return false;
   }
@@ -261,7 +282,7 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di
     costet = b[2] / bb;
     sintet = bt / bb;
   }
-  gpu::gpustd::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
                                       -sinphi * vecLab[0] + cosphi * vecLab[1],
                                       sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
                                       costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
@@ -389,6 +410,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils:
   // Estimate the impact parameter neglecting the track curvature
   value_t d = gpu::CAMath::Abs(x * snp - y * csp);
   if (d > maxD) {
+    if (dca) { // provide default DCA for failed propag
+      (*dca)[0] = o2::track::DefaultDCA;
+      (*dca)[1] = o2::track::DefaultDCA;
+    }
     return false;
   }
   value_t crv = getCurvature(b);
@@ -410,6 +435,10 @@ GPUd() bool TrackParametrization::propagateParamToDCA(const math_utils:
 #else
     LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z();
 #endif
+    if (dca) { // provide default DCA for failed propag
+      (*dca)[0] = o2::track::DefaultDCA;
+      (*dca)[1] = o2::track::DefaultDCA;
+    }
     return false;
   }
   *this = tmpT;
@@ -520,6 +549,40 @@ GPUd() typename TrackParametrization::value_t TrackParametrization
+GPUd() typename TrackParametrization::value_t TrackParametrization::getPhiAt(value_t xk, value_t b) const
+{
+  ///< this method is just an alias for obtaining phi @ X in the tree->Draw()
+  value_t dx = xk - getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    return getPhi();
+  }
+  value_t crv = (gpu::CAMath::Abs(b) < constants::math::Almost0) ? 0.f : getCurvature(b);
+  value_t x2r = crv * dx;
+  value_t snp = mP[kSnp] + x2r;
+  value_t phi = 999.;
+  if (gpu::CAMath::Abs(snp) < constants::math::Almost1) {
+    phi = gpu::CAMath::ASin(snp) + getAlpha();
+    math_utils::detail::bringTo02Pi(phi);
+  }
+  return phi;
+}
+
+//______________________________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getPhiPosAt(value_t xk, value_t b) const
+{
+  ///< this method is just an alias for obtaining phiPos @ X in the tree->Draw()
+  value_t phi = 999.;
+  auto y = getYAt(xk, b);
+  if (y > -9998.) {
+    phi = gpu::CAMath::ATan2(y, xk) + getAlpha();
+    math_utils::detail::bringTo02Pi(phi);
+  }
+  return phi;
+}
+
 //______________________________________________________________
 template 
 GPUd() typename TrackParametrization::value_t TrackParametrization::getSnpAt(value_t alpha, value_t xk, value_t b) const
@@ -555,6 +618,31 @@ std::string TrackParametrization::asString() const
   return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}",
                      getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName());
 }
+
+//_____________________________________________________________
+template 
+std::string TrackParametrization::asStringHexadecimal()
+{
+  auto _X = getX();
+  auto _Alpha = getAlpha();
+  auto _Y = getY();
+  auto _Z = getZ();
+  auto _Snp = getSnp();
+  auto _Tgl = getTgl();
+  float _Q2Pt = getQ2Pt();
+  float _AbsCharge = getAbsCharge();
+  // print parameters as string
+  return fmt::format("X:{:x} Alp:{:x} Par: {:x} {:x} {:x} {:x} {:x} |Q|:{:x} {:s}\n",
+                     reinterpret_cast(_X),
+                     reinterpret_cast(_Alpha),
+                     reinterpret_cast(_Y),
+                     reinterpret_cast(_Z),
+                     reinterpret_cast(_Snp),
+                     reinterpret_cast(_Tgl),
+                     reinterpret_cast(_Q2Pt),
+                     reinterpret_cast(_AbsCharge),
+                     getPID().getName());
+}
 #endif
 
 //______________________________________________________________
@@ -564,9 +652,30 @@ GPUd() void TrackParametrization::printParam() const
   // print parameters
 #ifndef GPUCA_ALIGPUCODE
   printf("%s\n", asString().c_str());
-#else
-  printf("X:%+.4e Alp:%+.3e Par: %+.4e %+.4e %+.4e %+.4e %+.4e |Q|:%d",
-         getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  printf("X:%+.4e Alp:%+.3e Par: %+.4e %+.4e %+.4e %+.4e %+.4e |Q|:%d %s\n",
+         getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName());
+#endif
+}
+
+//______________________________________________________________
+template 
+GPUd() void TrackParametrization::printParamHexadecimal()
+{
+  // print parameters
+#ifndef GPUCA_ALIGPUCODE
+  printf("%s\n", asStringHexadecimal().c_str());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  printf("X:%x Alp:%x Par: %x %x %x %x %x |Q|:%x %s\n",
+         gpu::CAMath::Float2UIntReint(getX()),
+         gpu::CAMath::Float2UIntReint(getAlpha()),
+         gpu::CAMath::Float2UIntReint(getY()),
+         gpu::CAMath::Float2UIntReint(getZ()),
+         gpu::CAMath::Float2UIntReint(getSnp()),
+         gpu::CAMath::Float2UIntReint(getTgl()),
+         gpu::CAMath::Float2UIntReint(getQ2Pt()),
+         gpu::CAMath::Float2UIntReint(getAbsCharge()),
+         getPID().getName());
 #endif
 }
 
@@ -582,30 +691,37 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
   // DirOutward (==1) - go along the track (increasing mX)
   // DirInward (==-1) - go backward (decreasing mX)
   //
-  const auto fy = mP[0], sn = mP[2];
+  const double fy = mP[0], sn = mP[2];
   const value_t kEps = 1.e-6;
   //
+  if (gpu::CAMath::Abs(getSnp()) > constants::math::Almost1) {
+    return false;
+  }
   auto crv = getCurvature(bz);
-  if (gpu::CAMath::Abs(crv) > constants::math::Almost0) { // helix
+  while (gpu::CAMath::Abs(crv) > constants::math::Almost0) { // helix ?
     // get center of the track circle
-    math_utils::CircleXY circle;
+    math_utils::CircleXY circle{};
     getCircleParamsLoc(bz, circle);
+    if (circle.rC == 0.) {
+      crv = 0.;
+      break;
+    }
     value_t r0 = gpu::CAMath::Sqrt(circle.getCenterD2());
     if (r0 <= constants::math::Almost0) {
       return false; // the track is concentric to circle
     }
-    value_t tR2r0 = 1.f, g = 0.f, tmp = 0.f;
+    double tR2r0 = 1., g = 0., tmp = 0.;
     if (gpu::CAMath::Abs(circle.rC - r0) > kEps) {
       tR2r0 = circle.rC / r0;
       g = 0.5f * (r * r / (r0 * circle.rC) - tR2r0 - 1.f / tR2r0);
       tmp = 1.f + g * tR2r0;
     } else {
       tR2r0 = 1.0;
-      g = 0.5f * r * r / (r0 * circle.rC) - 1.f;
-      tmp = 0.5f * r * r / (r0 * r0);
+      g = 0.5 * r * r / (r0 * circle.rC) - 1.;
+      tmp = 0.5 * r * r / (r0 * r0);
     }
-    value_t det = (1.f - g) * (1.f + g);
-    if (det < 0.f) {
+    auto det = (1. - g) * (1. + g);
+    if (det < 0.) {
       return false; // does not reach raduis r
     }
     det = gpu::CAMath::Sqrt(det);
@@ -615,25 +731,26 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
     // where s0 and c0 make direction for the circle center (=circle.xC/r0 and circle.yC/r0)
     //
     x = circle.xC * tmp;
-    value_t y = circle.yC * tmp;
+    auto y = circle.yC * tmp;
     if (gpu::CAMath::Abs(circle.yC) > constants::math::Almost0) { // when circle.yC==0 the x,y is unique
-      value_t dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det;
-      value_t dfy = tR2r0 * circle.xC * (circle.yC > 0.f ? det : -det);
+      auto dfx = tR2r0 * gpu::CAMath::Abs(circle.yC) * det;
+      auto dfy = tR2r0 * circle.xC * (circle.yC > 0. ? det : -det);
       if (dir == DirAuto) {                              // chose the one which corresponds to smallest step
-        value_t delta = (x - mX) * dfx - (y - fy) * dfy; // the choice of + in C will lead to smaller step if delta<0
-        x += delta < 0.f ? dfx : -dfx;
+        auto delta = (x - mX) * dfx - (y - fy) * dfy;    // the choice of + in C will lead to smaller step if delta<0
+        x += delta < 0. ? dfx : -dfx;
       } else if (dir == DirOutward) { // along track direction: x must be > mX
         x -= dfx;                     // try the smallest step (dfx is positive)
-        value_t dfeps = mX - x;       // handle special case of very small step
+        auto dfeps = mX - x;          // handle special case of very small step
         if (dfeps < -kEps) {
           return true;
         }
         if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r?
-          return mX;
+          x = mX;
+          return true;
         }
         x += dfx + dfx;
-        value_t dxm = x - mX;
-        if (dxm > 0.f) {
+        auto dxm = x - mX;
+        if (dxm > 0.) {
           return true;
         } else if (dxm < -kEps) {
           return false;
@@ -641,16 +758,17 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
         x = mX;                 // don't move
       } else {                  // backward: x must be < mX
         x += dfx;               // try the smallest step (dfx is positive)
-        value_t dfeps = x - mX; // handle special case of very small step
+        auto dfeps = x - mX;    // handle special case of very small step
         if (dfeps < -kEps) {
           return true;
         }
         if (gpu::CAMath::Abs(dfeps) < kEps && gpu::CAMath::Abs(mX * mX + fy * fy - r * r) < kEps) { // are we already in right r?
-          return mX;
+          x = mX;
+          return true;
         }
         x -= dfx + dfx;
-        value_t dxm = x - mX;
-        if (dxm < 0.f) {
+        auto dxm = x - mX;
+        if (dxm < 0.) {
           return true;
         }
         if (dxm > kEps) {
@@ -663,85 +781,86 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
         return false;
       }
     }
-  } else {                                                  // this is a straight track
-    if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis
-      value_t det = (r - mX) * (r + mX);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
-      }
-      x = mX;
-      if (dir == DirAuto) {
-        return true;
-      }
-      det = gpu::CAMath::Sqrt(det);
-      if (dir == DirOutward) { // along the track direction
-        if (sn > 0.f) {
-          if (fy > det) {
-            return false; // track is along Y axis and above the circle
-          }
-        } else {
-          if (fy < -det) {
-            return false; // track is against Y axis amd belo the circle
-          }
+    return true;
+  }
+  // this is a straight track
+  if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis
+    double det = (r - mX) * (r + mX);
+    if (det < 0.f) {
+      return false; // does not reach raduis r
+    }
+    x = mX;
+    if (dir == DirAuto) {
+      return true;
+    }
+    det = gpu::CAMath::Sqrt(det);
+    if (dir == DirOutward) { // along the track direction
+      if (sn > 0.) {
+        if (fy > det) {
+          return false; // track is along Y axis and above the circle
         }
-      } else if (dir == DirInward) { // against track direction
-        if (sn > 0.f) {
-          if (fy < -det) {
-            return false; // track is along Y axis
-          }
-        } else if (fy > det) {
-          return false; // track is against Y axis
+      } else {
+        if (fy < -det) {
+          return false; // track is against Y axis amd belo the circle
         }
       }
-    } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis
-      value_t det = (r - fy) * (r + fy);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
-      }
-      det = gpu::CAMath::Sqrt(det);
-      if (dir == DirAuto) {
-        x = mX > 0.f ? det : -det; // choose the solution requiring the smalest step
-        return true;
-      } else if (dir == DirOutward) { // along the track direction
-        if (mX > det) {
-          return false; // current point is in on the right from the circle
-        } else {
-          x = (mX < -det) ? -det : det; // on the left : within the circle
-        }
-      } else { // against the track direction
-        if (mX < -det) {
-          return false;
-        } else {
-          x = mX > det ? det : -det;
+    } else if (dir == DirInward) { // against track direction
+      if (sn > 0.) {
+        if (fy < -det) {
+          return false; // track is along Y axis
         }
+      } else if (fy > det) {
+        return false; // track is against Y axis
       }
-    } else { // general case of straight line
-      value_t cs = gpu::CAMath::Sqrt((1.f - sn) * (1.f + sn));
-      value_t xsyc = mX * sn - fy * cs;
-      value_t det = (r - xsyc) * (r + xsyc);
-      if (det < 0.f) {
-        return false; // does not reach raduis r
+    }
+  } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis
+    double det = (r - fy) * (r + fy);
+    if (det < 0.) {
+      return false; // does not reach raduis r
+    }
+    det = gpu::CAMath::Sqrt(det);
+    if (dir == DirAuto) {
+      x = mX > 0. ? det : -det; // choose the solution requiring the smalest step
+      return true;
+    } else if (dir == DirOutward) { // along the track direction
+      if (mX > det) {
+        return false; // current point is in on the right from the circle
+      } else {
+        x = (mX < -det) ? -det : det; // on the left : within the circle
       }
-      det = gpu::CAMath::Sqrt(det);
-      value_t xcys = mX * cs + fy * sn;
-      value_t t = -xcys;
-      if (dir == DirAuto) {
-        t += t > 0.f ? -det : det; // chose the solution requiring the smalest step
-      } else if (dir > 0) {        // go in increasing mX direction. ( t+-det > 0)
-        if (t >= -det) {
-          t += det; // take minimal step giving t>0
-        } else {
-          return false; // both solutions have negative t
-        }
-      } else { // go in decreasing mX direction. (t+-det < 0)
-        if (t < det) {
-          t -= det; // take minimal step giving t<0
-        } else {
-          return false; // both solutions have positive t
-        }
+    } else { // against the track direction
+      if (mX < -det) {
+        return false;
+      } else {
+        x = mX > det ? det : -det;
       }
-      x = mX + cs * t;
     }
+  } else { // general case of straight line
+    auto cs = gpu::CAMath::Sqrt((1. - sn) * (1. + sn));
+    auto xsyc = mX * sn - fy * cs;
+    auto det = (r - xsyc) * (r + xsyc);
+    if (det < 0.) {
+      return false; // does not reach raduis r
+    }
+    det = gpu::CAMath::Sqrt(det);
+    auto xcys = mX * cs + fy * sn;
+    auto t = -xcys;
+    if (dir == DirAuto) {
+      t += t > 0. ? -det : det;  // chose the solution requiring the smalest step
+    } else if (dir > 0) {        // go in increasing mX direction. ( t+-det > 0)
+      if (t >= -det) {
+        t += det; // take minimal step giving t>0
+      } else {
+        return false; // both solutions have negative t
+      }
+    } else { // go in decreasing mX direction. (t+-det < 0)
+      if (t < det) {
+        t -= det; // take minimal step giving t<0
+      } else {
+        return false; // both solutions have positive t
+      }
+    }
+    x = mX + cs * t;
   }
   //
   return true;
@@ -749,7 +868,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val
 
 //______________________________________________
 template 
-GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool anglecorr, value_t dedx)
+GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool anglecorr)
 {
   //------------------------------------------------------------------
   // This function corrects the track parameters for the energy loss in crossed material.
@@ -759,41 +878,77 @@ GPUd() bool TrackParametrization::correctForELoss(value_t xrho, bool an
   // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly
   // "anglecorr" - switch for the angular correction
   //------------------------------------------------------------------
-  constexpr value_t kMaxELossFrac = 0.3f; // max allowed fractional eloss
-  constexpr value_t kMinP = 0.01f;        // kill below this momentum
-
-  // Apply angle correction, if requested
-  if (anglecorr) {
-    value_t csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2
-    value_t cst2I = (1.f + getTgl() * getTgl());        // 1/cos(lambda)^2
-    value_t angle = gpu::CAMath::Sqrt(cst2I / (csp2));
-    xrho *= angle;
-  }
-  value_t p = getP();
-  value_t p2 = p * p;
-  value_t e2 = p2 + getPID().getMass2();
-  value_t beta2 = p2 / e2;
-
-  // Calculating the energy loss corrections************************
-  if ((xrho != 0.f) && (beta2 < 1.f)) {
-    if (dedx < kCalcdEdxAuto + constants::math::Almost1) { // request to calculate dedx on the fly
-      dedx = BetheBlochSolid(p / getPID().getMass());
-      if (mAbsCharge != 1) {
-        dedx *= mAbsCharge * mAbsCharge;
-      }
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
+
+  auto m = getPID().getMass();
+  if (m > 0 && xrho != 0.f) {
+    // Apply angle correction, if requested
+    if (anglecorr) {
+      value_t csp2 = (1.f - getSnp()) * (1.f + getSnp()); // cos(phi)^2
+      value_t cst2I = (1.f + getTgl() * getTgl());        // 1/cos(lambda)^2
+      value_t angle = gpu::CAMath::Sqrt(cst2I / (csp2));
+      xrho *= angle;
+    }
+    int charge2 = getAbsCharge() * getAbsCharge();
+    value_t p = getP(), p0 = p, p2 = p * p, e2 = p2 + getPID().getMass2(), massInv = 1. / m, bg = p * massInv;
+    value_t e = gpu::CAMath::Sqrt(e2), ekin = e - m, dedx = getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
     }
-
     value_t dE = dedx * xrho;
-    value_t e = gpu::CAMath::Sqrt(e2);
-    if (gpu::CAMath::Abs(dE) > kMaxELossFrac * e) {
-      return false; // 30% energy loss is too much!
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
     }
-    value_t eupd = e + dE;
-    value_t pupd2 = eupd * eupd - getPID().getMass2();
-    if (pupd2 < kMinP * kMinP) {
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - getPID().getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
+      }
+    }
+
+    if (p < kMinP) {
       return false;
     }
-    setQ2Pt(getQ2Pt() * p / gpu::CAMath::Sqrt(pupd2));
+    setQ2Pt(getQ2Pt() * p0 / p);
   }
 
   return true;
@@ -812,10 +967,30 @@ GPUd() typename o2::track::TrackParametrization::yzerr_t TrackParametri
           {v.getSigmaX2() * sn2 + dsxysncs + v.getSigmaY2() * cs2, (sn + cs) * v.getSigmaYZ(), v.getSigmaZ2()}};
 }
 
+//______________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getDCAYtoMV(value_t b, value_t xmv, value_t ymv, value_t zmv) const
+{
+  auto ttmp = *this;
+  dim2_t dca;
+  return ttmp.propagateParamToDCA({xmv, ymv, zmv}, b, &dca) ? dca[0] : -9999.;
+}
+
+//______________________________________________
+template 
+GPUd() typename TrackParametrization::value_t TrackParametrization::getDCAZtoMV(value_t b, value_t xmv, value_t ymv, value_t zmv) const
+{
+  auto ttmp = *this;
+  dim2_t dca;
+  return ttmp.propagateParamToDCA({xmv, ymv, zmv}, b, &dca) ? dca[1] : -9999.;
+}
+
 namespace o2::track
 {
+#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code.
 template class TrackParametrization;
-#ifndef GPUCA_GPUCODE_DEVICE
+#endif
+#ifndef GPUCA_GPUCODE
 template class TrackParametrization;
 #endif
 } // namespace o2::track
diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
index 3fe7be6d19fec..2f8f15f783c60 100644
--- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
+++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx
@@ -12,13 +12,11 @@
 #include "ReconstructionDataFormats/TrackParametrizationWithError.h"
 #include "ReconstructionDataFormats/Vertex.h"
 #include "ReconstructionDataFormats/DCA.h"
+#include "CommonConstants/MathConstants.h"
 #include 
 
 #ifndef GPUCA_GPUCODE_DEVICE
 #include 
-#ifndef GPUCA_STANDALONE
-#include "Math/SMatrix.h"
-#endif
 #endif
 
 #ifndef GPUCA_ALIGPUCODE
@@ -45,7 +43,7 @@ GPUd() void TrackParametrizationWithError::invert()
 
 //______________________________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, value_t b)
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, value_t bz)
 {
   //----------------------------------------------------------------
   // propagate this track to the plane X=xk (cm) in the field "b" (kG)
@@ -54,7 +52,7 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
     return true;
   }
-  value_t crv = this->getCurvature(b);
+  value_t crv = this->getCurvature(bz);
   value_t x2r = crv * dx;
   value_t f1 = this->getSnp(), f2 = f1 + x2r;
   if ((gpu::CAMath::Abs(f1) > constants::math::Almost1) || (gpu::CAMath::Abs(f2) > constants::math::Almost1)) {
@@ -68,7 +66,8 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   if (gpu::CAMath::Abs(r2) < constants::math::Almost0) {
     return false;
   }
-  double dy2dx = (f1 + f2) / (r1 + r2);
+  double r1pr2Inv = 1. / (r1 + r2);
+  double dy2dx = (f1 + f2) * r1pr2Inv;
   bool arcz = gpu::CAMath::Abs(x2r) > 0.05f;
   params_t dP{0.f};
   if (arcz) {
@@ -108,14 +107,17 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
           &c44 = mC[kSigQ2Pt2];
 
   // evaluate matrix in double prec.
-  double rinv = 1. / r1;
-  double r3inv = rinv * rinv * rinv;
-  double f24 = dx * b * constants::math::B2C; // x2r/mP[kQ2Pt];
-  double f02 = dx * r3inv;
-  double f04 = 0.5 * f24 * f02;
-  double f12 = f02 * this->getTgl() * f1;
-  double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1;
-  double f13 = dx * rinv;
+  value_t kb = bz * constants::math::B2C;
+  double r2inv = 1. / r2, r1inv = 1. / r1;
+  double dx2r1pr2 = dx * r1pr2Inv;
+
+  double hh = dx2r1pr2 * r2inv * (1. + r1 * r2 + f1 * f2), jj = dx * (dy2dx - f2 * r2inv);
+  double f02 = hh * r1inv;
+  double f04 = hh * dx2r1pr2 * kb;
+  double f24 = dx * kb; // x2r/mP[kQ2Pt];
+  double f12 = this->getTgl() * (f02 * f2 + jj);
+  double f13 = dx * (r2 + f2 * dy2dx);
+  double f14 = this->getTgl() * (f04 * f2 + jj * f24);
 
   // b = C*ft
   double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
@@ -153,6 +155,107 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, TrackParametrization& linRef0, value_t bz)
+{
+  //----------------------------------------------------------------
+  // propagate this track to the plane X=xk (cm) in the field "b" (kG), using linRef as linearization point
+  //----------------------------------------------------------------
+  if (this->getAbsCharge() == 0) {
+    bz = 0;
+  }
+  value_t dx = xk - this->getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    this->setX(xk);
+    linRef0.setX(xk);
+    return true;
+  }
+  // propagate reference track
+  TrackParametrization linRef1 = linRef0;
+  if (!linRef1.propagateTo(xk, bz)) {
+    return false;
+  }
+  value_t kb = bz * constants::math::B2C;
+  // evaluate in double prec.
+  double snpRef0 = linRef0.getSnp(), cspRef0 = gpu::CAMath::Sqrt((1 - snpRef0) * (1 + snpRef0));
+  double snpRef1 = linRef1.getSnp(), cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  double cspRef0Inv = 1 / cspRef0, cspRef1Inv = 1 / cspRef1, cc = cspRef0 + cspRef1, ccInv = 1 / cc, dy2dx = (snpRef0 + snpRef1) * ccInv;
+  double dxccInv = dx * ccInv, hh = dxccInv * cspRef1Inv * (1 + cspRef0 * cspRef1 + snpRef0 * snpRef1), jj = dx * (dy2dx - snpRef1 * cspRef1Inv);
+
+  double f02 = hh * cspRef0Inv;
+  double f04 = hh * dxccInv * kb;
+  double f24 = dx * kb;
+  double f12 = linRef0.getTgl() * (f02 * snpRef1 + jj);
+  double f13 = dx * (cspRef1 + snpRef1 * dy2dx); // dS
+  double f14 = linRef0.getTgl() * (f04 * snpRef1 + jj * f24);
+
+  // difference between the current and reference state
+  value_t diff[5];
+  for (int i = 0; i < 5; i++) {
+    diff[i] = this->getParam(i) - linRef0.getParam(i);
+  }
+  value_t snpUpd = snpRef1 + diff[kSnp] + f24 * diff[kQ2Pt];
+  if (gpu::CAMath::Abs(snpUpd) > constants::math::Almost1) {
+    return false;
+  }
+  linRef0 = linRef1; // update reference track
+  this->setX(xk);
+  this->setY(linRef1.getY() + diff[kY] + f02 * diff[kSnp] + f04 * diff[kQ2Pt]);
+  this->setZ(linRef1.getZ() + diff[kZ] + f13 * diff[kTgl] + f14 * diff[kQ2Pt]);
+  this->setSnp(snpUpd);
+  this->setTgl(linRef1.getTgl() + diff[kTgl]);
+  this->setQ2Pt(linRef1.getQ2Pt() + diff[kQ2Pt]);
+
+  value_t &c00 = mC[kSigY2], &c10 = mC[kSigZY], &c11 = mC[kSigZ2], &c20 = mC[kSigSnpY], &c21 = mC[kSigSnpZ],
+          &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
+          &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
+          &c44 = mC[kSigQ2Pt2];
+
+  // b = C*ft
+  double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
+  double b02 = f24 * c40;
+  double b10 = f02 * c21 + f04 * c41, b11 = f12 * c21 + f14 * c41 + f13 * c31;
+  double b12 = f24 * c41;
+  double b20 = f02 * c22 + f04 * c42, b21 = f12 * c22 + f14 * c42 + f13 * c32;
+  double b22 = f24 * c42;
+  double b40 = f02 * c42 + f04 * c44, b41 = f12 * c42 + f14 * c44 + f13 * c43;
+  double b42 = f24 * c44;
+  double b30 = f02 * c32 + f04 * c43, b31 = f12 * c32 + f14 * c43 + f13 * c33;
+  double b32 = f24 * c43;
+
+  // a = f*b = f*C*ft
+  double a00 = f02 * b20 + f04 * b40, a01 = f02 * b21 + f04 * b41, a02 = f02 * b22 + f04 * b42;
+  double a11 = f12 * b21 + f14 * b41 + f13 * b31, a12 = f12 * b22 + f14 * b42 + f13 * b32;
+  double a22 = f24 * b42;
+
+  // F*C*Ft = C + (b + bt + a)
+  c00 += b00 + b00 + a00;
+  c10 += b10 + b01 + a01;
+  c20 += b20 + b02 + a02;
+  c30 += b30;
+  c40 += b40;
+  c11 += b11 + b11 + a11;
+  c21 += b21 + b12 + a12;
+  c31 += b31;
+  c41 += b41;
+  c22 += b22 + b22 + a22;
+  c32 += b32;
+  c42 += b42;
+
+  checkCovariance();
+
+  return true;
+}
+
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::testRotate(value_t) const
+{
+  // no ops
+  return true;
+}
+
 //______________________________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::rotate(value_t alpha)
@@ -208,6 +311,101 @@ GPUd() bool TrackParametrizationWithError::rotate(value_t alpha)
   return true;
 }
 
+//______________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::rotate(value_t alpha, TrackParametrization& linRef0, value_t bz)
+{
+  // RS: similar to int32_t GPUTPCGMPropagator::RotateToAlpha(float newAlpha), i.e. rotate the track to new frame alpha, using linRef as linearization point
+  // rotate to alpha frame the reference (linearization point) trackParam, then align the current track to it
+  if (gpu::CAMath::Abs(this->getSnp()) > constants::math::Almost1) {
+    LOGP(debug, "Precondition is not satisfied: |sin(phi)|>1 ! {:f}", this->getSnp());
+    return false;
+  }
+  //
+  math_utils::detail::bringToPMPi(alpha);
+  //
+  value_t ca = 0, sa = 0;
+  TrackParametrization linRef1 = linRef0;
+  // rotate the reference, adjusting alpha to +-pi, return precalculated cos and sin of alpha - alphaOld
+  if (!linRef1.rotateParam(alpha, ca, sa)) {
+    return false;
+  }
+
+  value_t trackX = this->getX() * ca + this->getY() * sa; // X of the rotated current track
+  if (!linRef1.propagateParamTo(trackX, bz)) {
+    return false;
+  }
+
+  // now rotate the current track
+  value_t snp = this->getSnp(), csp = gpu::CAMath::Sqrt((1.f - snp) * (1.f + snp)), updSnp = snp * ca - csp * sa;
+  if ((csp * ca + snp * sa) < 0 || gpu::CAMath::Abs(updSnp) > constants::math::Almost1) {
+    // LOGP(warning,"Rotation failed: local cos(phi) would become {:.2f}", csp * ca + snp * sa);
+    return false;
+  }
+  this->setY(-sa * this->getX() + ca * this->getY());
+  this->setX(trackX);
+  this->setSnp(updSnp);
+  this->setAlpha(alpha);
+
+  // rotate covariance, accounting for the extra error from the rotated X
+  value_t snpRef0 = linRef0.getSnp(), cspRef0 = gpu::CAMath::Sqrt((value_t(1) - snpRef0) * (value_t(1) + snpRef0)); // original reference
+  value_t snpRef1 = linRef1.getSnp(), cspRef1 = ca * cspRef0 + sa * snpRef0;                                        // rotated reference
+  value_t rr = cspRef1 / cspRef0;                                                                                   // cos1_ref / cos0_ref
+
+  // "extra row" of the lower triangle of cov. matrix
+  value_t cXSigY = mC[kSigY2] * ca * sa;
+  value_t cXSigZ = mC[kSigZY] * sa;
+  value_t cXSigSnp = mC[kSigSnpY] * rr * sa;
+  value_t cXSigTgl = mC[kSigTglY] * sa;
+  value_t cXSigQ2Pt = mC[kSigQ2PtY] * sa;
+  value_t cSigX2 = mC[kSigY2] * sa * sa;
+
+  // plane rotation of existing cov matrix
+  mC[kSigY2] *= ca * ca;
+  mC[kSigZY] *= ca;
+  mC[kSigSnpY] *= ca * rr;
+  mC[kSigSnpZ] *= rr;
+  mC[kSigSnp2] *= rr * rr;
+  mC[kSigTglY] *= ca;
+  mC[kSigTglSnp] *= rr;
+  mC[kSigQ2PtY] *= ca;
+  mC[kSigQ2PtSnp] *= rr;
+
+  // transport covariance from pseudo 6x6 matrix to usual 5x5, Jacobian (trust to Sergey):
+  auto cspRef1Inv = value_t(1) / cspRef1;
+  auto j3 = -snpRef1 * cspRef1Inv;          // -pYmod/pXmod = -tg_pho = -sin_phi_mod / cos_phi_mod
+  auto j4 = -linRef1.getTgl() * cspRef1Inv; // -pZmod/pXmod = -tgl_mod / cos_phi_mod
+  auto j5 = linRef1.getCurvature(bz);
+  //       Y  Z Sin DzDs q/p  X
+  //  { {  1, 0, 0,  0,  0,  j3 }, // Y
+  //    {  0, 1, 0,  0,  0,  j4 }, // Z
+  //    {  0, 0, 1,  0,  0,  j5 }, // snp
+  //    {  0, 0, 0,  1,  0,   0 }, // tgl
+  //    {  0, 0, 0,  0,  1,   0 } }; // q/pt
+  auto hXSigY = cXSigY + cSigX2 * j3;
+  auto hXSigZ = cXSigZ + cSigX2 * j4;
+  auto hXSigSnp = cXSigSnp + cSigX2 * j5;
+
+  mC[kSigY2] += j3 * (cXSigY + hXSigY);
+  mC[kSigZ2] += j4 * (cXSigZ + hXSigZ);
+  mC[kSigSnpY] += cXSigSnp * j3 + hXSigY * j5;
+  mC[kSigSnp2] += j5 * (cXSigSnp + hXSigSnp);
+  mC[kSigTglZ] += cXSigTgl * j4;
+  mC[kSigQ2PtY] += cXSigQ2Pt * j3;
+  mC[kSigQ2PtSnp] += cXSigQ2Pt * j5;
+
+  mC[kSigZY] += cXSigZ * j3 + hXSigY * j4;
+  mC[kSigSnpZ] += cXSigSnp * j4 + hXSigZ * j5;
+  mC[kSigTglY] += cXSigTgl * j3;
+  mC[kSigTglSnp] += cXSigTgl * j5;
+  mC[kSigQ2PtZ] += cXSigQ2Pt * j4;
+
+  checkCovariance();
+  linRef0 = linRef1;
+
+  return true;
+}
+
 //_______________________________________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dataformats::VertexBase& vtx, value_t b, o2::dataformats::DCA* dca, value_t maxD)
@@ -222,6 +420,10 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
   // Estimate the impact parameter neglecting the track curvature
   value_t d = gpu::CAMath::Abs(x * snp - y * csp);
   if (d > maxD) {
+    if (dca) { // provide default DCA for failed propag
+      dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA,
+               o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov);
+    }
     return false;
   }
   value_t crv = this->getCurvature(b);
@@ -239,9 +441,11 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
   if (!tmpT.rotate(alp) || !tmpT.propagateTo(xv, b)) {
 #if !defined(GPUCA_ALIGPUCODE)
     LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: " << tmpT.asString();
-#elif !defined(GPUCA_NO_FMT)
-    LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx;
 #endif
+    if (dca) { // provide default DCA for failed propag
+      dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA,
+               o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov);
+    }
     return false;
   }
   *this = tmpT;
@@ -256,7 +460,7 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat
 //______________________________________________________________
 template 
 GPUd() TrackParametrizationWithError::TrackParametrizationWithError(const dim3_t& xyz, const dim3_t& pxpypz,
-                                                                             const gpu::gpustd::array& cv, int charge, bool sectorAlpha, const PID pid)
+                                                                             const std::array& cv, int charge, bool sectorAlpha, const PID pid)
 {
   // construct track param and covariance from kinematics and lab errors
   set(xyz, pxpypz, cv, charge, sectorAlpha, pid);
@@ -265,7 +469,7 @@ GPUd() TrackParametrizationWithError::TrackParametrizationWithError(con
 //______________________________________________________________
 template 
 GPUd() void TrackParametrizationWithError::set(const dim3_t& xyz, const dim3_t& pxpypz,
-                                                        const gpu::gpustd::array& cv, int charge, bool sectorAlpha, const PID pid)
+                                                        const std::array& cv, int charge, bool sectorAlpha, const PID pid)
 {
   // set track param and covariance from kinematics and lab errors
 
@@ -465,14 +669,14 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
   if (gpu::CAMath::Abs(r2) < constants::math::Almost0) {
     return false;
   }
-
-  value_t dy2dx = (f1 + f2) / (r1 + r2);
+  double r1pr2Inv = 1. / (r1 + r2), r2inv = 1. / r2, r1inv = 1. / r1;
+  double dy2dx = (f1 + f2) * r1pr2Inv, dx2r1pr2 = dx * r1pr2Inv;
   value_t step = (gpu::CAMath::Abs(x2r) < 0.05f) ? dx * gpu::CAMath::Abs(r2 + f2 * dy2dx)                                                   // chord
                                                  : 2.f * gpu::CAMath::ASin(0.5f * dx * gpu::CAMath::Sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc
   step *= gpu::CAMath::Sqrt(1.f + this->getTgl() * this->getTgl());
   //
   // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
-  gpu::gpustd::array vecLab{0.f};
+  std::array vecLab{0.f};
   if (!this->getPosDirGlo(vecLab)) {
     return false;
   }
@@ -482,15 +686,16 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
           &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
           &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
           &c44 = mC[kSigQ2Pt2];
+
   // evaluate matrix in double prec.
-  double rinv = 1. / r1;
-  double r3inv = rinv * rinv * rinv;
-  double f24 = dx * b[2] * constants::math::B2C; // x2r/track[kQ2Pt];
-  double f02 = dx * r3inv;
-  double f04 = 0.5 * f24 * f02;
-  double f12 = f02 * this->getTgl() * f1;
-  double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1;
-  double f13 = dx * rinv;
+  value_t kb = b[2] * constants::math::B2C;
+  double hh = dx2r1pr2 * r2inv * (1. + r1 * r2 + f1 * f2), jj = dx * (dy2dx - f2 * r2inv);
+  double f02 = hh * r1inv;
+  double f04 = hh * dx2r1pr2 * kb;
+  double f24 = dx * kb; // x2r/mP[kQ2Pt];
+  double f12 = this->getTgl() * (f02 * f2 + jj);
+  double f13 = dx * (r2 + f2 * dy2dx);
+  double f14 = this->getTgl() * (f04 * f2 + jj * f24);
 
   // b = C*ft
   double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
@@ -539,7 +744,7 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
     costet = b[2] / bb;
     sintet = bt / bb;
   }
-  gpu::gpustd::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
                                       -sinphi * vecLab[0] + cosphi * vecLab[1],
                                       sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
                                       costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
@@ -548,8 +753,8 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
                                       vecLab[6]};
 
   // Do the helix step
-  value_t sgn = this->getSign();
-  g3helx3(sgn * bb, step, vect);
+  value_t q = this->getCharge();
+  g3helx3(q * bb, step, vect);
 
   // Rotate back to the Global System
   vecLab[0] = cosphi * costet * vect[0] - sinphi * vect[1] + cosphi * sintet * vect[2];
@@ -588,7 +793,199 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons
   this->setZ(z);
   this->setSnp(vecLab[4] * t);
   this->setTgl(vecLab[5] * t);
-  this->setQ2Pt(sgn * t / vecLab[6]);
+  this->setQ2Pt(q * t / vecLab[6]);
+
+  return true;
+}
+
+//____________________________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, TrackParametrization& linRef0, const dim3_t& b)
+{
+  //----------------------------------------------------------------
+  // Extrapolate this track to the plane X=xk in the field b[].
+  //
+  // X [cm] is in the "tracking coordinate system" of this track.
+  // b[]={Bx,By,Bz} [kG] is in the Global coordidate system.
+  //----------------------------------------------------------------
+
+  value_t dx = xk - this->getX();
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    return true;
+  }
+  // Do not propagate tracks outside the ALICE detector
+  if (gpu::CAMath::Abs(dx) > 1e5 || gpu::CAMath::Abs(this->getY()) > 1e5 || gpu::CAMath::Abs(this->getZ()) > 1e5) {
+    LOG(warning) << "Anomalous track, target X:" << xk;
+    //    print();
+    return false;
+  }
+  if (gpu::CAMath::Abs(dx) < constants::math::Almost0) {
+    this->setX(xk);
+    linRef0.setX(xk);
+    return true;
+  }
+  // preliminary calculations to find the step size
+  value_t crv = (gpu::CAMath::Abs(b[2]) < constants::math::Almost0) ? 0.f : linRef0.getCurvature(b[2]);
+  if (gpu::CAMath::Abs(crv) < constants::math::Almost0) {
+    return propagateTo(xk, linRef0, 0.);
+  }
+  value_t kb = b[2] * constants::math::B2C, x2r = crv * dx;
+  // evaluate in double prec.
+  value_t snpRef0 = linRef0.getSnp(), snpRef1 = snpRef0 + x2r;
+  if ((gpu::CAMath::Abs(snpRef0) > constants::math::Almost1) || (gpu::CAMath::Abs(snpRef1) > constants::math::Almost1)) {
+    return false;
+  }
+  value_t cspRef0 = gpu::CAMath::Sqrt((1 - snpRef0) * (1 + snpRef0)), cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  if (gpu::CAMath::Abs(cspRef0) < constants::math::Almost0 || gpu::CAMath::Abs(cspRef1) < constants::math::Almost0) {
+    return false;
+  }
+  value_t cspRef0Inv = value_t(1) / cspRef0, cspRef1Inv = value_t(1) / cspRef1, cc = cspRef0 + cspRef1, ccInv = value_t(1) / cc, dy2dx = (snpRef0 + snpRef1) * ccInv;
+  value_t step = (gpu::CAMath::Abs(crv * dx) < 0.05f) ? dx * (cspRef1 + snpRef1 * dy2dx) : 2. * gpu::CAMath::ASin(0.5 * dx * gpu::CAMath::Sqrt(1.f + dy2dx * dy2dx) * crv) / crv; // arc
+  step *= gpu::CAMath::Sqrt(1.f + linRef0.getTgl() * linRef0.getTgl());
+
+  //
+  // get the track x,y,z,px/p,py/p,pz/p,p,sinAlpha,cosAlpha in the Global System
+  std::array vecLab{0.f};
+  if (!linRef0.getPosDirGlo(vecLab)) {
+    return false;
+  }
+  //
+  // Rotate to the system where Bx=By=0.
+  value_t bxy2 = b[0] * b[0] + b[1] * b[1];
+  value_t bt = gpu::CAMath::Sqrt(bxy2);
+  value_t cosphi = 1.f, sinphi = 0.f;
+  if (bt > constants::math::Almost0) {
+    cosphi = b[0] / bt;
+    sinphi = b[1] / bt;
+  }
+  value_t bb = gpu::CAMath::Sqrt(bxy2 + b[2] * b[2]);
+  value_t costet = 1., sintet = 0.;
+  if (bb > constants::math::Almost0) {
+    costet = b[2] / bb;
+    sintet = bt / bb;
+  }
+  std::array vect{costet * cosphi * vecLab[0] + costet * sinphi * vecLab[1] - sintet * vecLab[2],
+                              -sinphi * vecLab[0] + cosphi * vecLab[1],
+                              sintet * cosphi * vecLab[0] + sintet * sinphi * vecLab[1] + costet * vecLab[2],
+                              costet * cosphi * vecLab[3] + costet * sinphi * vecLab[4] - sintet * vecLab[5],
+                              -sinphi * vecLab[3] + cosphi * vecLab[4],
+                              sintet * cosphi * vecLab[3] + sintet * sinphi * vecLab[4] + costet * vecLab[5],
+                              vecLab[6]};
+
+  // Do the helix step
+  value_t q = this->getCharge();
+  g3helx3(q * bb, step, vect);
+
+  // Rotate back to the Global System
+  vecLab[0] = cosphi * costet * vect[0] - sinphi * vect[1] + cosphi * sintet * vect[2];
+  vecLab[1] = sinphi * costet * vect[0] + cosphi * vect[1] + sinphi * sintet * vect[2];
+  vecLab[2] = -sintet * vect[0] + costet * vect[2];
+
+  vecLab[3] = cosphi * costet * vect[3] - sinphi * vect[4] + cosphi * sintet * vect[5];
+  vecLab[4] = sinphi * costet * vect[3] + cosphi * vect[4] + sinphi * sintet * vect[5];
+  vecLab[5] = -sintet * vect[3] + costet * vect[5];
+
+  // Rotate back to the Tracking System
+  value_t sinalp = -vecLab[7], cosalp = vecLab[8];
+  value_t t = cosalp * vecLab[0] - sinalp * vecLab[1];
+  vecLab[1] = sinalp * vecLab[0] + cosalp * vecLab[1];
+  vecLab[0] = t;
+  t = cosalp * vecLab[3] - sinalp * vecLab[4];
+  vecLab[4] = sinalp * vecLab[3] + cosalp * vecLab[4];
+  vecLab[3] = t;
+
+  // Do the final correcting step to the target plane (linear approximation)
+  value_t x = vecLab[0], y = vecLab[1], z = vecLab[2];
+  if (gpu::CAMath::Abs(dx) > constants::math::Almost0) {
+    if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) {
+      return false;
+    }
+    auto dxFin = xk - vecLab[0];
+    x += dxFin;
+    y += vecLab[4] / vecLab[3] * dxFin;
+    z += vecLab[5] / vecLab[3] * dxFin;
+  }
+
+  // Calculate the track parameters
+  auto linRef1 = linRef0;
+  t = 1.f / gpu::CAMath::Sqrt(vecLab[3] * vecLab[3] + vecLab[4] * vecLab[4]);
+  linRef1.setX(xk);
+  linRef1.setY(y);
+  linRef1.setZ(z);
+  linRef1.setSnp(snpRef1 = vecLab[4] * t); // reassign snpRef1
+  linRef1.setTgl(vecLab[5] * t);
+  linRef1.setQ2Pt(q * t / vecLab[6]);
+
+  // recalculate parameters of the transported ref track needed for transport of this:
+  cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1));
+  cspRef1Inv = value_t(1) / cspRef1;
+  cc = cspRef0 + cspRef1;
+  ccInv = value_t(1) / cc;
+  dy2dx = (snpRef0 + snpRef1) * ccInv;
+  double dxccInv = dx * ccInv, hh = dxccInv * cspRef1Inv * (1 + cspRef0 * cspRef1 + snpRef0 * snpRef1), jj = dx * (dy2dx - snpRef1 * cspRef1Inv);
+  double f02 = hh * cspRef0Inv;
+  double f04 = hh * dxccInv * kb;
+  double f24 = dx * kb;
+  double f12 = linRef0.getTgl() * (f02 * snpRef1 + jj);
+  double f13 = dx * (cspRef1 + snpRef1 * dy2dx); // dS
+  double f14 = linRef0.getTgl() * (f04 * snpRef1 + jj * f24);
+
+  // difference between the current and reference state
+  value_t diff[5];
+  for (int i = 0; i < 5; i++) {
+    diff[i] = this->getParam(i) - linRef0.getParam(i);
+  }
+  value_t snpUpd = snpRef1 + diff[kSnp] + f24 * diff[kQ2Pt];
+  if (gpu::CAMath::Abs(snpUpd) > constants::math::Almost1) {
+    return false;
+  }
+  this->setX(xk);
+  this->setY(linRef1.getY() + diff[kY] + f02 * diff[kSnp] + f04 * diff[kQ2Pt]);
+  this->setZ(linRef1.getZ() + diff[kZ] + f13 * diff[kTgl] + f14 * diff[kQ2Pt]);
+  this->setSnp(snpUpd);
+  this->setTgl(linRef1.getTgl() + diff[kTgl]);
+  this->setQ2Pt(linRef1.getQ2Pt() + diff[kQ2Pt]);
+
+  linRef0 = linRef1; // update reference track
+
+  // matrix transformed with Bz component only
+  value_t &c00 = mC[kSigY2], &c10 = mC[kSigZY], &c11 = mC[kSigZ2], &c20 = mC[kSigSnpY], &c21 = mC[kSigSnpZ],
+          &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2],
+          &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl],
+          &c44 = mC[kSigQ2Pt2];
+
+  // b = C*ft
+  double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30;
+  double b02 = f24 * c40;
+  double b10 = f02 * c21 + f04 * c41, b11 = f12 * c21 + f14 * c41 + f13 * c31;
+  double b12 = f24 * c41;
+  double b20 = f02 * c22 + f04 * c42, b21 = f12 * c22 + f14 * c42 + f13 * c32;
+  double b22 = f24 * c42;
+  double b40 = f02 * c42 + f04 * c44, b41 = f12 * c42 + f14 * c44 + f13 * c43;
+  double b42 = f24 * c44;
+  double b30 = f02 * c32 + f04 * c43, b31 = f12 * c32 + f14 * c43 + f13 * c33;
+  double b32 = f24 * c43;
+
+  // a = f*b = f*C*ft
+  double a00 = f02 * b20 + f04 * b40, a01 = f02 * b21 + f04 * b41, a02 = f02 * b22 + f04 * b42;
+  double a11 = f12 * b21 + f14 * b41 + f13 * b31, a12 = f12 * b22 + f14 * b42 + f13 * b32;
+  double a22 = f24 * b42;
+
+  // F*C*Ft = C + (b + bt + a)
+  c00 += b00 + b00 + a00;
+  c10 += b10 + b01 + a01;
+  c20 += b20 + b02 + a02;
+  c30 += b30;
+  c40 += b40;
+  c11 += b11 + b11 + a11;
+  c21 += b21 + b12 + a12;
+  c31 += b31;
+  c41 += b41;
+  c22 += b22 + b22 + a22;
+  c32 += b32;
+  c42 += b42;
+
+  checkCovariance();
 
   return true;
 }
@@ -603,7 +1000,7 @@ GPUd() void TrackParametrizationWithError::checkCorrelations()
     for (int j = i; j--;) {
       auto sig2 = mC[DiagMap[i]] * mC[DiagMap[j]];
       auto& cov = mC[CovarMap[i][j]];
-      if (cov * cov >= MaxCorr) { // constrain correlation
+      if (cov * cov >= MaxCorr * sig2) { // constrain correlation
         cov = gpu::CAMath::Sqrt(sig2) * (cov > 0. ? MaxCorr : -MaxCorr);
       }
     }
@@ -721,19 +1118,45 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2(const value
   value_t z = this->getZ() - p[1];
   auto chi2 = (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det;
   if (chi2 < 0.) {
-    LOGP(warning, "Negative chi2={}, Cluster: {} {} {} Dy:{} Dz:{} | sdd:{} sdz:{} szz:{} det:{}", chi2, cov[0], cov[1], cov[2], d, z, sdd, sdz, szz, det);
 #ifndef GPUCA_ALIGPUCODE
+    LOGP(warning, "Negative chi2={}, Cluster: {} {} {} Dy:{} Dz:{} | sdd:{} sdz:{} szz:{} det:{}", chi2, cov[0], cov[1], cov[2], d, z, sdd, sdz, szz, det);
     LOGP(warning, "Track: {}", asString());
 #endif
   }
   return chi2;
 }
 
-#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // Disable function relying on ROOT SMatrix on GPU
+//______________________________________________
+template 
+GPUd() auto TrackParametrizationWithError::getPredictedChi2Quiet(const value_t* p, const value_t* cov) const -> value_t
+{
+  // Estimate the chi2 of the space point "p" with the cov. matrix "cov"
+  auto sdd = static_cast(getSigmaY2()) + static_cast(cov[0]);
+  auto sdz = static_cast(getSigmaZY()) + static_cast(cov[1]);
+  auto szz = static_cast(getSigmaZ2()) + static_cast(cov[2]);
+  auto det = sdd * szz - sdz * sdz;
+
+  if (gpu::CAMath::Abs(det) < constants::math::Almost0) {
+    return constants::math::VeryBig;
+  }
+
+  value_t d = this->getY() - p[0];
+  value_t z = this->getZ() - p[1];
+
+  return (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det;
+}
+
+//______________________________________________
+template 
+GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs) const -> value_t
+{
+  MatrixDSym5 cov; // perform matrix operations in double!
+  return getPredictedChi2(rhs, cov);
+}
 
 //______________________________________________
 template 
-void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const
+GPUd() void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackParametrizationWithError& rhs, MatrixDSym5& cov) const
 {
   // fill combined cov.matrix (NOT inverted)
   cov(kY, kY) = static_cast(getSigmaY2()) + static_cast(rhs.getSigmaY2());
@@ -755,24 +1178,16 @@ void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackP
 
 //______________________________________________
 template 
-auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs) const -> value_t
-{
-  MatrixDSym5 cov; // perform matrix operations in double!
-  return getPredictedChi2(rhs, cov);
-}
-
-//______________________________________________
-template 
-auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const -> value_t
+GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const -> value_t
 {
   // get chi2 wrt other track, which must be defined at the same parameters X,alpha
   // Supplied non-initialized covToSet matrix is filled by inverse combined matrix for further use
 
-  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha();
     return 2.f * HugeF;
   }
-  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX();
     return 2.f * HugeF;
   }
@@ -796,16 +1211,16 @@ auto TrackParametrizationWithError::getPredictedChi2(const TrackParamet
 
 //______________________________________________
 template 
-bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv)
+GPUd() bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv)
 {
   // update track with other track, the inverted combined cov matrix should be supplied
 
   // consider skipping this check, since it is usually already done upstream
-  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha();
     return false;
   }
-  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) {
+  if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) {
     LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX();
     return false;
   }
@@ -842,7 +1257,7 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
   }
 
   // updated covariance: Cov0 = Cov0 - K*Cov0
-  matK *= ROOT::Math::SMatrix>(matC0);
+  matK *= o2::math_utils::SMatrix>(matC0);
   mC[kSigY2] -= matK(kY, kY);
   mC[kSigZY] -= matK(kZ, kY);
   mC[kSigZ2] -= matK(kZ, kZ);
@@ -864,7 +1279,7 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
 
 //______________________________________________
 template 
-bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs)
+GPUd() bool TrackParametrizationWithError::update(const TrackParametrizationWithError& rhs)
 {
   // update track with other track
   MatrixDSym5 covI; // perform matrix operations in double!
@@ -876,8 +1291,6 @@ bool TrackParametrizationWithError::update(const TrackParametrizationWi
   return update(rhs, covI);
 }
 
-#endif
-
 //______________________________________________
 template 
 GPUd() bool TrackParametrizationWithError::update(const value_t* p, const value_t* cov)
@@ -962,7 +1375,7 @@ GPUd() value_T TrackParametrizationWithError::update(const o2::dataform
 
 //______________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr, value_t dedx)
+GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x2x0, value_t xrho, bool anglecorr)
 {
   //------------------------------------------------------------------
   // This function corrects the track parameters for the crossed material.
@@ -974,84 +1387,257 @@ GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x
   // "anglecorr" - switch for the angular correction
   //------------------------------------------------------------------
   constexpr value_t kMSConst2 = 0.0136f * 0.0136f;
-  constexpr value_t kMaxELossFrac = 0.3f; // max allowed fractional eloss
-  constexpr value_t kMinP = 0.01f;        // kill below this momentum
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
 
-  value_t& fC22 = mC[kSigSnp2];
-  value_t& fC33 = mC[kSigTgl2];
-  value_t& fC43 = mC[kSigQ2PtTgl];
-  value_t& fC44 = mC[kSigQ2Pt2];
-  //
   value_t csp2 = (1.f - this->getSnp()) * (1.f + this->getSnp()); // cos(phi)^2
   value_t cst2I = (1.f + this->getTgl() * this->getTgl());        // 1/cos(lambda)^2
-  // Apply angle correction, if requested
-  if (anglecorr) {
+  if (anglecorr) {                                                // Apply angle correction, if requested
     value_t angle = gpu::CAMath::Sqrt(cst2I / csp2);
     x2x0 *= angle;
     xrho *= angle;
   }
-  value_t p = this->getP();
-  value_t p2 = p * p;
-  value_t e2 = p2 + this->getPID().getMass2();
-  value_t beta2 = p2 / e2;
+  auto m = this->getPID().getMass();
+  int charge2 = this->getAbsCharge() * this->getAbsCharge();
+  value_t p = this->getP(), p0 = p, p02 = p * p, e2 = p02 + this->getPID().getMass2(), massInv = 1. / m, bg = p * massInv, dETot = 0.;
+  value_t e = gpu::CAMath::Sqrt(e2), e0 = e;
+  if (m > 0 && xrho != 0.f) {
+    value_t ekin = e - m, dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
+    }
+    value_t dE = dedx * xrho;
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
+    }
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - this->getPID().getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
+      }
+    }
+
+    if (p < kMinP) {
+      return false;
+    }
+    dETot = e - e0;
+  } // end of e.loss correction
 
   // Calculating the multiple scattering corrections******************
+  value_t& fC22 = mC[kSigSnp2];
+  value_t& fC33 = mC[kSigTgl2];
+  value_t& fC43 = mC[kSigQ2PtTgl];
+  value_t& fC44 = mC[kSigQ2Pt2];
+  //
   value_t cC22(0.f), cC33(0.f), cC43(0.f), cC44(0.f);
   if (x2x0 != 0.f) {
-    value_t theta2 = kMSConst2 / (beta2 * p2) * gpu::CAMath::Abs(x2x0);
-    if (this->getAbsCharge() != 1) {
-      theta2 *= this->getAbsCharge() * this->getAbsCharge();
+    value_t beta2 = p02 / e2, theta2 = kMSConst2 / (beta2 * p02) * gpu::CAMath::Abs(x2x0);
+    value_t fp34 = this->getTgl();
+    if (charge2 != 1) {
+      theta2 *= charge2;
+      fp34 *= this->getCharge2Pt();
     }
     if (theta2 > constants::math::PI * constants::math::PI) {
       return false;
     }
-    value_t fp34 = this->getTgl() * this->getCharge2Pt();
     value_t t2c2I = theta2 * cst2I;
     cC22 = t2c2I * csp2;
     cC33 = t2c2I * cst2I;
     cC43 = t2c2I * fp34;
     cC44 = theta2 * fp34 * fp34;
-    // optimes this
+    // optimize this
     //    cC22 = theta2*((1.-getSnp())*(1.+getSnp()))*(1. + this->getTgl()*getTgl());
     //    cC33 = theta2*(1. + this->getTgl()*getTgl())*(1. + this->getTgl()*getTgl());
     //    cC43 = theta2*getTgl()*this->getQ2Pt()*(1. + this->getTgl()*getTgl());
     //    cC44 = theta2*getTgl()*this->getQ2Pt()*getTgl()*this->getQ2Pt();
   }
 
-  // Calculating the energy loss corrections************************
-  value_t cP4 = 1.f;
-  if ((xrho != 0.f) && (beta2 < 1.f)) {
-    if (dedx < kCalcdEdxAuto + constants::math::Almost1) { // request to calculate dedx on the fly
-      dedx = BetheBlochSolid(p / this->getPID().getMass());
-      if (this->getAbsCharge() != 1) {
-        dedx *= this->getAbsCharge() * this->getAbsCharge();
+  // the energy loss correction contribution to cov.matrix: approximate energy loss fluctuation (M.Ivanov)
+  constexpr value_t knst = 0.0007f; // To be tuned.
+  value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dETot)) * e0 / p02 * this->getCharge2Pt();
+  cC44 += sigmadE * sigmadE;
+
+  // Applying the corrections*****************************
+  fC22 += cC22;
+  fC33 += cC33;
+  fC43 += cC43;
+  fC44 += cC44;
+  this->setQ2Pt(this->getQ2Pt() * p0 / p);
+
+  checkCovariance();
+
+  return true;
+}
+
+//______________________________________________
+template 
+GPUd() bool TrackParametrizationWithError::correctForMaterial(TrackParametrization& linRef, value_t x2x0, value_t xrho, bool anglecorr)
+{
+  //------------------------------------------------------------------
+  // This function corrects the reference and current track parameters for the crossed material
+  // "x2x0"   - X/X0, the thickness in units of the radiation length.
+  // "xrho" - is the product length*density (g/cm^2).
+  //     It should be passed as negative when propagating tracks
+  //     from the intreaction point to the outside of the central barrel.
+  // "dedx" - mean enery loss (GeV/(g/cm^2), if <=kCalcdEdxAuto : calculate on the fly
+  // "anglecorr" - switch for the angular correction
+  //------------------------------------------------------------------
+  constexpr value_t kMSConst2 = 0.0136f * 0.0136f;
+  constexpr value_t kMinP = 0.01f; // kill below this momentum
+
+  value_t csp2 = (1.f - linRef.getSnp()) * (1.f + linRef.getSnp()); // cos(phi)^2
+  value_t cst2I = (1.f + linRef.getTgl() * linRef.getTgl());        // 1/cos(lambda)^2
+  if (anglecorr) {                                                  // Apply angle correction, if requested
+    value_t angle = gpu::CAMath::Sqrt(cst2I / csp2);
+    x2x0 *= angle;
+    xrho *= angle;
+  }
+  auto pid = linRef.getPID();
+  auto m = pid.getMass();
+  int charge2 = linRef.getAbsCharge() * linRef.getAbsCharge();
+  value_t p = linRef.getP(), p0 = p, p02 = p * p, e2 = p02 + pid.getMass2(), massInv = 1. / m, bg = p * massInv, dETot = 0.;
+  value_t e = gpu::CAMath::Sqrt(e2), e0 = e;
+  if (m > 0 && xrho != 0.f) {
+    value_t ekin = e - m, dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+    value_t dedxDer = 0., dedx1 = dedx;
+#endif
+    if (charge2 != 1) {
+      dedx *= charge2;
+    }
+    value_t dE = dedx * xrho;
+    int na = 1 + int(gpu::CAMath::Abs(dE) / ekin * ELoss2EKinThreshInv);
+    if (na > MaxELossIter) {
+      na = MaxELossIter;
+    }
+    if (na > 1) {
+      dE /= na;
+      xrho /= na;
+#ifdef _BB_NONCONST_CORR_
+      dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx1, bg); // require correction for non-constantness of dedx vs betagamma
+      if (charge2 != 1) {
+        dedxDer *= charge2;
+      }
+#endif
+    }
+    while (na--) {
+#ifdef _BB_NONCONST_CORR_
+      if (dedxDer != 0.) { // correction for non-constantness of dedx vs beta*gamma (in linear approximation): for a single step dE -> dE * [(exp(dedxDer) - 1)/dedxDer]
+        if (xrho < 0) {
+          dedxDer = -dedxDer; // E.loss ( -> positive derivative)
+        }
+        auto corrC = (gpu::CAMath::Exp(dedxDer) - 1.) / dedxDer;
+        dE *= corrC;
+      }
+#endif
+      e += dE;
+      if (e > m) { // stopped
+        p = gpu::CAMath::Sqrt(e * e - pid.getMass2());
+      } else {
+        return false;
+      }
+      if (na) {
+        bg = p * massInv;
+        dedx = this->getdEdxBBOpt(bg);
+#ifdef _BB_NONCONST_CORR_
+        dedxDer = this->getBetheBlochSolidDerivativeApprox(dedx, bg);
+#endif
+        if (charge2 != 1) {
+          dedx *= charge2;
+#ifdef _BB_NONCONST_CORR_
+          dedxDer *= charge2;
+#endif
+        }
+        dE = dedx * xrho;
       }
     }
 
-    value_t dE = dedx * xrho;
-    value_t e = gpu::CAMath::Sqrt(e2);
-    if (gpu::CAMath::Abs(dE) > kMaxELossFrac * e) {
-      return false; // 30% energy loss is too much!
+    if (p < kMinP) {
+      return false;
+    }
+    dETot = e - e0;
+  } // end of e.loss correction
+
+  // Calculating the multiple scattering corrections******************
+  value_t& fC22 = mC[kSigSnp2];
+  value_t& fC33 = mC[kSigTgl2];
+  value_t& fC43 = mC[kSigQ2PtTgl];
+  value_t& fC44 = mC[kSigQ2Pt2];
+  //
+  value_t cC22(0.f), cC33(0.f), cC43(0.f), cC44(0.f);
+  if (x2x0 != 0.f) {
+    value_t beta2 = p02 / e2, theta2 = kMSConst2 / (beta2 * p02) * gpu::CAMath::Abs(x2x0);
+    value_t fp34 = linRef.getTgl();
+    if (charge2 != 1) {
+      theta2 *= charge2;
+      fp34 *= linRef.getCharge2Pt();
     }
-    value_t eupd = e + dE;
-    value_t pupd2 = eupd * eupd - this->getPID().getMass2();
-    if (pupd2 < kMinP * kMinP) {
+    if (theta2 > constants::math::PI * constants::math::PI) {
       return false;
     }
-    cP4 = p / gpu::CAMath::Sqrt(pupd2);
-    //
-    // Approximate energy loss fluctuation (M.Ivanov)
-    constexpr value_t knst = 0.07f; // To be tuned.
-    value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dE)) * e / p2 * this->getCharge2Pt();
-    cC44 += sigmadE * sigmadE;
+    value_t t2c2I = theta2 * cst2I;
+    cC22 = t2c2I * csp2;
+    cC33 = t2c2I * cst2I;
+    cC43 = t2c2I * fp34;
+    cC44 = theta2 * fp34 * fp34;
+    // optimize this
+    //    cC22 = theta2*((1.-getSnp())*(1.+getSnp()))*(1. + this->getTgl()*getTgl());
+    //    cC33 = theta2*(1. + this->getTgl()*getTgl())*(1. + this->getTgl()*getTgl());
+    //    cC43 = theta2*getTgl()*this->getQ2Pt()*(1. + this->getTgl()*getTgl());
+    //    cC44 = theta2*getTgl()*this->getQ2Pt()*getTgl()*this->getQ2Pt();
   }
 
+  // the energy loss correction contribution to cov.matrix: approximate energy loss fluctuation (M.Ivanov)
+  constexpr value_t knst = 0.0007f; // To be tuned.
+  value_t sigmadE = knst * gpu::CAMath::Sqrt(gpu::CAMath::Abs(dETot)) * e0 / p02 * linRef.getCharge2Pt();
+  cC44 += sigmadE * sigmadE;
+
   // Applying the corrections*****************************
   fC22 += cC22;
   fC33 += cC33;
   fC43 += cC43;
   fC44 += cC44;
-  this->setQ2Pt(this->getQ2Pt() * cP4);
+  auto pscale = p0 / p;
+  linRef.setQ2Pt(linRef.getQ2Pt() * pscale);
+  this->setQ2Pt(this->getQ2Pt() * pscale);
 
   checkCovariance();
 
@@ -1060,7 +1646,7 @@ GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x
 
 //______________________________________________________________
 template 
-GPUd() bool TrackParametrizationWithError::getCovXYZPxPyPzGlo(gpu::gpustd::array& cv) const
+GPUd() bool TrackParametrizationWithError::getCovXYZPxPyPzGlo(std::array& cv) const
 {
   //---------------------------------------------------------------------
   // This function returns the global covariance matrix of the track params
@@ -1132,6 +1718,18 @@ std::string TrackParametrizationWithError::asString() const
                      mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
                      mC[kSigQ2Pt2]);
 }
+
+template 
+std::string TrackParametrizationWithError::asStringHexadecimal()
+{
+  return TrackParametrization::asStringHexadecimal() +
+         fmt::format(" Cov: [{:x}] [{:x} {:x}] [{:x} {:x} {:x}] [{:x} {:x} {:x} {:x}] [{:x} {:x} {:x} {:x} {:x}]",
+                     reinterpret_cast(mC[kSigY2]), reinterpret_cast(mC[kSigZY]), reinterpret_cast(mC[kSigZ2]),
+                     reinterpret_cast(mC[kSigSnpY]), reinterpret_cast(mC[kSigSnpZ]), reinterpret_cast(mC[kSigSnp2]),
+                     reinterpret_cast(mC[kSigTglY]), reinterpret_cast(mC[kSigTglZ]), reinterpret_cast(mC[kSigTglSnp]),
+                     reinterpret_cast(mC[kSigTgl2]), reinterpret_cast(mC[kSigQ2PtY]), reinterpret_cast(mC[kSigQ2PtZ]),
+                     reinterpret_cast(mC[kSigQ2PtSnp]), reinterpret_cast(mC[kSigQ2PtTgl]), reinterpret_cast(mC[kSigQ2Pt2]));
+}
 #endif
 
 //______________________________________________________________
@@ -1141,24 +1739,41 @@ GPUd() void TrackParametrizationWithError::print() const
   // print parameters
 #ifndef GPUCA_ALIGPUCODE
   printf("%s\n", asString().c_str());
-#else
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
   TrackParametrization::printParam();
   printf(
-    "\n%7s %+.3e\n"
-    "%7s %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e %+.3e\n"
-    "%7s %+.3e %+.3e %+.3e %+.3e %+.3e",
-    "CovMat:", mC[kSigY2], "", mC[kSigZY], mC[kSigZ2], "", mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], "", mC[kSigTglY],
-    mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], "", mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
+    " Cov: [%+.3e] [%+.3e %+.3e] [%+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e %+.3e]\n",
+    mC[kSigY2], mC[kSigZY], mC[kSigZ2], mC[kSigSnpY], mC[kSigSnpZ], mC[kSigSnp2], mC[kSigTglY],
+    mC[kSigTglZ], mC[kSigTglSnp], mC[kSigTgl2], mC[kSigQ2PtY], mC[kSigQ2PtZ], mC[kSigQ2PtSnp], mC[kSigQ2PtTgl],
     mC[kSigQ2Pt2]);
 #endif
 }
 
+//______________________________________________________________
+template 
+GPUd() void TrackParametrizationWithError::printHexadecimal()
+{
+  // print parameters
+#ifndef GPUCA_ALIGPUCODE
+  printf("%s\n", asStringHexadecimal().c_str());
+#elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT))
+  TrackParametrization::printParamHexadecimal();
+  printf(
+    " Cov: [%x] [%x %x] [%x %x %x] [%x %x %x %x] [%x %x %x %x %x]\n",
+    gpu::CAMath::Float2UIntReint(mC[kSigY2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigZY]), gpu::CAMath::Float2UIntReint(mC[kSigZ2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigSnpY]), gpu::CAMath::Float2UIntReint(mC[kSigSnpZ]), gpu::CAMath::Float2UIntReint(mC[kSigSnp2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigTglY]), gpu::CAMath::Float2UIntReint(mC[kSigTglZ]), gpu::CAMath::Float2UIntReint(mC[kSigTglSnp]), gpu::CAMath::Float2UIntReint(mC[kSigTgl2]),
+    gpu::CAMath::Float2UIntReint(mC[kSigQ2PtY]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtZ]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtSnp]), gpu::CAMath::Float2UIntReint(mC[kSigQ2PtTgl]), gpu::CAMath::Float2UIntReint(mC[kSigQ2Pt2]));
+#endif
+}
+
 namespace o2::track
 {
+#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code.
 template class TrackParametrizationWithError;
-#ifndef GPUCA_GPUCODE_DEVICE
+#endif
+#ifndef GPUCA_GPUCODE
 template class TrackParametrizationWithError;
 #endif
 } // namespace o2::track
diff --git a/DataFormats/Reconstruction/src/V0.cxx b/DataFormats/Reconstruction/src/V0.cxx
index ad2c84f499bf8..986d52fed35f6 100644
--- a/DataFormats/Reconstruction/src/V0.cxx
+++ b/DataFormats/Reconstruction/src/V0.cxx
@@ -14,20 +14,18 @@
 using namespace o2::dataformats;
 
 V0::V0(const std::array& xyz, const std::array& pxyz, const std::array& covxyz,
-       const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg,
-       GIndex trPosID, GIndex trNegID, o2::track::PID pid)
-  : mProngIDs{trPosID, trNegID}, mProngs{trPos, trNeg}
+       const o2::track::TrackParCov& trPos, const o2::track::TrackParCov& trNeg, o2::track::PID pid) : mProngs{trPos, trNeg}
 {
-  std::array covV{}, covN{};
-  trPos.getCovXYZPxPyPzGlo(covV);
+  std::array covV{0.}, covP, covN;
+  trPos.getCovXYZPxPyPzGlo(covP);
   trNeg.getCovXYZPxPyPzGlo(covN);
-  for (int i = 0; i < 21; i++) {
-    covV[i] += covN[i];
-  }
+  constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component
   for (int i = 0; i < 6; i++) {
     covV[i] = covxyz[i];
+    covV[MomInd[i]] = covP[MomInd[i]] + covN[MomInd[i]];
   }
   this->set(xyz, pxyz, covV, trPos.getCharge() + trNeg.getCharge(), true, pid);
+  this->checkCorrelations();
 }
 
 float V0::calcMass2(float massPos2, float massNeg2) const
@@ -37,3 +35,36 @@ float V0::calcMass2(float massPos2, float massNeg2) const
   auto energy = std::sqrt(massPos2 + p2pos) + std::sqrt(massNeg2 + p2neg);
   return energy * energy - p2;
 }
+
+float V0::calcAPAlpha() const
+{
+  // calculate Armenteros-Podolanski alpha
+  std::array pP, pN, pV0;
+  float alp = 0.f, pV0tot2 = 0.f;
+  getProng(0).getPxPyPzGlo(pP);
+  getProng(1).getPxPyPzGlo(pN);
+  for (int i = 0; i < 3; i++) {
+    pV0[i] = pP[i] + pN[i];
+    alp += pV0[i] * (pP[i] - pN[i]);
+    pV0tot2 += pV0[i] * pV0[i];
+  }
+  alp /= pV0tot2;
+  return alp;
+}
+
+float V0::calcAPQt() const
+{
+  // calculate Armenteros-Podolanski qt
+  std::array pP, pN, pV0;
+  float pPtot2 = 0.f, pV0tot2 = 0.f, cross = 0.f;
+  getProng(0).getPxPyPzGlo(pP);
+  getProng(1).getPxPyPzGlo(pN);
+  for (int i = 0; i < 3; i++) {
+    pV0[i] = pP[i] + pN[i];
+    pPtot2 += pP[i] * pP[i];
+    pV0tot2 += pV0[i] * pV0[i];
+    cross += pV0[i] * pP[i]; // -> pP * pV0 * cos
+  }
+  float qt = pPtot2 - (cross * cross) / pV0tot2;
+  return qt > 0 ? std::sqrt(qt) : 0;
+}
diff --git a/DataFormats/Reconstruction/src/Vertex.cxx b/DataFormats/Reconstruction/src/Vertex.cxx
index dfbc5eac281e8..85145683ddd97 100644
--- a/DataFormats/Reconstruction/src/Vertex.cxx
+++ b/DataFormats/Reconstruction/src/Vertex.cxx
@@ -10,9 +10,9 @@
 // or submit itself to any jurisdiction.
 
 #include "ReconstructionDataFormats/Vertex.h"
-#include 
 #ifndef GPUCA_NO_FMT
-#include 
+#include 
+#include 
 #endif
 
 namespace o2
@@ -56,5 +56,8 @@ bool VertexBase::operator==(const VertexBase& other) const
 
 #endif
 
+template class o2::dataformats::Vertex>;
+template class o2::dataformats::Vertex>;
+
 } // namespace dataformats
 } // namespace o2
diff --git a/DataFormats/Reconstruction/test/testLTOFIntegration.cxx b/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
index bb65c60d08d18..f737b1df53666 100644
--- a/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
+++ b/DataFormats/Reconstruction/test/testLTOFIntegration.cxx
@@ -33,8 +33,8 @@ BOOST_AUTO_TEST_CASE(TrackLTIntegral)
   const int nStep = 100;
   const float dx2x0 = 0.01f;
   for (int i = 0; i < nStep; i++) {
-    lt.addStep(1., trc.getP2Inv());
-    lt1.addStep(1., trc1.getP2Inv());
+    lt.addStep(1., trc.getQ2P2());
+    lt1.addStep(1., trc1.getQ2P2());
     lt1.addX2X0(dx2x0);
   }
   trc.printParam();
diff --git a/DataFormats/common/CMakeLists.txt b/DataFormats/common/CMakeLists.txt
index 0e0e4076eb000..6b0081a920c8d 100644
--- a/DataFormats/common/CMakeLists.txt
+++ b/DataFormats/common/CMakeLists.txt
@@ -16,6 +16,7 @@ o2_add_library(CommonDataFormat
                        src/FlatHisto2D.cxx
                        src/AbstractRefAccessor.cxx
                        src/TFIDInfo.cxx
+                       src/TimeStamp.cxx
                PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::GPUCommon
                                      ROOT::Core O2::MathUtils Microsoft.GSL::GSL)
 
diff --git a/DataFormats/common/include/CommonDataFormat/AbstractRef.h b/DataFormats/common/include/CommonDataFormat/AbstractRef.h
index cdc65f6c412d6..72c195cfb7bc8 100644
--- a/DataFormats/common/include/CommonDataFormat/AbstractRef.h
+++ b/DataFormats/common/include/CommonDataFormat/AbstractRef.h
@@ -18,11 +18,12 @@
 
 #include "GPUCommonDef.h"
 #include "GPUCommonRtypes.h"
-#include "GPUCommonTypeTraits.h"
+#ifndef GPUCA_GPUCODE_DEVICE
+#include 
+#endif
 
-namespace o2
-{
-namespace dataformats
+
+namespace o2::dataformats
 {
 
 template 
@@ -32,8 +33,19 @@ class AbstractRef
   static constexpr auto MVAR()
   {
     static_assert(NBIT <= 64, "> 64 bits not supported");
-    typename std::conditional<(NBIT > 32), uint64_t, typename std::conditional<(NBIT > 16), uint32_t, typename std::conditional<(NBIT > 8), uint16_t, uint8_t>::type>::type>::type tp = 0;
-    return tp;
+    if constexpr (NBIT > 32) {
+      uint64_t tp = 0;
+      return tp;
+    } else if constexpr (NBIT > 16) {
+      uint32_t tp = 0;
+      return tp;
+    } else if constexpr (NBIT > 8) {
+      uint16_t tp = 0;
+      return tp;
+    } else {
+      uint8_t tp = 0;
+      return tp;
+    }
   }
 
  public:
@@ -79,6 +91,7 @@ class AbstractRef
 
   GPUdi() bool operator==(const AbstractRef& o) const { return getRawWOFlags() == o.getRawWOFlags(); }
   GPUdi() bool operator!=(const AbstractRef& o) const { return !operator==(o); }
+  GPUdi() void clear() { setRaw((Base_t(SrcMask & ((0x1 << NBSrc) - 1)) << NBIdx) | Base_t(IdxMask & ((0x1 << NBIdx) - 1))); }
 
  protected:
   Base_t mRef = IdxMask | (SrcMask << NBIdx); // packed reference, dummy by default
@@ -86,7 +99,6 @@ class AbstractRef
   ClassDefNV(AbstractRef, 1);
 };
 
-} // namespace dataformats
-} // namespace o2
+} // namespace o2::dataformats
 
 #endif
diff --git a/DataFormats/common/include/CommonDataFormat/BunchFilling.h b/DataFormats/common/include/CommonDataFormat/BunchFilling.h
index 182a665532668..f11ce2498d04b 100644
--- a/DataFormats/common/include/CommonDataFormat/BunchFilling.h
+++ b/DataFormats/common/include/CommonDataFormat/BunchFilling.h
@@ -107,7 +107,6 @@ class BunchFilling
 
   ClassDefNV(BunchFilling, 2);
 };
-} // namespace o2
 
 namespace framework
 {
@@ -118,5 +117,6 @@ struct is_messageable : std::true_type {
 };
 
 } // namespace framework
+} // namespace o2
 
 #endif
diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
index ac91eafc4d147..f8d49042dd8c5 100644
--- a/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
+++ b/DataFormats/common/include/CommonDataFormat/FlatHisto1D.h
@@ -34,7 +34,7 @@ namespace dataformats
   Fast 1D histo class which can be messages as
   FlatHisto1D histo(nbins, xmin, xmax);
   histo.fill(...);
-  pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase());
+  pc.outputs().snapshot(Output{"Origin", "Desc", 0}, histo.getBase());
 
   and received (read only!) as
   const auto hdata = pc.inputs().get>("histodata");
diff --git a/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
index 9102d53edeaaa..47793618c0649 100644
--- a/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
+++ b/DataFormats/common/include/CommonDataFormat/FlatHisto2D.h
@@ -35,7 +35,7 @@ namespace dataformats
   Fast 2D histo class which can be messages as
   FlatHisto2D histo(nbinsX, xmin, xmax, nbinsY, ymin, ymax);
   histo.fill(...);
-  pc.outputs().snapshot(Output{"Origin", "Desc", 0, Lifetime::Timeframe}, histo.getBase());
+  pc.outputs().snapshot(Output{"Origin", "Desc", 0}, histo.getBase());
 
   and received (read only!) as
   const auto hdata = pc.inputs().get>("histodata");
diff --git a/DataFormats/common/include/CommonDataFormat/IRFrame.h b/DataFormats/common/include/CommonDataFormat/IRFrame.h
index 26b1e33f17e50..d404a9538306a 100644
--- a/DataFormats/common/include/CommonDataFormat/IRFrame.h
+++ b/DataFormats/common/include/CommonDataFormat/IRFrame.h
@@ -28,9 +28,22 @@ namespace dataformats
 // We could just alias it to the bracket specialization, but this would create
 // problems with fwd.declaration
 struct IRFrame : public o2::math_utils::detail::Bracket {
+  static constexpr uint64_t LastIRFrame = uint64_t(0x1) << 63;
+
   using o2::math_utils::detail::Bracket::Bracket;
 
-  uint64_t info = 0;
+  uint64_t info = uint64_t(0);
+
+  void setLast(bool v = true)
+  {
+    if (v) {
+      info |= LastIRFrame;
+    } else {
+      v &= ~LastIRFrame;
+    }
+  }
+
+  bool isLast() const { return (info & LastIRFrame) != 0UL; }
 
   ClassDefNV(IRFrame, 2);
 };
diff --git a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
index c96b7745079ac..e99f338a16343 100644
--- a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
+++ b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h
@@ -71,9 +71,9 @@ struct InteractionRecord {
 
   static int ns2bc(double ns, unsigned int& orb)
   {
-    orb = ns > 0 ? ns / o2::constants::lhc::LHCOrbitNS : 0;
-    ns -= orb * o2::constants::lhc::LHCOrbitNS;
-    return std::round(ns / o2::constants::lhc::LHCBunchSpacingNS);
+    long nb = std::round(ns / o2::constants::lhc::LHCBunchSpacingNS);
+    orb = nb / o2::constants::lhc::LHCMaxBunches;
+    return nb % o2::constants::lhc::LHCMaxBunches;
   }
 
   double bc2ns() const
@@ -109,7 +109,7 @@ struct InteractionRecord {
 
   float differenceInBCMUS(const InteractionRecord& other) const
   {
-    // return difference in bunch-crossings in ms
+    // return difference in bunch-crossings in us
     return differenceInBC(other) * o2::constants::lhc::LHCBunchSpacingMUS;
   }
 
@@ -281,7 +281,7 @@ struct InteractionRecord {
     return tmp;
   }
 
-#ifndef GPUCA_ALIGPUCODE
+#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE)
   void print() const;
   std::string asString() const;
   friend std::ostream& operator<<(std::ostream& stream, InteractionRecord const& ir);
@@ -359,7 +359,7 @@ struct InteractionTimeRecord : public InteractionRecord {
     return !((*this) > other);
   }
 
-#ifndef GPUCA_ALIGPUCODE
+#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE)
   void print() const;
   std::string asString() const;
   friend std::ostream& operator<<(std::ostream& stream, InteractionTimeRecord const& ir);
diff --git a/DataFormats/common/include/CommonDataFormat/RangeReference.h b/DataFormats/common/include/CommonDataFormat/RangeReference.h
index 0308d3b8af937..3d0c58298de03 100644
--- a/DataFormats/common/include/CommonDataFormat/RangeReference.h
+++ b/DataFormats/common/include/CommonDataFormat/RangeReference.h
@@ -29,23 +29,23 @@ template 
 class RangeReference
 {
  public:
-  GPUd() RangeReference(FirstEntry ent, NElem n) { set(ent, n); }
-  GPUdDefault() RangeReference(const RangeReference& src) = default;
-  GPUdDefault() RangeReference() = default;
-  GPUdDefault() ~RangeReference() = default;
-  GPUd() void set(FirstEntry ent, NElem n)
+  GPUhd() RangeReference(FirstEntry ent, NElem n) { set(ent, n); }
+  GPUhdDefault() RangeReference(const RangeReference& src) = default;
+  GPUhdDefault() RangeReference() = default;
+  GPUhdDefault() ~RangeReference() = default;
+  GPUhd() void set(FirstEntry ent, NElem n)
   {
     mFirstEntry = ent;
     mEntries = n;
   }
-  GPUd() void clear() { set(0, 0); }
-  GPUd() FirstEntry getFirstEntry() const { return mFirstEntry; }
-  GPUd() FirstEntry getEntriesBound() const { return mFirstEntry + mEntries; }
-  GPUd() NElem getEntries() const { return mEntries; }
-  GPUd() void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; }
-  GPUd() void setEntries(NElem n) { mEntries = n; }
-  GPUd() void changeEntriesBy(NElem inc) { mEntries += inc; }
-  GPUd() bool operator==(const RangeReference& other) const
+  GPUhd() void clear() { set(0, 0); }
+  GPUhd() FirstEntry getFirstEntry() const { return mFirstEntry; }
+  GPUhd() FirstEntry getEntriesBound() const { return mFirstEntry + mEntries; }
+  GPUhd() NElem getEntries() const { return mEntries; }
+  GPUhd() void setFirstEntry(FirstEntry ent) { mFirstEntry = ent; }
+  GPUhd() void setEntries(NElem n) { mEntries = n; }
+  GPUhd() void changeEntriesBy(NElem inc) { mEntries += inc; }
+  GPUhd() bool operator==(const RangeReference& other) const
   {
     return mFirstEntry == other.mFirstEntry && mEntries == other.mEntries;
   }
@@ -68,21 +68,21 @@ class RangeRefComp
   static constexpr Base MaskN = ((0x1 << NBitsN) - 1);
   static constexpr Base MaskR = (~Base(0)) & (~MaskN);
   Base mData = 0; ///< packed 1st entry reference + N entries
-  GPUd() void sanityCheck()
+  GPUhd() void sanityCheck()
   {
     static_assert(NBitsN < NBitsTotal, "NBitsN too large");
   }
 
  public:
-  GPUd() RangeRefComp(int ent, int n) { set(ent, n); }
-  GPUdDefault() RangeRefComp() = default;
-  GPUdDefault() RangeRefComp(const RangeRefComp& src) = default;
+  GPUhd() RangeRefComp(int ent, int n) { set(ent, n); }
+  GPUhdDefault() RangeRefComp() = default;
+  GPUhdDefault() RangeRefComp(const RangeRefComp& src) = default;
   GPUhd() void set(int ent, int n)
   {
     mData = (Base(ent) << NBitsN) + (Base(n) & MaskN);
   }
-  GPUd() static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; }
-  GPUd() static constexpr Base getMaxEntries() { return MaskN; }
+  GPUhd() static constexpr Base getMaxFirstEntry() { return MaskR >> NBitsN; }
+  GPUhd() static constexpr Base getMaxEntries() { return MaskN; }
   GPUhd() int getFirstEntry() const { return mData >> NBitsN; }
   GPUhd() int getEntries() const { return mData & ((0x1 << NBitsN) - 1); }
   GPUhd() int getEntriesBound() const { return getFirstEntry() + getEntries(); }
diff --git a/DataFormats/common/include/CommonDataFormat/TimeStamp.h b/DataFormats/common/include/CommonDataFormat/TimeStamp.h
index c4732523d6e93..56a71414c6b86 100644
--- a/DataFormats/common/include/CommonDataFormat/TimeStamp.h
+++ b/DataFormats/common/include/CommonDataFormat/TimeStamp.h
@@ -25,10 +25,10 @@ template 
 class TimeStamp
 {
  public:
-  GPUhdDefault() TimeStamp() CON_DEFAULT;
-  GPUhdDefault() ~TimeStamp() CON_DEFAULT;
+  GPUhdDefault() TimeStamp() = default;
+  GPUhdDefault() ~TimeStamp() = default;
   GPUdi() TimeStamp(T time) { mTimeStamp = time; }
-  GPUdi() T getTimeStamp() const { return mTimeStamp; }
+  GPUhdi() T getTimeStamp() const { return mTimeStamp; }
   GPUdi() void setTimeStamp(T t) { mTimeStamp = t; }
   GPUdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; }
 
@@ -44,6 +44,7 @@ class TimeStampWithError : public TimeStamp
   GPUdDefault() TimeStampWithError() = default;
   GPUd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {}
   GPUdi() E getTimeStampError() const { return mTimeStampError; }
+  GPUdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; }
   GPUdi() void setTimeStampError(E te) { mTimeStampError = te; }
 
  private:
diff --git a/DataFormats/common/src/CommonDataFormatLinkDef.h b/DataFormats/common/src/CommonDataFormatLinkDef.h
index 631305cd28f13..d66e89af637cc 100644
--- a/DataFormats/common/src/CommonDataFormatLinkDef.h
+++ b/DataFormats/common/src/CommonDataFormatLinkDef.h
@@ -26,10 +26,12 @@
 #pragma link C++ class o2::dataformats::TimeStamp < float> + ;
 #pragma link C++ class o2::dataformats::TimeStamp < double> + ;
 #pragma link C++ class o2::dataformats::TimeStamp < int> + ;
-#pragma link C++ class o2::dataformats::TimeStamp < Float16_t > + ;
+#pragma link C++ class o2::dataformats::TimeStamp < uint32_t> + ;
+#pragma link C++ class o2::dataformats::TimeStamp < Float16_t> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < float, float> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < double, double> + ;
 #pragma link C++ class o2::dataformats::TimeStampWithError < int, int> + ;
+#pragma link C++ class o2::dataformats::TimeStampWithError < uint32_t, uint16_t> + ;
 
 #pragma link C++ class o2::dataformats::EvIndex < int, int> + ;
 #pragma link C++ class o2::dataformats::RangeReference < int, int> + ;
diff --git a/DataFormats/common/src/FlatHisto1D.cxx b/DataFormats/common/src/FlatHisto1D.cxx
index 1313da72da8b7..8e04c2c58614e 100644
--- a/DataFormats/common/src/FlatHisto1D.cxx
+++ b/DataFormats/common/src/FlatHisto1D.cxx
@@ -40,9 +40,10 @@ FlatHisto1D& FlatHisto1D::operator=(const FlatHisto1D& rhs)
   if (this == &rhs) {
     return *this;
   }
-  if (!rhs.canFill()) {
+  if (!rhs.canFill() && rhs.getNBins()) { // was initialized
     throw std::runtime_error("trying to copy read-only histogram");
-  } else {
+  }
+  if (rhs.getNBins()) {
     mContainer = rhs.mContainer;
     init(gsl::span(mContainer.data(), mContainer.size()));
   }
diff --git a/DataFormats/common/src/FlatHisto2D.cxx b/DataFormats/common/src/FlatHisto2D.cxx
index b06241ff8d193..aa064eee10bca 100644
--- a/DataFormats/common/src/FlatHisto2D.cxx
+++ b/DataFormats/common/src/FlatHisto2D.cxx
@@ -43,9 +43,10 @@ FlatHisto2D& FlatHisto2D::operator=(const FlatHisto2D& rhs)
   if (this == &rhs) {
     return *this;
   }
-  if (!rhs.canFill()) {
+  if (!rhs.canFill() && rhs.getNBins()) { // was initialized
     throw std::runtime_error("trying to copy read-only histogram");
-  } else {
+  }
+  if (rhs.getNBins()) {
     mContainer = rhs.mContainer;
     init(gsl::span(mContainer.data(), mContainer.size()));
   }
diff --git a/DataFormats/common/src/TimeStamp.cxx b/DataFormats/common/src/TimeStamp.cxx
new file mode 100644
index 0000000000000..2681c9721ba42
--- /dev/null
+++ b/DataFormats/common/src/TimeStamp.cxx
@@ -0,0 +1,19 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#include "CommonDataFormat/TimeStamp.h"
+
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStamp;
+template class o2::dataformats::TimeStampWithError;
+template class o2::dataformats::TimeStampWithError;
+template class o2::dataformats::TimeStampWithError;
diff --git a/DataFormats/simulation/CMakeLists.txt b/DataFormats/simulation/CMakeLists.txt
index d7d048879a7a9..33c91337c77e9 100644
--- a/DataFormats/simulation/CMakeLists.txt
+++ b/DataFormats/simulation/CMakeLists.txt
@@ -26,6 +26,7 @@ o2_add_library(SimulationDataFormat
                                      O2::DetectorsCommonDataFormats
                                      O2::DataFormatsParameters
                                      O2::DataFormatsCalibration
+                                     O2::DataFormatsCTP
                                      O2::GPUCommon
                                      ROOT::TreePlayer)
 
@@ -54,6 +55,11 @@ o2_target_root_dictionary(
 # * src/SimulationDataLinkDef.h
 # * and not src/SimulationDataFormatLinkDef.h
 
+o2_add_test(InteractionSampler
+            SOURCES test/testInteractionSampler.cxx
+            COMPONENT_NAME SimulationDataFormat
+            PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
+
 o2_add_test(BasicHits
             SOURCES test/testBasicHits.cxx
             COMPONENT_NAME SimulationDataFormat
@@ -83,3 +89,8 @@ o2_add_test(MCGenStatus
             SOURCES test/testMCGenStatus.cxx
             COMPONENT_NAME SimulationDataFormat
             PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
+
+o2_add_test(MCGenId
+            SOURCES test/testMCGenId.cxx
+            COMPONENT_NAME SimulationDataFormat
+            PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat)
diff --git a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
index b9ed356ec8b5a..d1e1ee357c1cf 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/BaseHits.h
@@ -49,13 +49,13 @@ class BasicXYZVHit : public BaseHit
   math_utils::Point3D mPos; // cartesian position of Hit
   E mTime;                     // time of flight
   V mHitValue;                 // hit value
-  short mDetectorID;           // the detector/sensor id
+  unsigned short mDetectorID;  // the detector/sensor id
 
  public:
   BasicXYZVHit() = default; // for ROOT IO
 
   // constructor
-  BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, short did)
+  BasicXYZVHit(T x, T y, T z, E time, V val, int trackid, unsigned short did)
     : mPos(x, y, z), mTime(time), mHitValue(val), BaseHit(trackid), mDetectorID(did)
   {
   }
@@ -70,12 +70,12 @@ class BasicXYZVHit : public BaseHit
   // getting the time
   E GetTime() const { return mTime; }
   // get detector + track information
-  short GetDetectorID() const { return mDetectorID; }
+  unsigned short GetDetectorID() const { return mDetectorID; }
 
   // modifiers
   void SetTime(E time) { mTime = time; }
   void SetHitValue(V val) { mHitValue = val; }
-  void SetDetectorID(short detID) { mDetectorID = detID; }
+  void SetDetectorID(unsigned short detID) { mDetectorID = detID; }
   void SetX(T x) { mPos.SetX(x); }
   void SetY(T y) { mPos.SetY(y); }
   void SetZ(T z) { mPos.SetZ(z); }
@@ -87,7 +87,7 @@ class BasicXYZVHit : public BaseHit
   }
   void SetPos(math_utils::Point3D const& p) { mPos = p; }
 
-  ClassDefNV(BasicXYZVHit, 1);
+  ClassDefNV(BasicXYZVHit, 2);
 };
 
 // Class for a hit containing energy loss as hit value
diff --git a/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
index 0e76cef50f5f6..72c66c863f698 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/ConstMCTruthContainer.h
@@ -139,7 +139,7 @@ class ConstMCTruthContainerView
   {
     (void)0;
   }
-  ConstMCTruthContainerView(const ConstMCTruthContainerView&) = default;
+  // ConstMCTruthContainerView(const ConstMCTruthContainerView&) = default;
 
   // const data access
   // get individual const "view" container for a given data index
diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
index 783884615df05..0dc3806e52cf2 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
@@ -23,6 +23,7 @@
 #include 
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -81,6 +82,9 @@ class DigitizationContext
   void setMuPerBC(float m) { mMuBC = m; }
   float getMuPerBC() const { return mMuBC; }
 
+  /// returns the main (hadronic interaction rate) associated to this digitization context
+  float getCalculatedInteractionRate() const { return getMuPerBC() * getBunchFilling().getNBunches() * o2::constants::lhc::LHCRevFreq; }
+
   void printCollisionSummary(bool withQED = false, int truncateOutputTo = -1) const;
 
   // we need a method to fill the file names
@@ -109,6 +113,11 @@ class DigitizationContext
   /// Check collision parts for vertex consistency.
   bool checkVertexCompatibility(bool verbose = false) const;
 
+  /// retrieves collision context for a single timeframe-id (which may be needed by simulation)
+  /// (Only copies collision context without QED information. This can be added to the result with the fillQED method
+  ///  in a second step. Takes as input a timeframe indices collection)
+  DigitizationContext extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset);
+
   /// function reading the hits from a chain (previously initialized with initSimChains
   /// The hits pointer will be initialized (what to we do about ownership??)
   template 
@@ -121,16 +130,23 @@ class DigitizationContext
   /// returns the GRP object associated to this context
   o2::parameters::GRPObject const& getGRP() const;
 
-  // apply collision number cuts and potential relabeling of eventID
-  void applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl);
+  // apply collision number cuts and potential relabeling of eventID, (keeps collisions which fall into the orbitsEarly range for the next timeframe)
+  // needs a timeframe index structure (determined by calcTimeframeIndices), which is adjusted during the process to reflect the filtering
+  void applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly = 0.);
 
-  // finalize timeframe structure (fixes the indices in mTimeFrameStartIndex)
-  void finalizeTimeframeStructure(long startOrbit, long orbitsPerTF);
+  /// get timeframe structure --> index markers where timeframe starts/ends/is_influenced_by
+  std::vector> calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly = 0.) const;
 
   // Sample and fix interaction vertices (according to some distribution). Makes sure that same event ids
   // have to have same vertex, as well as event ids associated to same collision.
   void sampleInteractionVertices(o2::dataformats::MeanVertexObject const& v);
 
+  // Function allowing to inject interaction vertixes from the outside.
+  // Useful when this is given from data for instance. The vertex vector needs to be of same
+  // size as the interaction record.
+  // Returns 0 if success. 1 if there is a problem.
+  int setInteractionVertices(std::vector> const& vertices);
+
   // helper functions to save and load a context
   void saveToFile(std::string_view filename) const;
 
@@ -141,6 +157,20 @@ class DigitizationContext
 
   static DigitizationContext* loadFromFile(std::string_view filename = "");
 
+  void setCTPDigits(std::vector const* ctpdigits) const
+  {
+    mCTPTrigger = ctpdigits;
+    if (mCTPTrigger) {
+      mHasTrigger = true;
+    }
+  }
+
+  void setDigitizerInteractionRate(float intRate) { mDigitizerInteractionRate = intRate; }
+  float getDigitizerInteractionRate() const { return mDigitizerInteractionRate; }
+
+  std::vector const* getCTPDigits() const { return mCTPTrigger; }
+  bool hasTriggerInput() const { return mHasTrigger; }
+
  private:
   int mNofEntries = 0;
   int mMaxPartNumber = 0; // max number of parts in any given collision
@@ -152,24 +182,29 @@ class DigitizationContext
   // for each collision we record the constituents (which shall not exceed mMaxPartNumber)
   std::vector> mEventParts;
 
-  // for each collision we may record/fix the interaction vertex (to be used in event generation)
+  // for each collisionstd::vector> &timeframeindice we may record/fix the interaction vertex (to be used in event generation)
   std::vector> mInteractionVertices;
 
-  // the collision records _with_ QED interleaved;
+  // the collision records **with** QED interleaved;
   std::vector mEventRecordsWithQED;
   std::vector> mEventPartsWithQED;
 
-  // timeframe structure
-  std::vector> mTimeFrameStartIndex;    // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords
-  std::vector> mTimeFrameStartIndexQED; // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords (QED version)
-
   o2::BunchFilling mBCFilling; // pattern of active BCs
 
   std::vector mSimPrefixes;             // identifiers to the hit sim products; the key corresponds to the source ID of event record
   std::string mQEDSimPrefix;                         // prefix for QED production/contribution
   mutable o2::parameters::GRPObject* mGRP = nullptr; //!
 
-  ClassDefNV(DigitizationContext, 4);
+  mutable std::vector const* mCTPTrigger = nullptr; // CTP trigger info associated to this digitization context
+  mutable bool mHasTrigger = false;                                    //
+
+  // The global ALICE interaction hadronic interaction rate as applied in digitization.
+  // It should be consistent with mMuPerBC but it might be easier to handle.
+  // The value will be filled/inserted by the digitization workflow so that digiters can access it.
+  // There is no guarantee that the value is available elsewhere.
+  float mDigitizerInteractionRate{-1};
+
+  ClassDefNV(DigitizationContext, 6);
 };
 
 /// function reading the hits from a chain (previously initialized with initSimChains
@@ -180,12 +215,19 @@ inline void DigitizationContext::retrieveHits(std::vector const& chains
                                               int entryID,
                                               std::vector* hits) const
 {
+  if (chains.size() <= sourceID) {
+    return;
+  }
   auto br = chains[sourceID]->GetBranch(brname);
   if (!br) {
     LOG(error) << "No branch found with name " << brname;
     return;
   }
   br->SetAddress(&hits);
+  auto maxEntries = br->GetEntries();
+  if (maxEntries) {
+    entryID %= maxEntries;
+  }
   br->GetEntry(entryID);
 }
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
index 13fe099aa344a..47dd4f5e4652d 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h
@@ -22,6 +22,7 @@
 #include "CommonDataFormat/BunchFilling.h"
 #include "CommonConstants/LHCConstants.h"
 #include "MathUtils/RandomRing.h"
+#include 
 
 namespace o2
 {
@@ -67,7 +68,7 @@ class InteractionSampler
   void print() const;
 
  protected:
-  int simulateInteractingBC();
+  virtual int simulateInteractingBC();
   void nextCollidingBC(int n);
 
   o2::math_utils::RandomRing<10000> mBCJumpGenerator;  // generator of random jumps in BC
@@ -89,7 +90,7 @@ class InteractionSampler
 
   static constexpr float DefIntRate = 50e3; ///< default interaction rate
 
-  ClassDefNV(InteractionSampler, 1);
+  ClassDef(InteractionSampler, 1);
 };
 
 //_________________________________________________
@@ -113,6 +114,47 @@ inline void InteractionSampler::nextCollidingBC(int n)
   mIR.bc = mInteractingBCs[mCurrBCIdx];
 }
 
+// Special case of InteractionSampler without actual sampling.
+// Engineers interaction sequence by putting one in each N-th BC with multiplicity mult.
+class FixedSkipBC_InteractionSampler : public InteractionSampler
+{
+
+ public:
+  FixedSkipBC_InteractionSampler(int every_n, int mult) : mEveryN{every_n}, mMultiplicity{mult}, InteractionSampler() {}
+
+ protected:
+  int simulateInteractingBC() override;
+
+ private:
+  int mEveryN;       // the skip number ---> fills every N-th BC in the bunch filling scheme
+  int mMultiplicity; // how many events to put if bc is filled
+  ClassDef(FixedSkipBC_InteractionSampler, 1);
+};
+
+// A version of the interaction sampler which can sample according to non-uniform mu(bc) as
+// observed during data taking.
+class NonUniformMuInteractionSampler : public InteractionSampler
+{
+ public:
+  NonUniformMuInteractionSampler() : InteractionSampler() { mBCIntensityScales.resize(o2::constants::lhc::LHCMaxBunches, 1); }
+  bool setBCIntensityScales(const std::vector& scales_from_vector);
+  bool setBCIntensityScales(const TH1F& scales_from_histo); // initialize scales
+
+  // helper function to determine the scales from a histogram (count from event selection analysis)
+  std::vector determineBCIntensityScalesFromHistogram(const TH1F& scales_from_histo);
+
+  const std::vector& getBCIntensityScales() const { return mBCIntensityScales; }
+
+ protected:
+  int simulateInteractingBC() override;
+  int getBCJump() const;
+
+ private:
+  // non-uniformity
+  std::vector mBCIntensityScales;
+  ClassDef(NonUniformMuInteractionSampler, 1);
+};
+
 } // namespace steer
 } // namespace o2
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
index bdac15ef8e10e..74c47c87f22d5 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCCompLabel.h
@@ -12,7 +12,9 @@
 #ifndef ALICEO2_MCCOMPLABEL_H
 #define ALICEO2_MCCOMPLABEL_H
 
+#include 
 #include "GPUCommonRtypes.h"
+#include 
 
 namespace o2
 {
@@ -127,7 +129,7 @@ class MCCompLabel
   int getEventID() const { return (mLabel >> nbitsTrackID) & maskEvID; }
   int getSourceID() const { return (mLabel >> (nbitsTrackID + nbitsEvID)) & maskSrcID; }
   uint64_t getTrackEventSourceID() const { return static_cast(mLabel & maskFull); }
-  void get(int& trackID, int& evID, int& srcID, bool& fake)
+  void get(int& trackID, int& evID, int& srcID, bool& fake) const
   {
     /// parse label
     trackID = getTrackID();
@@ -137,6 +139,7 @@ class MCCompLabel
   }
 
   void print() const;
+  std::string asString() const;
 
   static constexpr int maxSourceID() { return maskSrcID; }
   static constexpr int maxEventID() { return maskEvID; }
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
index cf323e197392d..efaf46806a89a 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h
@@ -27,6 +27,73 @@ namespace dataformats
 
 class GeneratorHeader;
 
+/** Common keys for information in MC event header */
+struct MCInfoKeys {
+  /** @{
+@name HepMC3 heavy-ion fields */
+  static constexpr const char* impactParameter = "Bimpact";
+  static constexpr const char* nPart = "Npart";
+  static constexpr const char* nPartProjectile = "Npart_proj";
+  static constexpr const char* nPartTarget = "Npart_targ";
+  static constexpr const char* nColl = "Ncoll";
+  static constexpr const char* nCollHard = "Ncoll_hard";
+  static constexpr const char* nCollNNWounded = "NColl_NNw";
+  static constexpr const char* nCollNWoundedN = "NColl_NwN";
+  static constexpr const char* nCollNWoundedNwounded = "NColl_NwNW";
+  static constexpr const char* planeAngle = "eventPsi";
+  static constexpr const char* sigmaInelNN = "sigmaInelNN";
+  static constexpr const char* centrality = "centrality";
+  static constexpr const char* nSpecProjectileProton = "Nspec_proj_p";
+  static constexpr const char* nSpecProjectileNeutron = "Nspec_proj_n";
+  static constexpr const char* nSpecTargetProton = "Nspec_targ_p";
+  static constexpr const char* nSpecTargetNeutron = "Nspec_targ_n";
+  /** @} */
+  /** @{
+@name HepMC3 PDF information
+
+In principle a header can have many of these.  In that case,
+each set should be prefixed with "_" where "" is a
+serial number.
+  */
+  static constexpr const char* pdfParton1Id = "pdf_parton_1_id";
+  static constexpr const char* pdfParton2Id = "pdf_parton_2_id";
+  static constexpr const char* pdfX1 = "pdf_x1";
+  static constexpr const char* pdfX2 = "pdf_x2";
+  static constexpr const char* pdfScale = "pdf_scale";
+  static constexpr const char* pdfXF1 = "pdf_par_x1";
+  static constexpr const char* pdfXF2 = "pdf_par_x2";
+  static constexpr const char* pdfCode1 = "pdf_lhc_1_id";
+  static constexpr const char* pdfCode2 = "pdf_lhc_2_id";
+  /** @} */
+  /** @{
+@name HepMC3 cross-section information
+
+In principle we can have one cross section per weight. In that
+case, each should be post-fixed by "_" where "" is a
+serial number.  These should then matcht possible names of
+weights.
+  */
+  static constexpr const char* acceptedEvents = "accepted_events";
+  static constexpr const char* attemptedEvents = "attempted_events";
+  static constexpr const char* xSection = "cross_section";
+  static constexpr const char* xSectionError = "cross_section_error";
+  /** @} */
+  /** @{
+@name Common fields */
+  static constexpr const char* generator = "generator";
+  static constexpr const char* generatorVersion = "version";
+  static constexpr const char* processName = "processName";
+  static constexpr const char* processCode = "processCode";
+  static constexpr const char* weight = "weight";
+  /** @} */
+  /** @{
+@name Pythia8-specific fields */
+  static constexpr const char* processID = "signal_process_id";
+  static constexpr const char* eventScale = "event_scale";
+  static constexpr const char* mpi = "mpi";
+  /** @} */
+};
+
 /*****************************************************************/
 /*****************************************************************/
 
@@ -75,17 +142,28 @@ class MCEventHeader : public FairMCEventHeader
     return ref;
   };
 
+  void print() const;
+
   /// prints a summary of info keys/types attached to this header
   void printInfo() const
   {
     mEventInfo.print();
   }
 
+  /// inits info fields from another Event header
+  void copyInfoFrom(MCEventHeader const& other)
+  {
+    mEventInfo.copyFrom(other.mEventInfo);
+  }
+
   /** methods **/
   virtual void Reset();
 
   MCEventStats& getMCEventStats() { return mEventStats; }
 
+  void setDetId2HitBitLUT(std::vector const& v) { mDetId2HitBitIndex = v; }
+  std::vector const& getDetId2HitBitLUT() const { return mDetId2HitBitIndex; }
+
   /// create a standalone ROOT file/tree with only the MCHeader branch
   static void extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename);
 
@@ -96,9 +174,10 @@ class MCEventHeader : public FairMCEventHeader
   // store a few global properties that this event
   // had in the current simulation (which can be used quick filtering/searching)
   MCEventStats mEventStats{};
+  std::vector mDetId2HitBitIndex;
   o2::utils::RootSerializableKeyValueStore mEventInfo;
 
-  ClassDefOverride(MCEventHeader, 3);
+  ClassDefOverride(MCEventHeader, 4);
 
 }; /** class MCEventHeader **/
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h b/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h
new file mode 100644
index 0000000000000..1a4f86cc98122
--- /dev/null
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCGenProperties.h
@@ -0,0 +1,132 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#ifndef ALICEO2_SIMDATA_MCGENPROPERTIES_H_
+#define ALICEO2_SIMDATA_MCGENPROPERTIES_H_
+
+namespace o2
+{
+namespace mcgenstatus
+{
+
+// Value to check MCGenStatusEncoding::isEncoded against to decide whether or not the stored value is encoded or basically only the HepMC status code
+// as it used to be
+constexpr unsigned int isEncodedValue{5};
+
+// internal structure to allow convenient manipulation of properties as bits on an int to (dis)entangle HepMC and specific generator status codes
+union MCGenStatusEncoding {
+  MCGenStatusEncoding() : fullEncoding(0) {}
+  MCGenStatusEncoding(int enc) : fullEncoding(enc) {}
+  // To be backward-compatible, only set transport to 1 if hepmc status is 1
+  MCGenStatusEncoding(int hepmcIn, int genIn) : isEncoded(isEncodedValue), hepmc(hepmcIn), gen(genIn), reserved(0) {}
+  int fullEncoding;
+  struct {
+    int hepmc : 9;              // HepMC status code
+    int gen : 10;               // specific generator status code
+    int reserved : 10;          // reserved bits for future usage
+    unsigned int isEncoded : 3; // special bits to check whether or not the fullEncoding is a combination of HepMC and gen status codes
+  };
+};
+
+inline bool isEncoded(MCGenStatusEncoding statusCode)
+{
+  return (statusCode.isEncoded == isEncodedValue);
+}
+
+inline bool isEncoded(int statusCode)
+{
+  return isEncoded(MCGenStatusEncoding(statusCode));
+}
+
+inline int getHepMCStatusCode(MCGenStatusEncoding enc)
+{
+  if (!isEncoded(enc)) {
+    // in this case simply set hepmc code to what was given
+    return enc.fullEncoding;
+  }
+  return enc.hepmc;
+}
+
+inline int getGenStatusCode(MCGenStatusEncoding enc)
+{
+  if (!isEncoded(enc)) {
+    // in this case simply set hepmc code to what was given
+    return enc.fullEncoding;
+  }
+  return enc.gen;
+}
+
+inline int getHepMCStatusCode(int encoded)
+{
+  return getHepMCStatusCode(MCGenStatusEncoding(encoded));
+}
+
+inline int getGenStatusCode(int encoded)
+{
+  return getGenStatusCode(MCGenStatusEncoding(encoded));
+}
+
+} // namespace mcgenstatus
+
+namespace mcgenid
+{
+
+// Define some common properties that can be set for Generators
+class GeneratorProperty
+{
+ public:
+  typedef const char* Property;
+  static constexpr Property GENERATORID{"generator_id"};
+  static constexpr Property GENERATORDESCRIPTION{"generator_description"};
+  static constexpr Property SUBGENERATORID{"subgenerator_id"};
+  static constexpr Property SUBGENERATORDESCRIPTIONMAP{"subgenerator_description_map"};
+};
+
+// internal structure to allow encoding of generator IDs and map different numbers to a single short
+union MCGenIdEncoding {
+  MCGenIdEncoding() : fullEncoding(0) {}
+  MCGenIdEncoding(int enc) : fullEncoding(enc) {}
+  MCGenIdEncoding(int generatorId, int sourceId, int subGeneratorId) : generatorId(generatorId), sourceId(sourceId), subGeneratorId(subGeneratorId) {}
+  short fullEncoding;
+  struct {
+    unsigned short generatorId : 7;    // an additional identifier for a generator which can be set by the user
+    unsigned short sourceId : 4;       // ID used in embedding scenarios
+    unsigned short subGeneratorId : 5; // sub generator ID in case a generator implements some additional logic
+  };
+};
+
+inline short getEncodedGenId(int generatorId, int sourceId, int subGeneratorId = -1)
+{
+
+  return MCGenIdEncoding(generatorId, sourceId, subGeneratorId + 1).fullEncoding;
+}
+
+inline int getGeneratorId(short encoded)
+{
+
+  return static_cast(MCGenIdEncoding(encoded).generatorId);
+}
+
+inline int getSourceId(short encoded)
+{
+  return static_cast(MCGenIdEncoding(encoded).sourceId);
+}
+
+inline int getSubGeneratorId(short encoded)
+{
+  return static_cast(MCGenIdEncoding(encoded).subGeneratorId) - 1;
+}
+
+} // namespace mcgenid
+
+} // namespace o2
+
+#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h b/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h
deleted file mode 100644
index 09b2b51c8e483..0000000000000
--- a/DataFormats/simulation/include/SimulationDataFormat/MCGenStatus.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-#ifndef ALICEO2_SIMDATA_MCGENSTATUS_H_
-#define ALICEO2_SIMDATA_MCGENSTATUS_H_
-
-namespace o2
-{
-namespace mcgenstatus
-{
-
-// Value to check MCGenStatusEncoding::isEncoded against to decide whether or not the stored value is encoded or basically only the HepMC status code
-// as it used to be
-constexpr unsigned int isEncodedValue{5};
-
-// internal structure to allow convenient manipulation of properties as bits on an int to (dis)entangle HepMC and specific generator status codes
-union MCGenStatusEncoding {
-  MCGenStatusEncoding() : fullEncoding(0) {}
-  MCGenStatusEncoding(int enc) : fullEncoding(enc) {}
-  // To be backward-compatible, only set transport to 1 if hepmc status is 1
-  MCGenStatusEncoding(int hepmcIn, int genIn) : isEncoded(isEncodedValue), hepmc(hepmcIn), gen(genIn), reserved(0) {}
-  int fullEncoding;
-  struct {
-    int hepmc : 9;              // HepMC status code
-    int gen : 10;               // specific generator status code
-    int reserved : 10;          // reserved bits for future usage
-    unsigned int isEncoded : 3; // special bits to check whether or not the fullEncoding is a combination of HepMC and gen status codes
-  };
-};
-
-inline bool isEncoded(MCGenStatusEncoding statusCode)
-{
-  return (statusCode.isEncoded == isEncodedValue);
-}
-
-inline bool isEncoded(int statusCode)
-{
-  return isEncoded(MCGenStatusEncoding(statusCode));
-}
-
-inline int getHepMCStatusCode(MCGenStatusEncoding enc)
-{
-  if (!isEncoded(enc)) {
-    // in this case simply set hepmc code to what was given
-    return enc.fullEncoding;
-  }
-  return enc.hepmc;
-}
-
-inline int getGenStatusCode(MCGenStatusEncoding enc)
-{
-  if (!isEncoded(enc)) {
-    // in this case simply set hepmc code to what was given
-    return enc.fullEncoding;
-  }
-  return enc.gen;
-}
-
-inline int getHepMCStatusCode(int encoded)
-{
-  return getHepMCStatusCode(MCGenStatusEncoding(encoded));
-}
-
-inline int getGenStatusCode(int encoded)
-{
-  return getGenStatusCode(MCGenStatusEncoding(encoded));
-}
-
-} // namespace mcgenstatus
-
-} // namespace o2
-
-#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
index 0fc9b932ce7a6..b4cf26b11f82e 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h
@@ -17,7 +17,7 @@
 #define ALICEO2_DATA_MCTRACK_H_
 
 #include "SimulationDataFormat/ParticleStatus.h"
-#include "SimulationDataFormat/MCGenStatus.h"
+#include "SimulationDataFormat/MCGenProperties.h"
 #include "DetectorsCommonDataFormats/DetID.h"
 #include "Rtypes.h"
 #include "SimulationDataFormat/O2DatabasePDG.h"
@@ -27,6 +27,7 @@
 #include "TParticle.h"
 #include "TParticlePDG.h"
 #include "TVector3.h"
+#include 
 
 namespace o2
 {
@@ -45,6 +46,8 @@ template 
 class MCTrackT
 {
  public:
+  static constexpr int NHITBITS = 22; // do not modify this
+
   ///  Default constructor
   MCTrackT();
 
@@ -69,7 +72,7 @@ class MCTrackT
   Int_t GetPdgCode() const { return mPdgCode; }
   Int_t getMotherTrackId() const { return mMotherTrackId; }
   Int_t getSecondMotherTrackId() const { return mSecondMotherTrackId; }
-  bool isPrimary() const { return getProcess() == TMCProcess::kPPrimary; }
+  bool isPrimary() const { return (getProcess() == TMCProcess::kPPrimary) || (getMotherTrackId() < 0 && getSecondMotherTrackId() < 0); }
   bool isSecondary() const { return !isPrimary(); }
   Int_t getFirstDaughterTrackId() const { return mFirstDaughterTrackId; }
   Int_t getLastDaughterTrackId() const { return mLastDaughterTrackId; }
@@ -81,6 +84,11 @@ class MCTrackT
   Double_t GetStartVertexCoordinatesZ() const { return mStartVertexCoordinatesZ; }
   Double_t GetStartVertexCoordinatesT() const { return mStartVertexCoordinatesT; }
 
+  /// production radius squared
+  Double_t R2() const { return Vx() * Vx() + Vy() * Vy(); }
+  /// production radius
+  Double_t R() const { return std::sqrt(R2()); }
+
   /// return mass from PDG Database if known (print message in case cannot look up)
   Double_t GetMass() const;
 
@@ -117,7 +125,7 @@ class MCTrackT
   {
     double mx(mStartVertexMomentumX);
     double my(mStartVertexMomentumY);
-    return (TMath::Pi() + TMath::ATan2(-mx, -my));
+    return (TMath::Pi() + TMath::ATan2(-my, -mx));
   }
 
   Double_t GetEta() const
@@ -131,6 +139,12 @@ class MCTrackT
     }
   }
 
+  Double_t GetTgl() const
+  {
+    auto pT = GetPt();
+    return pT > 1e-6 ? mStartVertexMomentumZ / pT : (GetStartVertexMomentumZ() > 0 ? 999. : -999.);
+  }
+
   Double_t GetTheta() const
   {
     double mz(mStartVertexMomentumZ);
@@ -139,11 +153,11 @@ class MCTrackT
 
   Double_t GetRapidity() const;
 
-  void GetMomentum(TVector3& momentum);
+  void GetMomentum(TVector3& momentum) const;
 
-  void Get4Momentum(TLorentzVector& momentum);
+  void Get4Momentum(TLorentzVector& momentum) const;
 
-  void GetStartVertex(TVector3& vertex);
+  void GetStartVertex(TVector3& vertex) const;
 
   /// Accessors to the hit mask
   Int_t getHitMask() const { return ((PropEncoding)mProp).hitmask; }
@@ -153,24 +167,40 @@ class MCTrackT
   void SetSecondMotherTrackId(Int_t id) { mSecondMotherTrackId = id; }
   void SetFirstDaughterTrackId(Int_t id) { mFirstDaughterTrackId = id; }
   void SetLastDaughterTrackId(Int_t id) { mLastDaughterTrackId = id; }
+
   // set bit indicating that this track
-  // left a hit in detector with id iDet
-  void setHit(Int_t iDet)
+  // left a hit in detector that corresponds to bit iDetBit. This bit is found in
+  // a lookup table.
+  void setHit(Int_t iDetBit)
   {
-    assert(0 <= iDet && iDet < o2::detectors::DetID::nDetectors);
+    assert(0 <= iDetBit && iDetBit < o2::detectors::DetID::nDetectors);
     auto prop = ((PropEncoding)mProp);
-    prop.hitmask |= 1 << iDet;
+    prop.hitmask |= 1 << iDetBit;
     mProp = prop.i;
   }
 
-  // did detector iDet see this track?
-  bool leftTrace(Int_t iDet) const { return (((PropEncoding)mProp).hitmask & (1 << iDet)) > 0; }
+  bool leftTraceGivenBitField(int bit) const
+  {
+    return (((PropEncoding)mProp).hitmask & (1 << bit)) > 0;
+  }
+
+  /// Returns of detector with DetID iDet has seen this track.
+  /// Needs lookup table detIDToBit mapping detectorIDs to bitfields (which is persistified in MCEventHeaders).
+  bool leftTrace(Int_t iDet, std::vector const& detIDtoBit) const
+  {
+    auto bit = detIDtoBit[iDet];
+    if (bit != -1) {
+      return leftTraceGivenBitField(bit);
+    }
+    return false;
+  }
+
   // determine how many detectors "saw" this track
   int getNumDet() const
   {
     int count = 0;
-    for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) {
-      if (leftTrace(i)) {
+    for (auto i = 0; i < NHITBITS; ++i) {
+      if (leftTraceGivenBitField(i)) {
         count++;
       }
     }
@@ -255,9 +285,8 @@ class MCTrackT
     struct {
       int storage : 1;  // encoding whether to store this track to the output
       unsigned int process : 6; // encoding process that created this track (enough to store TMCProcess from ROOT)
-      int hitmask : 21; // encoding hits per detector
-      int reserved1 : 1; // bit reserved for possible future purposes
-      int reserved2 : 1; // bit reserved for possible future purposes
+      int hitmask : NHITBITS;   // encoding hits per detector
+      int reserved1 : 1;        // bit reserved for possible future purposes
       int inhibited : 1; // whether tracking of this was inhibited
       int toBeDone : 1; // whether this (still) needs tracking --> we might more complete information to cover full ParticleStatus space
     };
@@ -275,24 +304,26 @@ template 
 inline Double_t MCTrackT::GetEnergy() const
 {
   const auto mass = GetMass();
-  return std::sqrt(mass * mass + mStartVertexMomentumX * mStartVertexMomentumX +
-                   mStartVertexMomentumY * mStartVertexMomentumY + mStartVertexMomentumZ * mStartVertexMomentumZ);
+  Double_t px = mStartVertexMomentumX;
+  Double_t py = mStartVertexMomentumY;
+  Double_t pz = mStartVertexMomentumZ;
+  return std::sqrt(mass * mass + px * px + py * py + pz * pz);
 }
 
 template 
-inline void MCTrackT::GetMomentum(TVector3& momentum)
+inline void MCTrackT::GetMomentum(TVector3& momentum) const
 {
   momentum.SetXYZ(mStartVertexMomentumX, mStartVertexMomentumY, mStartVertexMomentumZ);
 }
 
 template 
-inline void MCTrackT::Get4Momentum(TLorentzVector& momentum)
+inline void MCTrackT::Get4Momentum(TLorentzVector& momentum) const
 {
   momentum.SetXYZT(mStartVertexMomentumX, mStartVertexMomentumY, mStartVertexMomentumZ, GetEnergy());
 }
 
 template 
-inline void MCTrackT::GetStartVertex(TVector3& vertex)
+inline void MCTrackT::GetStartVertex(TVector3& vertex) const
 {
   vertex.SetXYZ(mStartVertexCoordinatesX, mStartVertexCoordinatesY, mStartVertexCoordinatesZ);
 }
diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h b/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
index d6bdbbe79498e..c54b424eb0238 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/MCUtils.h
@@ -16,8 +16,8 @@
 #ifndef O2_MCUTILS_H
 #define O2_MCUTILS_H
 
+#include 
 #include 
-#include 
 #include 
 #include "TPDGCode.h"
 #include "TParticle.h"
@@ -26,6 +26,7 @@ namespace o2
 {
 namespace mcutils
 {
+
 /// A couple of functions to query on MC tracks ( that needs navigation within the global container
 /// of available tracks. It is a class so as to make it available for interactive ROOT more easily.
 class MCTrackNavigator
@@ -79,7 +80,6 @@ class MCGenHelper
   // Has to be in a class as a static methid. Just in a namespace it doesn't work to use this function in ROOT macros.
   static void encodeParticleStatusAndTracking(TParticle& particle, bool wanttracking = true);
   static void encodeParticleStatusAndTracking(TParticle& particle, int hepmcStatus, int genStatus, bool wanttracking = true);
-
   ClassDefNV(MCGenHelper, 1)
 };
 
diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
index 16d85d337fa3e..23dc30119aa7a 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h
@@ -15,6 +15,7 @@
 
 #ifndef O2_O2DATABASEPDG_H
 #define O2_O2DATABASEPDG_H
+
 #include 
 #include "TDatabasePDG.h"
 #include "TParticlePDG.h"
@@ -37,10 +38,6 @@ class O2DatabasePDG
     auto db = TDatabasePDG::Instance();
     if (!initialized) {
       addALICEParticles(db);
-      if (const char* o2Root = std::getenv("O2_ROOT")) {
-        auto inputExtraPDGs = std::string(o2Root) + "/share/Detectors/gconfig/data/extra_ions_pdg_table.dat";
-        db->ReadPDGTable(inputExtraPDGs.c_str());
-      }
       initialized = true;
     }
     return db;
@@ -48,6 +45,7 @@ class O2DatabasePDG
 
   // adds ALICE particles to a given TDatabasePDG instance
   static void addALICEParticles(TDatabasePDG* db = TDatabasePDG::Instance());
+  static void addParticlesFromExternalFile(TDatabasePDG* db);
 
   // get particle's (if any) mass
   static Double_t MassImpl(TParticlePDG* particle, bool& success)
@@ -61,10 +59,8 @@ class O2DatabasePDG
   }
 
   // determine particle to get mass for based on PDG
-  static Double_t Mass(int pdg, bool& success)
+  static Double_t Mass(int pdg, bool& success, TDatabasePDG* db = O2DatabasePDG::Instance())
   {
-    auto db = Instance();
-
     if (pdg < IONBASELOW || pdg > IONBASEHIGH) {
       // not an ion, return immediately
       return MassImpl(db->GetParticle(pdg), success);
@@ -78,9 +74,10 @@ class O2DatabasePDG
     return MassImpl(db->GetParticle(pdg), success);
   }
 
- private:
-  // private constructor
+  // remove default constructor
   O2DatabasePDG() = delete;
+
+ private:
   static constexpr int IONBASELOW{1000000000};
   static constexpr int IONBASEHIGH{1099999999};
 };
@@ -108,18 +105,23 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   const Double_t khShGev = khSlash * kErg2Gev;
   const Double_t kYear2Sec = 3600 * 24 * 365.25;
 
+  // Heavy-flavour particles
+
+  // χc1(3872) aka X(3872), taken from PDG 2022 (https://pdg.lbl.gov/2022/listings/rpp2022-list-chi-c1-3872.pdf)
+  db->AddParticle("Chi_c1(3872)", "Chi_c1(3872)", 3.87165, kFALSE, 0, 0, "CCBarMeson", 9920443);
+
   //
   // Bottom mesons
   // mass and life-time from PDG
   //
   db->AddParticle("Upsilon(3S)", "Upsilon(3S)", 10.3552, kTRUE,
-                  0, 1, "Bottonium", 200553);
+                  0, 0, "Bottonium", 200553);
 
   // QCD diffractive states
   db->AddParticle("rho_diff0", "rho_diff0", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9900110);
   db->AddParticle("pi_diffr+", "pi_diffr+", 0, kTRUE,
-                  0, 1, "QCD diffr. state", 9900210);
+                  0, 3, "QCD diffr. state", 9900210);
   db->AddParticle("omega_di", "omega_di", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9900220);
   db->AddParticle("phi_diff", "phi_diff", 0, kTRUE,
@@ -129,7 +131,7 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   db->AddParticle("n_diffr0", "n_diffr0", 0, kTRUE,
                   0, 0, "QCD diffr. state", 9902110);
   db->AddParticle("p_diffr+", "p_diffr+", 0, kTRUE,
-                  0, 1, "QCD diffr. state", 9902210);
+                  0, 3, "QCD diffr. state", 9902210);
 
   // From Herwig
   db->AddParticle("PSID    ", " ", 3.7699, kFALSE, 0.0, 0, "meson", 30443);
@@ -229,81 +231,67 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                   0, 0, "Special", kspe + 50);
   db->AddParticle("FeedbackPhoton", "FeedbackPhoton", 0, kFALSE,
                   0, 0, "Special", kspe + 51);
-  db->AddParticle("Lambda1520", "Lambda1520", 1.5195, kFALSE,
-                  0.0156, 0, "Resonance", 3124);
-  db->AddAntiParticle("Lambda1520bar", -3124);
 
   //Hyper nuclei and exotica
   ionCode = 1010010030;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("HyperTriton", "HyperTriton", 2.99131, kFALSE,
+    db->AddParticle("HyperTriton", "HyperTriton", 2.991134, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
 
   ionCode = -1010010030;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.99131, kFALSE,
+    db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.991134, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
 
   //hyper hydrogen 4 ground state
   ionCode = 1010010040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.9226, kFALSE,
+    db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.922434, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
   //anti hyper hydrogen 4 ground state
   ionCode = -1010010040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.9226, kFALSE,
-                    2.5e-15, 3, "Ion", ionCode);
-  }
-  //hyper hydrogen 4 excited state
-  ionCode = 1010010041;
-  if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhydrog4*", "Hyperhydrog4*", 3.9237, kFALSE,
-                    2.5e-15, 3, "Ion", ionCode);
-  }
-  //anti hyper hydrogen 4 excited state
-  ionCode = -1010010041;
-  if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhydrog4*", "AntiHyperhydrog4*", 3.9237, kFALSE,
+    db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.922434, kFALSE,
                     2.5e-15, 3, "Ion", ionCode);
   }
   //hyper helium 4 ground state
   ionCode = 1010020040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.9217, kFALSE,
+    db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.921728, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
   //anti hyper helium 4 ground state
   ionCode = -1010020040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.9217, kFALSE,
+    db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.921728, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
-  //hyper helium 4 excited state
-  ionCode = 1010020041;
+
+  // Lithium 4 ground state
+  ionCode = 1000030040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium4*", "Hyperhelium4*", 3.9231, kFALSE,
-                    2.5e-15, 6, "Ion", ionCode);
+    db->AddParticle("Lithium4", "Lithium4", 3.7513, kFALSE,
+                    0.003, 9, "Ion", ionCode);
   }
-  //anti hyper helium 4 excited state
-  ionCode = -1010020041;
+  // anti Lithium 4 ground state
+  ionCode = -1000030040;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium4*", "AntiHyperhelium4*", 3.9231, kFALSE,
-                    2.5e-15, 6, "Ion", ionCode);
+    db->AddParticle("AntiLithium4", "AntiLithium4", 3.7513, kFALSE,
+                    0.003, 9, "Ion", ionCode);
   }
 
   ionCode = 1010020050;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.841, kFALSE,
+    db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.839961, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
 
   ionCode = -1010020050;
   if (!db->GetParticle(ionCode)) {
-    db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.841, kFALSE,
+    db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.839961, kFALSE,
                     2.5e-15, 6, "Ion", ionCode);
   }
 
@@ -319,6 +307,33 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                     2.5e-15, 6, "Ion", ionCode);
   }
 
+  // 4-Xi-He
+  ionCode = 1120020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("4XiHe", "4XiHe", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode);
+    db->AddAntiParticle("Anti4XiHe", -ionCode);
+  }
+
+  // 4-Xi-H
+  ionCode = 1120010040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("4XiH", "4XiH", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode);
+    db->AddAntiParticle("Anti4XiH", -ionCode);
+  }
+
+  // hyper helium 4 sigma
+  ionCode = 1110020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Hyperhelium4sigma", "Hyperhelium4sigma", 3.995, kFALSE,
+                    2.5e-15, 6, "Ion", ionCode);
+  }
+  // anti-hyper helium 4 sigma
+  ionCode = -1110020040;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("AntiHyperhelium4sigma", "AntiHyperhelium4sigma", 3.995, kFALSE,
+                    2.5e-15, 6, "Ion", ionCode);
+  }
+
   ionCode = 1010000020;
   if (!db->GetParticle(ionCode)) {
     db->AddParticle("LambdaNeutron", "LambdaNeutron", 2.054, kFALSE,
@@ -455,6 +470,60 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
                     0.185, 0, "Resonance", ionCode);
   }
 
+  // Lambda(1405)0
+  ionCode = 102132;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_1405_0", "Lambda_1405_0", 1.405, kFALSE, 0.05, 0, "Resonance", ionCode);
+  }
+  if (!db->GetParticle(-ionCode)) {
+    db->AddParticle("AntiLambda_1405_0", "AntiLambda_1405_0", 1.405, kFALSE, 0.05, 0, "Resonance", -ionCode);
+  }
+
+  // Lambda(1520)0
+  ionCode = 102134;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Lambda_1520_0", "Lambda_1520_0", 1.5195, kFALSE, 0.0156, 0, "Resonance", ionCode);
+  }
+  if (!db->GetParticle(-ionCode)) {
+    db->AddParticle("AntiLambda_1520_0", "AntiLambda_1520_0", 1.5195, kFALSE, 0.0156, 0, "Resonance", -ionCode);
+  }
+
+  // f1 study
+  ionCode = 20223;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f1_1285", "f1_1285", 1.28210, kFALSE, 0.02420, 0, "Resonance", ionCode);
+  }
+  ionCode = 20333;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f1_1420", "f1_1420", 1.42640, kFALSE, 0.05490, 0, "Resonance", ionCode);
+  }
+
+  // glueball hunting
+  ionCode = 225;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f2_1270", "f2_1270", 1.2754, kFALSE, 0.1858, 0, "Resonance", ionCode);
+  }
+  ionCode = 115;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("a2_1320", "a2_1320", 1.3182, kFALSE, 0.1078, 0, "Resonance", ionCode);
+  }
+  ionCode = 10221;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1370", "f0_1370", 1.37, kFALSE, 0.200, 0, "Resonance", ionCode);
+  }
+  ionCode = 9030221;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1500", "f0_1500", 1.500, kFALSE, 0.112, 0, "Resonance", ionCode);
+  }
+  ionCode = 10331;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f0_1710", "f0_1710", 1.710, kFALSE, 0.139, 0, "Resonance", ionCode);
+  }
+  ionCode = 335;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("f2_1525", "f2_1525", 1.525, kFALSE, 0.073, 0, "Resonance", ionCode);
+  }
+
   // Xi-/+ (1820)
   ionCode = 123314;
   if (!db->GetParticle(ionCode)) {
@@ -580,10 +649,47 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
     db->AddParticle("Anti-Theta_c_3100", "Anti-Theta_c_3100", 3.099, kFALSE,
                     83.e-6, 0, "Resonance", ionCode); // same width as D*+ (83 keV)
   }
-  if (!db->GetParticle(-ionCode)) {
-    db->AddParticle("Theta_c_3100", "Theta_c_3100", 3.099, kFALSE,
-                    83.e-6, 0, "Resonance", -ionCode); // same width as D*+ (83 keV)
+  db->AddAntiParticle("Theta_c_3100", -ionCode);
+
+  // Charm resonances not present in PYTHIA (consistent with https://github.com/AliceO2Group/O2DPG/blob/master/MC/config/PWGHF/pythia8/generator/pythia8_charmhadronic_with_decays_DResoTrigger.cfg)
+  // Mesons
+  ionCode = 30433;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s1_Plus_2700", "D*_s1_Plus_2700", 2.714, false, 0.122, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s1_Minus_2700", -ionCode);
+  ionCode = 40433;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s1_Plus_2860", "D*_s1_Plus_2860", 2.859, false, 0.160, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s1_Minus_2860", -ionCode);
+  ionCode = 437;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("D*_s3_Plus_2860", "D*_s3_Plus_2860", 2.860, false, 0.053, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("D*_s3_Minus_2860", -ionCode);
+
+  // Baryons
+  ionCode = 4325;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_Plus_3055", "Xi_c_Plus_3055", 3.0559, false, 0.0078, 3, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Xi_c_Minus_3055", -ionCode);
+  ionCode = 4326;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_Plus_3080", "Xi_c_Plus_3080", 3.0772, false, 0.0036, 3, "Resonance", ionCode);
   }
+  db->AddAntiParticle("Xi_c_Minus_3080", -ionCode);
+  ionCode = 4315;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_0_3055", "Xi_c_0_3055", 3.0590, false, 0.0064, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Xi_c_0_3055", -ionCode);
+  ionCode = 4316;
+  if (!db->GetParticle(ionCode)) {
+    db->AddParticle("Xi_c_0_3080", "Xi_c_0_3080", 3.0799, false, 0.0056, 0, "Resonance", ionCode);
+  }
+  db->AddAntiParticle("Anti-Xi_c_0_3080", -ionCode);
 
   // d*(2380) - dibaryon resonance
 
@@ -603,6 +709,26 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db)
   if (!db->GetParticle(-ionCode)) {
     db->AddParticle("AntiSexaquark", "AntiSexaquark", 2.0, kTRUE, 0.0, 0, "Special", -ionCode);
   }
+
+  // lastly, add particle from the the extra text file
+  addParticlesFromExternalFile(db);
+}
+
+inline void O2DatabasePDG::addParticlesFromExternalFile(TDatabasePDG* db)
+{
+  static bool initialized = false;
+  if (!initialized) {
+    // allow user to specify custom file
+    if (const char* custom = std::getenv("O2_SIM_CUSTOM_PDG")) {
+      // TODO: make sure this is a file
+      db->ReadPDGTable(custom);
+    } else if (const char* o2Root = std::getenv("O2_ROOT")) {
+      // take the maintained file from O2
+      auto inputExtraPDGs = std::string(o2Root) + "/share/Detectors/gconfig/data/extra_ions_pdg_table.dat";
+      db->ReadPDGTable(inputExtraPDGs.c_str());
+    }
+    initialized = true;
+  }
 }
 
 } // namespace o2
diff --git a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h b/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h
deleted file mode 100644
index 150a8272c7714..0000000000000
--- a/DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
-// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
-// All rights not expressly granted are reserved.
-//
-// This software is distributed under the terms of the GNU General Public
-// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
-//
-// In applying this license CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-/// \file ProcessingEventInfo.h
-/// \brief Encapsulated meta information about current event being processed by FairRoot (analysis) tasks
-/// \author Sandro Wenzel
-
-#ifndef ALICEO2_DATA_EVENTINFO_H_
-#define ALICEO2_DATA_EVENTINFO_H_
-
-namespace o2
-{
-
-// A class encapsulating meta information about events being process
-// and the data being sent by run classes such as FairRunAna.
-// Can be send to processing tasks for usage so that they do no longer
-// need to access the FairRootManager directly.
-struct ProcessingEventInfo {
-  double eventTime;   //! time of the current event
-  int eventNumber;    //! the current entry
-  int sourceNumber;   //! the current source number
-  bool numberSources; //! number of sources
-  // can be extended further
-};
-
-} // namespace o2
-
-#endif
diff --git a/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h b/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
index f3d41a17208f0..34d1c57aa9f0b 100644
--- a/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
+++ b/DataFormats/simulation/include/SimulationDataFormat/TrackReference.h
@@ -171,7 +171,7 @@ class TrackReference
   float mTrackLength = 0; ///< track length from its origin in cm
   float mTof = 0;         ///< time of flight in cm
   Int_t mUserId = 0;      ///< optional Id defined by user
-  Int_t mDetectorId = 0;  ///< Detector Id
+  Int_t mDetectorId = -1; ///< sensitive Detector Id (-1 if unknown or in passive material)
   SimTrackStatus mStatus; ///< encoding the track status
 
   friend std::ostream& operator<<(std::ostream&, const TrackReference&);
diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx
index f3f40d77042a5..79e36aa9fa48b 100644
--- a/DataFormats/simulation/src/DigitizationContext.cxx
+++ b/DataFormats/simulation/src/DigitizationContext.cxx
@@ -19,6 +19,7 @@
 #include  // for iota
 #include 
 #include 
+#include 
 
 using namespace o2::steer;
 
@@ -48,6 +49,27 @@ void DigitizationContext::printCollisionSummary(bool withQED, int truncateOutput
     }
   } else {
     std::cout << "Number of Collisions " << mEventRecords.size() << "\n";
+    if (mEventPartsWithQED.size() > 0) {
+      auto num_qed_events = mEventPartsWithQED.size() - mEventRecords.size();
+      if (num_qed_events > 0) {
+        std::cout << "Number of QED events (but not shown) " << num_qed_events << "\n";
+        // find first and last QED collision so that we can give the range in orbits where these
+        // things are included
+        auto firstQEDcoll_iter = std::find_if(mEventPartsWithQED.begin(), mEventPartsWithQED.end(),
+                                              [](const std::vector& vec) {
+                                                return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
+                                              });
+
+        auto lastColl_iter = std::find_if(mEventPartsWithQED.rbegin(), mEventPartsWithQED.rend(),
+                                          [](const std::vector& vec) {
+                                            return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
+                                          });
+
+        auto firstindex = std::distance(mEventPartsWithQED.begin(), firstQEDcoll_iter);
+        auto lastindex = std::distance(mEventPartsWithQED.begin(), lastColl_iter.base()) - 1;
+        std::cout << "QED from: " << mEventRecordsWithQED[firstindex] << " ---> " << mEventRecordsWithQED[lastindex] << "\n";
+      }
+    }
     for (int i = 0; i < mEventRecords.size(); ++i) {
       if (truncateOutputTo >= 0 && i > truncateOutputTo) {
         std::cout << "--- Output truncated to " << truncateOutputTo << " ---\n";
@@ -77,6 +99,14 @@ bool DigitizationContext::initSimChains(o2::detectors::DetID detid, std::vector<
     return false;
   }
 
+  // check that all files are present, otherwise quit
+  for (int source = 0; source < mSimPrefixes.size(); ++source) {
+    if (!std::filesystem::exists(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[source].data()))) {
+      LOG(info) << "Not hit file present for " << detid.getName() << " (exiting SimChain setup)";
+      return false;
+    }
+  }
+
   simchains.emplace_back(new TChain("o2sim"));
   // add the main (background) file
   simchains.back()->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[0].data()).c_str());
@@ -121,6 +151,19 @@ bool DigitizationContext::initSimKinematicsChains(std::vector& simkinem
     // add signal files
     simkinematicschains.back()->AddFile(o2::base::NameConf::getMCKinematicsFileName(mSimPrefixes[source].data()).c_str());
   }
+
+  // we add QED, if used in the digitization context
+  if (mEventRecordsWithQED.size() > 0) {
+    if (mSimPrefixes.size() >= QEDSOURCEID) {
+      LOG(fatal) << "Too many signal chains; crashes with QED source ID";
+    }
+
+    // it might be better to use an unordered_map for the simchains but this requires interface changes
+    simkinematicschains.resize(QEDSOURCEID + 1, nullptr);
+    simkinematicschains[QEDSOURCEID] = new TChain("o2sim");
+    simkinematicschains[QEDSOURCEID]->AddFile(o2::base::DetectorNameConf::getMCKinematicsFileName(mQEDSimPrefix).c_str());
+  }
+
   return true;
 }
 
@@ -196,10 +239,52 @@ o2::parameters::GRPObject const& DigitizationContext::getGRP() const
 
 void DigitizationContext::saveToFile(std::string_view filename) const
 {
+  // checks if the path content of filename exists ... otherwise it is created before creating the ROOT file
+  auto ensure_path_exists = [](std::string_view filename) {
+    try {
+      // Extract the directory path from the filename
+      std::filesystem::path file_path(filename);
+      std::filesystem::path dir_path = file_path.parent_path();
+
+      // Check if the directory path is empty (which means filename was just a name without path)
+      if (dir_path.empty()) {
+        // nothing to do
+        return true;
+      }
+
+      // Create directories if they do not exist
+      if (!std::filesystem::exists(dir_path)) {
+        if (std::filesystem::create_directories(dir_path)) {
+          // std::cout << "Directories created successfully: " << dir_path.string() << std::endl;
+          return true;
+        } else {
+          std::cerr << "Failed to create directories: " << dir_path.string() << std::endl;
+          return false;
+        }
+      }
+      return true;
+    } catch (const std::filesystem::filesystem_error& ex) {
+      std::cerr << "Filesystem error: " << ex.what() << std::endl;
+      return false;
+    } catch (const std::exception& ex) {
+      std::cerr << "General error: " << ex.what() << std::endl;
+      return false;
+    }
+  };
+
+  if (!ensure_path_exists(filename)) {
+    LOG(error) << "Filename contains path component which could not be created";
+    return;
+  }
+
   TFile file(filename.data(), "RECREATE");
-  auto cl = TClass::GetClass(typeid(*this));
-  file.WriteObjectAny(this, cl, "DigitizationContext");
-  file.Close();
+  if (file.IsOpen()) {
+    auto cl = TClass::GetClass(typeid(*this));
+    file.WriteObjectAny(this, cl, "DigitizationContext");
+    file.Close();
+  } else {
+    LOG(error) << "Could not write to file " << filename.data();
+  }
 }
 
 DigitizationContext* DigitizationContext::loadFromFile(std::string_view filename)
@@ -217,6 +302,11 @@ DigitizationContext* DigitizationContext::loadFromFile(std::string_view filename
 
 void DigitizationContext::fillQED(std::string_view QEDprefix, int max_events, double qedrate)
 {
+  if (mEventRecords.size() <= 1) {
+    // nothing to do
+    return;
+  }
+
   o2::steer::InteractionSampler qedInteractionSampler;
   qedInteractionSampler.setBunchFilling(mBCFilling);
 
@@ -337,9 +427,67 @@ std::vector> getTimeFrameBoundaries(std::vector(left, right - 1));
   return result;
 }
+
+// a common helper for timeframe structure - includes indices for orbits-early (orbits from last timeframe still affecting current one)
+std::vector> getTimeFrameBoundaries(std::vector const& irecords,
+                                                              long startOrbit,
+                                                              long orbitsPerTF,
+                                                              float orbitsEarly)
+{
+  // we could actually use the other method first ... then do another pass to fix the early-index ... or impact index
+  auto true_indices = getTimeFrameBoundaries(irecords, startOrbit, orbitsPerTF);
+
+  std::vector> indices_with_early{};
+  for (int ti = 0; ti < true_indices.size(); ++ti) {
+    // for each timeframe we copy the true indices
+    auto& tf_range = true_indices[ti];
+
+    // init new index without fixing the early index yet
+    indices_with_early.push_back(std::make_tuple(tf_range.first, tf_range.second, -1));
+
+    // from the second timeframe on we can determine the index in the previous timeframe
+    // which matches our criterion
+    if (orbitsEarly > 0. && ti > 0) {
+      auto& prev_tf_range = true_indices[ti - 1];
+      // in this range search the smallest index which precedes
+      // timeframe ti by not more than "orbitsEarly" orbits
+      // (could probably use binary search, in case optimization becomes necessary)
+      int earlyOrbitIndex = -1; // init to start of this timeframe ... there may not be early orbits
+
+      // this is the orbit of the ti-th timeframe start
+      auto orbit_timeframe_start = startOrbit + ti * orbitsPerTF;
+
+      auto orbit_timeframe_early_fractional = 1. * orbit_timeframe_start - orbitsEarly;
+      auto orbit_timeframe_early_integral = static_cast(std::floor(orbit_timeframe_early_fractional));
+
+      auto bc_early = (uint32_t)((orbit_timeframe_early_fractional - orbit_timeframe_early_integral) * o2::constants::lhc::LHCMaxBunches);
+
+      // this is the interaction record of the ti-th timeframe start
+      o2::InteractionRecord timeframe_start_record(0, orbit_timeframe_start);
+      // this is the interaction record in some previous timeframe after which interactions could still
+      // influence the ti-th timeframe according to orbitsEarly
+      o2::InteractionRecord timeframe_early_record(bc_early, orbit_timeframe_early_integral);
+
+      auto differenceInBCNS_max = timeframe_start_record.differenceInBCNS(timeframe_early_record);
+
+      for (int j = prev_tf_range.second; j >= prev_tf_range.first; --j) {
+        // determine difference in timing in NS; compare that with the limit given by orbitsEarly
+        auto timediff_NS = timeframe_start_record.differenceInBCNS(irecords[j]);
+        if (timediff_NS < differenceInBCNS_max) {
+          earlyOrbitIndex = j;
+        } else {
+          break;
+        }
+      }
+      std::get<2>(indices_with_early.back()) = earlyOrbitIndex;
+    }
+  }
+  return indices_with_early;
+}
+
 } // namespace
 
-void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl)
+void DigitizationContext::applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly)
 {
   // the idea is to go through each timeframe and throw away collisions beyond a certain count
   // then the indices should be condensed
@@ -347,9 +495,6 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
   std::vector> newparts;
   std::vector newrecords;
 
-  // get a timeframe boundary indexing
-  auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF);
-
   std::unordered_map currMaxId;                           // the max id encountered for a source
   std::unordered_map> reIndexMap; // for each source, a map of old to new index for the event parts
 
@@ -357,12 +502,51 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
     maxColl = mEventRecords.size();
   }
 
+  // the actual first actual timeframe
+  int first_timeframe = orbitsEarly > 0. ? 1 : 0;
+
+  // mapping of old to new indices
+  std::unordered_map indices_old_to_new;
+
   // now we can go through the structure timeframe by timeframe
-  for (auto timeframe : timeframeindices) {
-    auto firstindex = timeframe.first;
-    auto lastindex = timeframe.second;
+  for (int tf_id = first_timeframe; tf_id < timeframeindices.size(); ++tf_id) {
+    auto& tf_indices = timeframeindices[tf_id];
+
+    auto firstindex = std::get<0>(tf_indices); // .first;
+    auto lastindex = std::get<1>(tf_indices);  // .second;
+    auto previndex = std::get<2>(tf_indices);
+
+    LOG(info) << "timeframe indices " << previndex << " : " << firstindex << " : " << lastindex;
+
+    int collCount = 0; // counting collisions within timeframe
     // copy to new structure
-    for (int index = firstindex; index <= std::min(lastindex, firstindex + maxColl - 1); ++index) {
+    for (int index = previndex >= 0 ? previndex : firstindex; index <= lastindex; ++index) {
+      if (collCount >= maxColl) {
+        continue;
+      }
+
+      // look if this index was already done?
+      // avoid duplicate entries in transformed records
+      if (indices_old_to_new.find(index) != indices_old_to_new.end()) {
+        continue;
+      }
+
+      // we put these events under a certain condition
+      bool keep = index < firstindex || collCount < maxColl;
+
+      if (!keep) {
+        continue;
+      }
+
+      if (index >= firstindex) {
+        collCount++;
+      }
+
+      // we must also make sure that we don't duplicate the records
+      // moreover some records are merely put as precoll of tf2 ---> so they shouldn't be part of tf1 in the final
+      // extraction, ouch !
+      // maybe we should combine the filter and individual tf extraction in one step !!
+      indices_old_to_new[index] = newrecords.size();
       newrecords.push_back(mEventRecords[index]);
       newparts.push_back(mEventParts[index]);
 
@@ -384,6 +568,19 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
           currMaxId[source] += 1;
         }
       }
+    } // ends one timeframe
+
+    // correct the timeframe indices
+    if (indices_old_to_new.find(firstindex) != indices_old_to_new.end()) {
+      std::get<0>(tf_indices) = indices_old_to_new[firstindex]; // start
+    }
+    if (indices_old_to_new.find(lastindex) != indices_old_to_new.end()) {
+      std::get<1>(tf_indices) = indices_old_to_new[lastindex]; // end;
+    } else {
+      std::get<1>(tf_indices) = newrecords.size() - 1; // end; -1 since index inclusif
+    }
+    if (indices_old_to_new.find(previndex) != indices_old_to_new.end()) {
+      std::get<2>(tf_indices) = indices_old_to_new[previndex]; // previous or "early" index
     }
   }
   // reassignment
@@ -391,13 +588,10 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe
   mEventParts = newparts;
 }
 
-void DigitizationContext::finalizeTimeframeStructure(long startOrbit, long orbitsPerTF)
+std::vector> DigitizationContext::calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly) const
 {
-  mTimeFrameStartIndex = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF);
-  LOG(info) << "Fixed " << mTimeFrameStartIndex.size() << " timeframes ";
-  for (auto p : mTimeFrameStartIndex) {
-    LOG(info) << p.first << " " << p.second;
-  }
+  auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF, orbitsEarly);
+  return timeframeindices;
 }
 
 std::unordered_map DigitizationContext::getCollisionIndicesForSource(int source) const
@@ -436,6 +630,17 @@ struct pair_hash {
 };
 } // namespace
 
+int DigitizationContext::setInteractionVertices(std::vector> const& external_vertices)
+{
+  if (external_vertices.size() != mEventRecords.size()) {
+    LOG(error) << "Size mismatch with event record";
+    return 1;
+  }
+  mInteractionVertices.clear();
+  std::copy(external_vertices.begin(), external_vertices.end(), std::back_inserter(mInteractionVertices));
+  return 0;
+}
+
 void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexObject const& meanv)
 {
   // mapping of source x event --> index into mInteractionVertices
@@ -483,3 +688,63 @@ void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexO
     }
   }
 }
+
+DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset)
+{
+  DigitizationContext r; // make a return object
+  if (timeframeindices.size() == 0) {
+    LOG(error) << "Timeframe index structure empty; Returning empty object.";
+    return r;
+  }
+  r.mSimPrefixes = mSimPrefixes;
+  r.mMuBC = mMuBC;
+  r.mBCFilling = mBCFilling;
+  r.mDigitizerInteractionRate = mDigitizerInteractionRate;
+  try {
+    auto tf_ranges = timeframeindices.at(timeframeid);
+
+    auto startindex = std::get<0>(tf_ranges);
+    auto endindex = std::get<1>(tf_ranges) + 1;
+    auto earlyindex = std::get<2>(tf_ranges);
+
+    if (earlyindex >= 0) {
+      startindex = earlyindex;
+    }
+    std::copy(mEventRecords.begin() + startindex, mEventRecords.begin() + endindex, std::back_inserter(r.mEventRecords));
+    std::copy(mEventParts.begin() + startindex, mEventParts.begin() + endindex, std::back_inserter(r.mEventParts));
+    if (mInteractionVertices.size() >= endindex) {
+      std::copy(mInteractionVertices.begin() + startindex, mInteractionVertices.begin() + endindex, std::back_inserter(r.mInteractionVertices));
+    }
+
+    // let's assume we want to fix the ids for source = source_id
+    // Then we find the first index that has this source_id and take the corresponding number
+    // as offset. Thereafter we subtract this offset from all known event parts.
+    auto perform_offsetting = [&r](int source_id) {
+      auto indices_for_source = r.getCollisionIndicesForSource(source_id);
+      int minvalue = std::numeric_limits::max();
+      for (auto& p : indices_for_source) {
+        if (p.first < minvalue) {
+          minvalue = p.first;
+        }
+      }
+      // now fix them
+      for (auto& p : indices_for_source) {
+        auto index_into_mEventParts = p.second;
+        for (auto& part : r.mEventParts[index_into_mEventParts]) {
+          if (part.sourceID == source_id) {
+            part.entryID -= minvalue;
+          }
+        }
+      }
+    };
+    for (auto source_id : sources_to_offset) {
+      perform_offsetting(source_id);
+    }
+
+  } catch (std::exception) {
+    LOG(warn) << "No such timeframe id in collision context. Returing empty object";
+  }
+  // fix number of collisions
+  r.setNCollisions(r.mEventRecords.size());
+  return r;
+}
diff --git a/DataFormats/simulation/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx
index 1936bf7dc06a9..f3ece5c51f90b 100644
--- a/DataFormats/simulation/src/InteractionSampler.cxx
+++ b/DataFormats/simulation/src/InteractionSampler.cxx
@@ -115,8 +115,8 @@ const o2::InteractionTimeRecord& InteractionSampler::generateCollisionTime()
 int InteractionSampler::simulateInteractingBC()
 {
   // Returns number of collisions assigned to selected BC
-
   nextCollidingBC(mBCJumpGenerator.getNextValue());
+
   // once BC is decided, enforce at least one interaction
   int ncoll = mNCollBCGenerator.getNextValue();
 
@@ -130,6 +130,24 @@ int InteractionSampler::simulateInteractingBC()
   return ncoll;
 }
 
+//_________________________________________________
+int FixedSkipBC_InteractionSampler::simulateInteractingBC()
+{
+  // Returns number of collisions assigned to selected BC
+
+  nextCollidingBC(mEveryN);  // we jump regular intervals
+  int ncoll = mMultiplicity; // well defined pileup
+
+  // assign random time withing a bunch
+  for (int i = ncoll; i--;) {
+    mTimeInBC.push_back(mCollTimeGenerator.getNextValue());
+  }
+  if (ncoll > 1) { // sort in DECREASING time order (we are reading vector from the end)
+    std::sort(mTimeInBC.begin(), mTimeInBC.end(), [](const float a, const float b) { return a > b; });
+  }
+  return ncoll;
+}
+
 //_________________________________________________
 void InteractionSampler::setBunchFilling(const std::string& bcFillingFile)
 {
@@ -144,3 +162,101 @@ void InteractionSampler::setBunchFilling(const std::string& bcFillingFile)
   mBCFilling = *bc;
   delete bc;
 }
+
+// ________________________________________________
+bool NonUniformMuInteractionSampler::setBCIntensityScales(const std::vector& scales_from_vector)
+{
+  // Sets the intensity scales per bunch crossing index
+  // The length of this vector needs to be compatible with the bunch filling chosen
+  mBCIntensityScales = scales_from_vector;
+
+  if (scales_from_vector.size() != mInteractingBCs.size()) {
+    LOG(error) << "Scaling factors and bunch filling scheme are not compatible. Not doing anything";
+    return false;
+  }
+
+  float sum = 0.;
+  for (auto v : mBCIntensityScales) {
+    sum += std::abs(v);
+  }
+  if (sum == 0) {
+    LOGP(warn, "total intensity is 0, assuming uniform");
+    for (auto& v : mBCIntensityScales) {
+      v = 1.f;
+    }
+  } else { // normalize
+    float norm = mBCIntensityScales.size() / sum;
+    for (auto& v : mBCIntensityScales) {
+      v = std::abs(v) * norm;
+    }
+  }
+  return false;
+}
+
+// ________________________________________________
+
+bool NonUniformMuInteractionSampler::setBCIntensityScales(const TH1F& hist)
+{
+  return setBCIntensityScales(determineBCIntensityScalesFromHistogram(hist));
+}
+
+std::vector NonUniformMuInteractionSampler::determineBCIntensityScalesFromHistogram(const TH1F& hist)
+{
+  if (mInteractingBCs.size() == 0) {
+    LOG(error) << " Initialize bunch crossing scheme before assigning scales";
+  }
+  std::vector scales;
+  // we go through the BCs and query the count from histogram
+  for (auto bc : mInteractingBCs) {
+    scales.push_back(hist.GetBinContent(bc + 1));
+  }
+  return scales;
+}
+
+int NonUniformMuInteractionSampler::getBCJump() const
+{
+  auto muFunc = [this](int bc_position) {
+    return mBCIntensityScales[bc_position % mInteractingBCs.size()] * mMuBC;
+  };
+
+  double U = gRandom->Rndm();    // uniform (0,1)
+  double T = -std::log(1.0 - U); // threshold
+  double sumMu = 0.0;
+  int offset = 0;
+  auto bcStart = mCurrBCIdx; // the current bc
+
+  while (sumMu < T) {
+    auto mu_here = muFunc(bcStart + offset); // mu at next BC
+    sumMu += mu_here;
+    if (sumMu >= T) {
+      break; // found BC with at least one collision
+    }
+    ++offset;
+  }
+  return offset;
+}
+
+int NonUniformMuInteractionSampler::simulateInteractingBC()
+{
+  nextCollidingBC(getBCJump());
+
+  auto muFunc = [this](int bc_position) {
+    return mBCIntensityScales[bc_position % mInteractingBCs.size()] * mMuBC;
+  };
+
+  // now sample number of collisions in chosenBC, conditioned >=1:
+  double mu_chosen = muFunc(mCurrBCIdx); // or does it need to be mCurrBCIdx
+  int ncoll = 0;
+  do {
+    ncoll = gRandom->Poisson(mu_chosen);
+  } while (ncoll == 0);
+
+  // assign random time withing a bunch
+  for (int i = ncoll; i--;) {
+    mTimeInBC.push_back(mCollTimeGenerator.getNextValue());
+  }
+  if (ncoll > 1) { // sort in DECREASING time order (we are reading vector from the end)
+    std::sort(mTimeInBC.begin(), mTimeInBC.end(), [](const float a, const float b) { return a > b; });
+  }
+  return ncoll;
+}
\ No newline at end of file
diff --git a/DataFormats/simulation/src/MCCompLabel.cxx b/DataFormats/simulation/src/MCCompLabel.cxx
index 8e4f2ad73f876..656fcd9770579 100644
--- a/DataFormats/simulation/src/MCCompLabel.cxx
+++ b/DataFormats/simulation/src/MCCompLabel.cxx
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -31,6 +32,16 @@ std::ostream& operator<<(std::ostream& os, MCCompLabel const& c)
   return os;
 }
 
+//_____________________________________________
+std::string MCCompLabel::asString() const
+{
+  // describe itself
+  if (isValid()) {
+    return fmt::format("[{}/{}{}/{:6d}]", getSourceID(), getEventID(), isFake() ? '-' : '+', getTrackID());
+  }
+  return isNoise() ? "[noise]" : "[unset]";
+}
+
 //_____________________________________________
 void MCCompLabel::print() const
 {
diff --git a/DataFormats/simulation/src/MCEventHeader.cxx b/DataFormats/simulation/src/MCEventHeader.cxx
index 99004fec46ffa..8a46d84805507 100644
--- a/DataFormats/simulation/src/MCEventHeader.cxx
+++ b/DataFormats/simulation/src/MCEventHeader.cxx
@@ -15,6 +15,7 @@
 #include "FairRootManager.h"
 #include 
 #include 
+#include 
 
 namespace o2
 {
@@ -61,6 +62,69 @@ void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std
   }
 }
 
+void MCEventHeader::print() const
+{
+  // print some used parts from FairMCEventHeader
+  std::cout << "RunID " << fRunId << "\n";
+  std::cout << "EventID " << fEventId << "\n";
+  std::cout << "Vertex-X " << fX << "\n";
+  std::cout << "Vertex-Y " << fY << "\n";
+  std::cout << "Vertex-Z " << fZ << "\n";
+  std::cout << "Vertex-T " << fT << "\n";
+  std::cout << "Impact-B " << fB << "\n";
+  std::cout << "NPrim " << fNPrim << "\n";
+  // print meta-fields
+  printInfo();
+}
+
+/** alternative implementations below
+
+void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename) {
+  TFile kinefile(kinefilename.data(), "OPEN");
+  auto kinetree = static_cast(kinefile.Get("o2sim")); // belongs to "kinefile"
+  kinetree->SetBranchStatus("*", 0);
+  // activate header branch
+  kinetree->SetBranchStatus("MCEventHeader*", 1);
+  std::unique_ptr headeronlyfile{TFile::Open(targetfilename.data(), "RECREATE")};
+  auto headeronlytree = kinetree->CloneTree(0);
+
+  // copy the branches
+  headeronlytree->CopyEntries(kinetree, kinetree->GetEntries());
+  headeronlytree->SetEntries(kinetree->GetEntries());
+  // flush to disc
+  headeronlytree->Write();
+}
+
+void MCEventHeader::extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename)
+{
+  TFile kinefile(kinefilename.data(), "OPEN");
+  auto kinetree = static_cast(kinefile.Get("o2sim")); // owned by "kinefile"
+  auto headerbranchorig = kinetree->GetBranch("MCEventHeader.");
+  if (!headerbranchorig) {
+    return;
+  }
+
+  std::unique_ptr headeronlyfile{TFile::Open(targetfilename.data(), "RECREATE")};
+  auto headeronlytree = new TTree("o2sim", "o2sim"); // owned by headeronlyfile
+  MCEventHeader* header = nullptr;
+  auto targetBranch = headeronlytree->Branch("MCEventHeader.", &header);
+  headerbranchorig->SetAddress(&header);
+
+  for (auto entry = 0; entry < kinetree->GetEntries(); ++entry) {
+    headerbranchorig->GetEntry(entry);
+    targetBranch->Fill();
+    if (header) {
+      delete header;
+      header = nullptr;
+    }
+  }
+  headeronlytree->SetEntries(kinetree->GetEntries());
+  // flush to disc
+  headeronlytree->Write();
+}
+
+*/
+
 /*****************************************************************/
 /*****************************************************************/
 
diff --git a/DataFormats/simulation/src/MCUtils.cxx b/DataFormats/simulation/src/MCUtils.cxx
index 8c1e242a5332b..14cf7ab7048a4 100644
--- a/DataFormats/simulation/src/MCUtils.cxx
+++ b/DataFormats/simulation/src/MCUtils.cxx
@@ -14,7 +14,7 @@
 //
 
 #include 
-#include 
+#include 
 
 namespace o2::mcutils
 {
diff --git a/DataFormats/simulation/src/SimulationDataLinkDef.h b/DataFormats/simulation/src/SimulationDataLinkDef.h
index 8a1e0c536c089..8f74bd757e791 100644
--- a/DataFormats/simulation/src/SimulationDataLinkDef.h
+++ b/DataFormats/simulation/src/SimulationDataLinkDef.h
@@ -24,6 +24,8 @@
 #pragma link off all functions;
 
 #pragma link C++ class o2::steer::InteractionSampler + ;
+#pragma link C++ class o2::steer::FixedSkipBC_InteractionSampler + ;
+#pragma link C++ class o2::steer::NonUniformMuInteractionSampler + ;
 #pragma link C++ class o2::sim::StackParam + ;
 #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::sim::StackParam> + ;
 #pragma link C++ class o2::MCTrackT < double> + ;
diff --git a/DataFormats/simulation/test/MCTrack.cxx b/DataFormats/simulation/test/MCTrack.cxx
index 699adc89abb8c..e857e2b88da41 100644
--- a/DataFormats/simulation/test/MCTrack.cxx
+++ b/DataFormats/simulation/test/MCTrack.cxx
@@ -24,10 +24,17 @@ using namespace o2;
 BOOST_AUTO_TEST_CASE(MCTrack_test)
 {
   MCTrack track;
+
+  // auxiliary lookup table needed to fetch and set hit properties
+  std::vector hitLUT(o2::detectors::DetID::nDetectors, -1);
+  // in this test we have a single fictional detector 1, which we map to
+  // the first bit
+  hitLUT[1] = 0;
+
   // check properties on default constructed object
   BOOST_CHECK(track.getStore() == false);
   for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) {
-    BOOST_CHECK(track.leftTrace(i) == false);
+    BOOST_CHECK(track.leftTrace(i, hitLUT) == false);
   }
   BOOST_CHECK(track.getNumDet() == 0);
   BOOST_CHECK(track.hasHits() == false);
@@ -41,10 +48,10 @@ BOOST_AUTO_TEST_CASE(MCTrack_test)
   BOOST_CHECK(track.getStore() == true);
 
   // set hit for first detector
-  BOOST_CHECK(track.leftTrace(1) == false);
-  track.setHit(1);
+  BOOST_CHECK(track.leftTrace(1, hitLUT) == false);
+  track.setHit(hitLUT[1]);
   BOOST_CHECK(track.hasHits() == true);
-  BOOST_CHECK(track.leftTrace(1) == true);
+  BOOST_CHECK(track.leftTrace(1, hitLUT) == true);
   BOOST_CHECK(track.getNumDet() == 1);
 
   // check process encoding
diff --git a/DataFormats/simulation/test/testBasicHits.cxx b/DataFormats/simulation/test/testBasicHits.cxx
index e81c173fedae8..ccd16ae7a3671 100644
--- a/DataFormats/simulation/test/testBasicHits.cxx
+++ b/DataFormats/simulation/test/testBasicHits.cxx
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(BasicXYZHit_ROOTIO)
   }
 
   // same for double valued hits
-  using HitTypeD = BasicXYZEHit;
+  using HitTypeD = BasicXYZEHit;
   HitTypeD hitD(1., 2., 3., 0.01, -1.1, -1, 1);
 
   // try writing hit to a TBuffer
diff --git a/DataFormats/simulation/test/testInteractionSampler.cxx b/DataFormats/simulation/test/testInteractionSampler.cxx
new file mode 100644
index 0000000000000..b1b3691884ccf
--- /dev/null
+++ b/DataFormats/simulation/test/testInteractionSampler.cxx
@@ -0,0 +1,76 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#define BOOST_TEST_MODULE Test InteractionSampler class
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_DYN_LINK
+
+#include 
+#include "SimulationDataFormat/InteractionSampler.h"
+#include "CCDB/BasicCCDBManager.h"
+#include "DataFormatsParameters/AggregatedRunInfo.h"
+#include "DataFormatsParameters/GRPLHCIFData.h"
+#include "TFile.h"
+#include "TGrid.h"
+#include 
+
+namespace o2
+{
+
+BOOST_AUTO_TEST_CASE(NonUniformSampler)
+{
+  auto run_number = 559827;
+  TGrid::Connect("alien");
+  if (gGrid) {
+    auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run_number);
+
+    o2::steer::NonUniformMuInteractionSampler sampler;
+    sampler.setBunchFilling(runInfo.grpLHC->getBunchFilling());
+
+    // the test distribution provided by Igor Altsybeev
+    auto distr_file = TFile::Open("alien:///alice/cern.ch/user/s/swenzel/AliceO2_TestData/NBcVTX_559827/hBcTVX_data_PbPb_24ar_559827.root");
+
+    //
+    if (distr_file && !distr_file->IsZombie()) {
+      auto hist = distr_file->Get("hBcTVX");
+      if (hist) {
+        sampler.init();
+        sampler.setBCIntensityScales(*hist);
+
+        // sample into a vector of a certain size
+        std::vector samples;
+
+        int N = 100000;
+        samples.resize(N);
+
+        sampler.generateCollisionTimes(samples);
+
+        // fill an output histogram
+        auto output_hist = (TH1F*)hist->Clone("h2"); // make a full copy
+        output_hist->Reset();
+
+        for (const auto& sample : samples) {
+          output_hist->Fill(sample.bc);
+        }
+
+        // Write out
+        auto fout = TFile::Open("NBCVTX_out.root", "RECREATE");
+        fout->WriteObject(output_hist, "NBcVTX");
+        fout->Close();
+
+        // compare mean values of original and newly sampled hist
+        BOOST_CHECK_CLOSE(hist->GetMean(), output_hist->GetMean(), 0.5);
+      }
+    }
+  }
+}
+
+} // namespace o2
diff --git a/DataFormats/simulation/test/testMCGenId.cxx b/DataFormats/simulation/test/testMCGenId.cxx
new file mode 100644
index 0000000000000..246268ac78d52
--- /dev/null
+++ b/DataFormats/simulation/test/testMCGenId.cxx
@@ -0,0 +1,50 @@
+// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+#define BOOST_TEST_MODULE Test MCGenId class
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_DYN_LINK
+#include 
+#include "SimulationDataFormat/MCGenProperties.h"
+
+using namespace o2::mcgenid;
+
+BOOST_AUTO_TEST_CASE(MCGenId_test)
+{
+  // possible generator IDs range from 0 to 127 (included)
+  constexpr int highGenerator{128};
+  // possible sub-generator IDs range from -1 to 30 (included)
+  constexpr int highSubGenerator{31};
+  // possible soufce IDs range from 0 to 15 (included)
+  constexpr int highSource{16};
+
+  // test all combinations
+  for (int sourceId = 0; sourceId < highSource; sourceId++) {
+    for (int generatorId = 0; generatorId < highGenerator; generatorId++) {
+      for (int subGeneratorId = -1; subGeneratorId < highSubGenerator; subGeneratorId++) {
+        auto encoded = getEncodedGenId(generatorId, sourceId, subGeneratorId);
+        // decode them
+        auto sourceIdAfter = getSourceId(encoded);
+        auto generatorIdAfter = getGeneratorId(encoded);
+        auto subGeneratorIdAfter = getSubGeneratorId(encoded);
+
+        std::cout << "SourceID: " << sourceId << " ==> " << sourceIdAfter << "\n"
+                  << "generatorId: " << generatorId << " ==> " << generatorIdAfter << "\n"
+                  << "subGeneratorId: " << subGeneratorId << " ==> " << subGeneratorIdAfter << "\n";
+
+        // check if original and decoded numbers are the same
+        BOOST_CHECK(sourceIdAfter == sourceId);
+        BOOST_CHECK(generatorIdAfter == generatorId);
+        BOOST_CHECK(subGeneratorId == subGeneratorIdAfter);
+      }
+    }
+  }
+}
diff --git a/DataFormats/simulation/test/testMCGenStatus.cxx b/DataFormats/simulation/test/testMCGenStatus.cxx
index 6156f20b0fa47..a9b8b7277b3e7 100644
--- a/DataFormats/simulation/test/testMCGenStatus.cxx
+++ b/DataFormats/simulation/test/testMCGenStatus.cxx
@@ -15,7 +15,7 @@
 #include 
 #include "SimulationDataFormat/MCTrack.h"
 #include "SimulationDataFormat/ParticleStatus.h"
-#include "SimulationDataFormat/MCGenStatus.h"
+#include "SimulationDataFormat/MCGenProperties.h"
 #include "SimulationDataFormat/MCUtils.h"
 #include "TFile.h"
 #include "TParticle.h"
diff --git a/Detectors/AOD/CMakeLists.txt b/Detectors/AOD/CMakeLists.txt
index 5ec99773a5dc9..827b23b3e4cdd 100644
--- a/Detectors/AOD/CMakeLists.txt
+++ b/Detectors/AOD/CMakeLists.txt
@@ -19,6 +19,7 @@ target_link_libraries(
           O2::FDDWorkflow
           O2::FV0Workflow
           O2::Framework
+          O2::FrameworkAnalysisSupport
           O2::GlobalTracking
           O2::GlobalTrackingWorkflow
           O2::ITSMFTWorkflow
@@ -41,15 +42,15 @@ o2_add_executable(
   workflow
   COMPONENT_NAME aod-producer
   TARGETVARNAME targetName
-  SOURCES src/aod-producer-workflow.cxx src/AODProducerWorkflowSpec.cxx
-  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version
+  SOURCES src/aod-producer-workflow.cxx src/AODProducerWorkflowSpec.cxx src/AODMcProducerHelpers.cxx
+  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version nlohmann_json::nlohmann_json
 )
 
 o2_add_executable(
   workflow
   COMPONENT_NAME aod-mc-producer
-  SOURCES src/aod-mc-producer-workflow.cxx src/AODMcProducerWorkflowSpec.cxx
-  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version
+  SOURCES src/aod-mc-producer-workflow.cxx src/AODMcProducerWorkflowSpec.cxx src/AODMcProducerHelpers.cxx
+  PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version nlohmann_json::nlohmann_json
 )
 
 o2_add_executable(
@@ -75,7 +76,9 @@ o2_add_executable(
         O2::DataFormatsFT0
         O2::Steer
         O2::ZDCBase
-)
+        O2::FrameworkAnalysisSupport
+        nlohmann_json::nlohmann_json
+        )
 
 if (OpenMP_CXX_FOUND)
   target_compile_definitions(${targetName} PRIVATE WITH_OPENMP)
diff --git a/Detectors/AOD/README.md b/Detectors/AOD/README.md
new file mode 100644
index 0000000000000..42a9840fabc04
--- /dev/null
+++ b/Detectors/AOD/README.md
@@ -0,0 +1,50 @@
+
+
+# AOD producers
+
+The code in this directory is meant to produce `AO2D.root` output
+files.   There are two such producers:
+
+- [`o2-aod-producer-workflow`](src/AODProducerWorkflowSpec.cxx) that
+  produces `AO2D.root` from reconstructed data (whether from
+  simulations or the experiment).  The output will contain
+  reconstructed tracks "calo" hits, and so on.
+- [`o2-aod-mc-producer-workflow`](src/AODMcProducerWorkflowSpec.cxx)
+  that produces `AO2D.root` from simulation data only. The output will
+  only contain the simulation particles and collision headers.
+
+## Do a full simulation to AO2D chain
+
+You need [`O2DPG`](https://github.com/AliceO2Group/O2DPG/) installed.
+
+Then, a script a la
+
+    #!/bin/bash
+    #
+    # A example workflow MC->RECO->AOD for a simple pp production
+    # excluding ZDC
+    NWORKERS=${NWORKERS:-6}
+    SIMENGINE=${SIMENGINE:-TGeant4}
+
+    ${O2DPG_ROOT}/MC/bin/o2dpg_sim_workflow.py \
+                 -eCM 14000 \
+                 -col pp \
+                 -gen pythia8 \
+                 -proc "cdiff" \
+                 -tf 2  \
+                 -ns 50 \
+                 -e ${SIMENGINE} \
+                 -j ${NWORKERS} \
+                 -run 303000 \
+                 -seed 624 \
+                 -interactionRate 50000
+
+    ${O2DPG_ROOT}/MC/bin/o2_dpg_workflow_runner.py -f workflow.json -tt aod
+
+will set-up a chain and run it.  The above will make 2 time-frames,
+each with 50 pp collisions at sqrt(s)=14TeV generated by Pythia8.
+
+See also [this page](https://aliceo2group.github.io/simulation/) for more on simulations with O2.
+
diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h
new file mode 100644
index 0000000000000..5e9cd445b576b
--- /dev/null
+++ b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h
@@ -0,0 +1,325 @@
+// Copyright 2023-2099 CERN and copyright holders of ALICE O2.
+// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
+// All rights not expressly granted are reserved.
+//
+// This software is distributed under the terms of the GNU General Public
+// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
+//
+// In applying this license CERN does not waive the privileges and immunities
+// granted to it by virtue of its status as an Intergovernmental Organization
+// or submit itself to any jurisdiction.
+
+/// @file   AODMcProducerHelpers.h
+/// @author Christian Holm Christensen 
+/// common helpers for AOD MC producers
+
+#ifndef O2_AODMCPRODUCER_HELPERS
+#define O2_AODMCPRODUCER_HELPERS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/**
+ * Utilities to transform simulated data into AO2D tables.
+ *
+ * The function templates below are templated on the cursor type over
+ * the relevant AOD tables.  Such a table can be obtained from the
+ * ProcessingContext @c pc
+ *
+ * @code
+ * auto builder = pc.make(OutputForTable::ref());
+ * auto cursor  = builder->cursor
(); + * @endcode + * + * If the task uses the @c Produces
template, + * + * @code + * Produces
mTable; + * @endcode + * + * then a cursor is obtained via, + * + * @code + * auto cursor = mTable.cursor; + * @endcode + * + * Note that these functions cannot be moved into a compilation unit, + * because that would require deducing the table cursor type, by + * f.ex. + * + * @code + * template + * struct TableCursor { + * using cursor_t = decltype(std::declval() + * .cursor
()); + * }; + * using CollisionCursor = TableCursor:cursor_t; + * @endcode + * + * but since cursors are really Lambdas and Lambda types are specific + * to the compilation unit, then the implementation file (compilation + * unit) of these functions definitions and their use (another + * compilation unit) would have different types of the the cursers, + * and thus not be able to link. More information is given at + * https://stackoverflow.com/questions/50033797. + */ +namespace o2::aodmchelpers +{ +//================================================================== +/** + * Deduce cursor type and wrap in std::function + */ +template +struct TableCursor { + using type = decltype(framework::FFL(std::declval() + .cursor
())); +}; +//================================================================== +/** Cursor over aod::McCollisions */ +using CollisionCursor = TableCursor::type; +/** Cursor over aod::McParticles */ +using ParticleCursor = TableCursor::type; +/** Cursor over aod::HepMCXSections */ +using XSectionCursor = TableCursor::type; +/** Cursor over aod::HepMCPdfInfos */ +using PdfInfoCursor = TableCursor::type; +/** Cursor over aod::HepMCHeavyIons */ +using HeavyIonCursor = TableCursor::type; +//================================================================== +/** Types of updates on HepMC tables. */ +enum HepMCUpdate { + never, + always, + anyKey, + allKeys +}; + +//================================================================== +/** + * Check if header has keys. If the argument @a anyNotAll is true, + * then this member function returns true if @e any of the keys + * were found. If @a anyNotAll is false, then return true only if + * @a all keys were found. + * + * @param header MC event header + * @param keys Keys to look for + * @param anyNotAll If true, return true if @e any key was found. + * If false, return true only if @a all keys were found + * + * @return true if any or all keys were found + */ +bool hasKeys(o2::dataformats::MCEventHeader const& header, + const std::vector& keys, + bool anyNotall = true); +//-------------------------------------------------------------------- +/** + * Get a property from the header, or if not set or not valid, a + * default value. + * + * @param header The MC event header + * @param key Key to look for + * @param def Value to return if key is not found + * + * @return Value of key or def if key is not found + */ +template +const T getEventInfo(o2::dataformats::MCEventHeader const& header, + std::string const& key, + T const& def) +{ + if (not header.hasInfo(key)) + return def; + + bool isValid = false; + const T& val = header.getInfo(key, isValid); + if (not isValid) + return def; + + return val; +} +//==================================================================== +/** + * Fill in collision information. This is read from the passed MC + * header and stored in the MCCollision table. The member function + * returns the encoded generator ID. + * + * @param cursor Cursor over o2::aod::McCollisions table + * @param bcId Bunch-crossing Identifier + * @param time Time of collisions + * @param header Event header from generator + * @param generatorId Default generator + * @param sourceId Identifier of source + * + * @return encoded generator ID + */ +short updateMCCollisions(const CollisionCursor& cursor, + int bcId, + float time, + o2::dataformats::MCEventHeader const& header, + short generatorId = 0, + int sourceId = 0, + unsigned int mask = 0xFFFFFFF0); +//-------------------------------------------------------------------- +/** + * Fill in HepMC cross-section table from event generator header. + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCXSection(const XSectionCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Fill in HepMC parton distribution function table from event + * generator header + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCPdfInfo(const PdfInfoCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Fill in HepMC heavy-ion table from generator event header. + * + * @param cursor Cursor over o2::aod::HepMCXSections table + * @param collisionID Identifier of collision (as given updateMCCollision) + * @param generatorID Encoded generator ID + * @param header Event header from generator + * @param anyNotAll If true, then any key present trigger and update. + * If false, then all keys must be present to update + * the table. + * + * @return true if table was updated + */ +bool updateHepMCHeavyIon(const HeavyIonCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when = HepMCUpdate::anyKey); +//-------------------------------------------------------------------- +/** + * Type of mapping from track number to row index + */ +using TrackToIndex = std::unordered_map; +//-------------------------------------------------------------------- +/** + * Update aod::McParticles table with information from an MC track. + * + * @param cursor Cursor over aod::McParticles table + * @param mapping Maps track number to index in table + * @param collisionID Collision identifier + * @param track Track to update table with + * @param tracks List of all tracks of current collision + * @param flags Base flags of this track + * @param weightMask Mask on weight floating point value + * @param momentumMask Mask on momentum floating point values + * @param positionMask Mask on position floating point values + */ +void updateParticle(const ParticleCursor& cursor, + const TrackToIndex& toStore, + int collisionID, + o2::MCTrack const& track, + std::vector const& tracks, + uint8_t flags = 0, + uint32_t weightMask = 0xFFFFFFF0, + uint32_t momentumMask = 0xFFFFFFF0, + uint32_t positionMask = 0xFFFFFFF0); +//-------------------------------------------------------------------- +/** + * Update aod::McParticles table with tracks from MC. + * + * To add particles from many events, one will do + * + * @code + * TrackToIndex preselect = findMcTracksToStore(...); + * + * size_t offset = 0; + * for (auto event : events) + * offset = updateParticles(cursor, + * event.getCollisionID(), + * event.getTracks(), + * offset, + * filter, + * event.isBackground(), + * preselect); + * @endcode + * + * Here @a preselect must be a map from track number to a positive + * value. Tracks that are mapped as such in @a preselect are stored + * in addition to other tracks selected by the function. Note that @a + * preselect may be empty. + * + * If @a filter is false, then @a all tracks will be stored. + * + * If @a filter is true, then tracks that are + * + * - generated by the generator, + * - physical primaries + * (MCTrackNavigator::isPhysicalPrimary), + * - to be kept for physics + * (MCTrackNavigator::isKeepPhysics), or + * - is listed with a positive value in @a preselect, or + * - either a mother or daughter of one such track, then + * + * that track is kept + * + * On return, the @a preselect will map from track number (index in + * the @a tracks container) to the table row index (including offset + * from previous events in the same time-frame). + * + * @param cursor Cursor over aod::McParticles + * @param int Collision identifier + * @param tracks List of all tracks of current collision + * @param offset Index just beyond last table entry + * @param filter Filter tracks + * @param background True of from background event + * @param preselect Mapping of preselected tracks + * @param weightMask Mask on weight floating point value + * @param momentumMask Mask on momentum floating point values + * @param positionMask Mask on position floating point values + * + * @return Index beyond the last particle added to table + */ +uint32_t updateParticles(const ParticleCursor& cursor, + int collisionID, + std::vector const& tracks, + TrackToIndex& preselect, + uint32_t offset = 0, + bool filter = false, + bool background = false, + uint32_t weightMask = 0xFFFFFFF0, + uint32_t momentumMask = 0xFFFFFFF0, + uint32_t positionMask = 0xFFFFFFF0, + bool signalFilter = false); +} // namespace o2::aodmchelpers + +#endif /* O2_AODMCPRODUCER_HELPERS */ +// Local Variables: +// mode: C++ +// End: diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h index 392dad61df4b3..674fbf7bfcd05 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerWorkflowSpec.h @@ -15,14 +15,14 @@ #define O2_AODMCPRODUCER_WORKFLOW_SPEC #include "AODProducerHelpers.h" +#include "AODMcProducerHelpers.h" #include "CommonDataFormat/InteractionRecord.h" #include "Framework/AnalysisDataModel.h" #include "Framework/AnalysisHelpers.h" -#include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" #include "Steer/MCKinematicsReader.h" -#include "TMap.h" -#include "TStopwatch.h" +#include #include #include @@ -41,30 +41,95 @@ class AODMcProducerWorkflowDPL : public Task void run(ProcessingContext& pc) final; void endOfStream(EndOfStreamContext& ec) final; + /** Some types we will use */ + using MCEventHeader = dataformats::MCEventHeader; + using McCollisions = aod::McCollisions; + using McParticles = aod::StoredMcParticles; + using Origins = aod::Origins; + using XSections = aod::HepMCXSections; + using PdfInfos = aod::HepMCPdfInfos; + using HeavyIons = aod::HepMCHeavyIons; + using MCKinematicsReader = steer::MCKinematicsReader; + /** */ private: + /** some other types we will use */ + using CollisionCursor = aodmchelpers::CollisionCursor; + using XSectionCursor = aodmchelpers::XSectionCursor; + using PdfInfoCursor = aodmchelpers::PdfInfoCursor; + using HeavyIonCursor = aodmchelpers::HeavyIonCursor; + using HepMCUpdate = aodmchelpers::HepMCUpdate; + /** + * Update the header (collision and HepMC aux) information. + * + * When updating the HepMC aux tables, we take the relevant policies + * into account (mXSectionUpdate, mPdfInfoUpdate, mHeavyIonUpdate). + * + * - If a policy is "never", then the corresponding table is never + * updated. + * + * - If the policy is "always", then the table is always + * update. + * + * - If the policy is either "anyKey" or "allKeys", _and_ + * this is the first event, then we check if any or all keys, + * respectively are present in the header. + * + * - If that check fails, then we do not update and set the + * corresponding policy to be "never". + * + * - If the check succeeds, then we do update the table, and set + * the corresponding policty to "always". + * + * In this way, we will let the first event decide what to do for + * subsequent events and thus avoid too many string comparisions. + * + * @param collisionCursor Cursor over aod::McCollisions + * @param xSectionCursor Cursor over aod::HepMCXSections + * @param pdfInfoCursor Cursor over aod::HepMCPdfInfos + * @param heavyIonCursor Cursor over aod::HepMCHeavyIons + * @param header Header to read information from + * @param collisionID Index of collision in table + * @param bcID Current event identifier (bcID) + * @param time Time of event + * @param generatorID Generator identifier, if any + * @param sourceID Source identifier + * + */ + void updateHeader(CollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, // Index + int bcID, + float time, + short generatorID, + int sourceID); + /** Current timeframe number */ int64_t mTFNumber{1}; - int mTruncate{1}; + /** Whether to filter tracks so that only primary particles, + particles from EG, or their parents are written out */ int mFilterMC{0}; + /** Whether to enable embedding */ bool mEnableEmbed{false}; - std::string mMCHeaderFNames; + /** LPM production tag, for anchoring */ TString mLPMProdTag{""}; + /** Pass identifier for anchoring */ TString mAnchorPass{""}; + /** Production identifier for anchoring */ TString mAnchorProd{""}; + /** Reconstruction pass for anchoring */ TString mRecoPass{""}; + /** Timer */ TStopwatch mTimer; + /** Rules for when to update HepMC tables */ + HepMCUpdate mXSectionUpdate = HepMCUpdate::anyKey; + HepMCUpdate mPdfInfoUpdate = HepMCUpdate::anyKey; + HepMCUpdate mHeavyIonUpdate = HepMCUpdate::anyKey; std::string mSimPrefix; - o2::aodhelpers::TripletsMap_t mToStore; - - // keep track event/source id for each mc-collision - // using map and not unordered_map to ensure - // correct ordering when iterating over container elements - std::vector> mMCColToEvSrc; - // MC production metadata holder - std::vector mMetaDataKeys; - std::vector mMetaDataVals; bool mIsMDSent{false}; // truncation is enabled by default @@ -72,10 +137,6 @@ class AODMcProducerWorkflowDPL : public Task uint32_t mMcParticleW = 0xFFFFFFF0; // 19 bits uint32_t mMcParticlePos = 0xFFFFFFF0; // 19 bits uint32_t mMcParticleMom = 0xFFFFFFF0; // 19 bits - - template - void fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor); }; /// create a processor spec diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h index f53b2a3082260..5351504443269 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h @@ -18,8 +18,7 @@ #include #include #include -#include -#include +#include namespace o2::aodhelpers { @@ -48,6 +47,15 @@ struct TripletEqualTo { typedef boost::unordered_map TripletsMap_t; +template +auto createTableCursor(framework::ProcessingContext& pc) +{ + framework::Produces c; + c.resetCursor(pc.outputs() + .make(framework::OutputForTable::ref())); + c.setLabel(aod::label()); + return c; +} } // namespace o2::aodhelpers #endif /* O2_AODPRODUCER_HELPERS */ diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 127c452d5772b..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -14,36 +14,29 @@ #ifndef O2_AODPRODUCER_WORKFLOW_SPEC #define O2_AODPRODUCER_WORKFLOW_SPEC -#include "CCDB/BasicCCDBManager.h" -#include "DataFormatsFT0/RecPoints.h" -#include "DataFormatsFDD/RecPoint.h" -#include "DataFormatsFV0/RecPoints.h" +#include "AODMcProducerHelpers.h" +#include "DataFormatsEMCAL/Cell.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsMFT/TrackMFT.h" -#include "DataFormatsMCH/TrackMCH.h" -#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsPHOS/Cell.h" #include "DataFormatsTRD/TrackTRD.h" -#include "DataFormatsZDC/BCRecData.h" -#include "DataFormatsEMCAL/EventHandler.h" -#include "DataFormatsPHOS/EventHandler.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisHelpers.h" +#include "DetectorsBase/Propagator.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" -#include "SimulationDataFormat/MCCompLabel.h" #include "Steer/MCKinematicsReader.h" -#include "TMap.h" #include "TStopwatch.h" - -#include +#include "ZDCBase/Constants.h" +#include "GlobalTracking/MatchGlobalFwd.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "CommonUtils/EnumFlags.h" + +#include +#include +#include #include - +#include using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; using GIndex = o2::dataformats::VtxTrackIndex; @@ -51,7 +44,6 @@ using DataRequest = o2::globaltracking::DataRequest; namespace o2::aodproducer { - /// A structure or container to organize bunch crossing data of a timeframe /// and to facilitate fast lookup and search within bunch crossings. class BunchCrossings @@ -74,15 +66,23 @@ class BunchCrossings /// return the sorted vector of increaing BC times std::vector const& getBCTimeVector() const { return mBCTimeVector; } - /// Performs a "lower bound" search for timestamp within the bunch crossing data. - /// Returns the smallest bunch crossing (index and value) equal or greater than timestamp. - /// The functions is expected to perform much better than - /// a binary search in the bunch crossing data directly. Expect O(1) instead of O(log(N)) at the cost - /// of the additional memory used by this class. + /// Performs a "lower bound" search for timestamp within the bunch + /// crossing data. + /// + /// Returns the smallest bunch crossing (index and value) equal or + /// greater than timestamp. + /// + /// The functions is expected to perform much better than a binary + /// search in the bunch crossing data directly. Expect O(1) instead + /// of O(log(N)) at the cost of the additional memory used by this + /// class. + /// + /// This is _not_ O(1). The loop below makes it at least O(N). The + /// call to std::lower_bound is O(log(N)). std::pair lower_bound(uint64_t timestamp) const { // a) determine the timewindow - const auto NofWindows = mTimeWindows.size(); + const auto NofWindows = static_cast(mTimeWindows.size()); const auto smallestBC = mBCTimeVector[0]; const auto largestBC = mBCTimeVector.back(); auto timeindex = std::max((int)0, (int)((timestamp - smallestBC) / mWindowSize)); @@ -158,19 +158,19 @@ class BunchCrossings mWindowSize = bcrange / (1. * window_number); // now we go through the list of times and bucket them into the correct windows mTimeWindows.resize(window_number); - for (int bcindex = 0; bcindex < mBCTimeVector.size(); ++bcindex) { + for (auto bcindex = 0U; bcindex < mBCTimeVector.size(); ++bcindex) { auto windowindex = (int)((mBCTimeVector[bcindex] - mBCTimeVector[0]) / mWindowSize); // we add "bcindex" to the TimeWindow windowindex auto& tw = mTimeWindows[windowindex]; if (tw.from == -1) { tw.from = bcindex; } else { - tw.from = std::min(tw.from, bcindex); + tw.from = std::min(tw.from, static_cast(bcindex)); } if (tw.to == -1) { tw.to = bcindex; } else { - tw.to = std::max(tw.to, bcindex); + tw.to = std::max(tw.to, static_cast(bcindex)); } } @@ -205,12 +205,17 @@ class BunchCrossings std::vector mTimeWindows; // the time window structure covering the complete duration of mBCTimeVector double mWindowSize; // the size of a single time window -}; // end internal class +}; // end internal class + +// Steering bits for additional output during AOD production +enum struct AODProducerStreamerFlags : uint8_t { + TrackQA, +}; class AODProducerWorkflowDPL : public Task { public: - AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true) : mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr), mEnableSV(enableSV), mUseMC(useMC) {} + AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true, bool enableFITextra = false) : mUseMC(useMC), mEnableSV(enableSV), mEnableFITextra(enableFITextra), mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr) {} ~AODProducerWorkflowDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -229,9 +234,33 @@ class AODProducerWorkflowDPL : public Task return std::uint64_t(mStartIR.toLong()) + relativeTime_to_LocalBC(relativeTimeStampInNS); } + bool mThinTracks{false}; + bool mPropTracks{false}; + bool mPropMuons{false}; + float mTrackQCKeepGlobalTracks{false}; + float mTrackQCRetainOnlydEdx{false}; + float mTrackQCFraction{0.00}; + int64_t mTrackQCNTrCut{4}; + float mTrackQCDCAxy{3.}; + float mTrackQCPt{0.2}; + int mTrackQCNCls{80}; + float mSqrtS{13860.}; + std::mt19937 mGenerator{}; ///< random generator for trackQA sampling + o2::base::Propagator::MatCorrType mMatCorr{o2::base::Propagator::MatCorrType::USEMatCorrLUT}; + o2::dataformats::MeanVertexObject mVtx; + float mMaxPropXiu{5.0f}; // max X_IU for which track is to be propagated if mPropTracks is true. (other option: o2::constants::geom::XTPCInnerRef + 0.1f) + + std::unordered_set mGIDUsedBySVtx; + std::unordered_set mGIDUsedByStr; + + o2::utils::EnumFlags mStreamerFlags; + std::shared_ptr mStreamer; + int mNThreads = 1; bool mUseMC = true; + bool mUseSigFiltMC = false; // enable signal filtering for MC with embedding bool mEnableSV = true; // enable secondary vertices + bool mEnableFITextra = false; bool mFieldON = false; const float cSpeed = 0.029979246f; // speed of light in TOF units @@ -245,7 +274,10 @@ class AODProducerWorkflowDPL : public Task TString mAnchorPass{""}; TString mAnchorProd{""}; TString mRecoPass{""}; + TString mUser{"aliprod"}; // who created this AOD (aliprod, alidaq, individual users) TStopwatch mTimer; + bool mEMCselectLeading{false}; + uint64_t mEMCALTrgClassMask = 0; // unordered map connects global indices and table indices of barrel tracks std::unordered_map mGIDToTableID; @@ -264,6 +296,7 @@ class AODProducerWorkflowDPL : public Task int mTableV0ID{0}; // Strangeness tracking indices lookup tables + std::vector mVertexStrLUT; /// LUT for accessing strangeness tracks for each vertex std::vector> mCollisionStrTrk; /// collision index and original index of the strangeness track std::vector mStrTrkIndices; /// indices of strangeness tracks in the track table @@ -288,6 +321,8 @@ class AODProducerWorkflowDPL : public Task std::vector mMCHROFs; // mapping from MCH tracks ID to corresponding ROF (for SA MCH tracks time extraction) double mITSROFrameHalfLengthNS = -1; // ITS ROF half length double mMFTROFrameHalfLengthNS = -1; // ITS ROF half length + double mITSROFBiasNS = 0; // ITS ROF start bias + double mMFTROFBiasNS = 0; // ITS ROF start bias double mNSigmaTimeTrack = -1; // number track errors sigmas (for gaussian errors only) used in track-vertex matching double mTimeMarginTrackTime = -1; // safety margin in NS used for track-vertex matching (additive to track uncertainty) double mTPCBinNS = -1; // inverse TPC time-bin in ns @@ -296,16 +331,18 @@ class AODProducerWorkflowDPL : public Task // Mapping of eventID, sourceID, trackID to some integer. // The first two indices are not sparse whereas the trackID index is sparse which explains // the combination of vector and map - std::vector*>> mToStore; + std::vector>> mToStore; + o2::steer::MCKinematicsReader* mMCKineReader = nullptr; //! // production metadata std::vector mMetaDataKeys; std::vector mMetaDataVals; - bool mIsMDSent{false}; std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; + o2::globaltracking::MatchGlobalFwd mMatching; + static constexpr int TOFTimePrecPS = 16; // required max error in ps for TOF tracks // truncation is enabled by default uint32_t mCollisionPosition = 0xFFFFFFF0; // 19 bits mantissa @@ -320,6 +357,7 @@ class AODProducerWorkflowDPL : public Task uint32_t mTrackCovOffDiag = 0xFFFF0000; // 7 bits uint32_t mTrackSignal = 0xFFFFFF00; // 15 bits uint32_t mTrackTime = 0xFFFFFFFF; // use full float precision for time + uint32_t mTPCTime0 = 0xFFFFFFE0; // 18 bits, providing 14256./(1<<19) = 0.027 TB precision e.g., ~0.13 mm in z uint32_t mTrackTimeError = 0xFFFFFF00; // 15 bits uint32_t mTrackPosEMCAL = 0xFFFFFF00; // 15 bits uint32_t mTracklets = 0xFFFFFF00; // 15 bits @@ -340,8 +378,11 @@ class AODProducerWorkflowDPL : public Task uint32_t mMuonCl = 0xFFFFFF00; // 15 bits uint32_t mMuonClErr = 0xFFFF0000; // 7 bits uint32_t mV0Time = 0xFFFFF000; // 11 bits + uint32_t mV0ChannelTime = 0xFFFFFF00; // 15 bits uint32_t mFDDTime = 0xFFFFF000; // 11 bits + uint32_t mFDDChannelTime = 0xFFFFFF00; // 15 bits uint32_t mT0Time = 0xFFFFFF00; // 15 bits + uint32_t mT0ChannelTime = 0xFFFFFFF0; // 19 bits uint32_t mV0Amplitude = 0xFFFFF000; // 11 bits uint32_t mFDDAmplitude = 0xFFFFF000; // 11 bits uint32_t mT0Amplitude = 0xFFFFF000; // 11 bits @@ -351,9 +392,11 @@ class AODProducerWorkflowDPL : public Task struct TrackExtraInfo { float tpcInnerParam = 0.f; uint32_t flags = 0; + uint32_t itsClusterSizes = 0u; uint8_t itsClusterMap = 0; uint8_t tpcNClsFindable = 0; int8_t tpcNClsFindableMinusFound = 0; + int8_t tpcNClsFindableMinusPID = 0; int8_t tpcNClsFindableMinusCrossedRows = 0; uint8_t tpcNClsShared = 0; uint8_t trdPattern = 0; @@ -369,7 +412,38 @@ class AODProducerWorkflowDPL : public Task float trackPhiEMCAL = -999.f; float trackTime = -999.f; float trackTimeRes = -999.f; + int diffBCRef = 0; // offset of time reference BC from the start of the orbit int bcSlice[2] = {-1, -1}; + bool isTPConly = false; // not to be written out + }; + + struct TrackQA { + GID trackID; + float tpcTime0{}; + float tpcdEdxNorm{}; + int16_t tpcdcaR{}; + int16_t tpcdcaZ{}; + uint8_t tpcClusterByteMask{}; + uint8_t tpcdEdxMax0R{}; + uint8_t tpcdEdxMax1R{}; + uint8_t tpcdEdxMax2R{}; + uint8_t tpcdEdxMax3R{}; + uint8_t tpcdEdxTot0R{}; + uint8_t tpcdEdxTot1R{}; + uint8_t tpcdEdxTot2R{}; + uint8_t tpcdEdxTot3R{}; + int8_t dRefContY{std::numeric_limits::min()}; + int8_t dRefContZ{std::numeric_limits::min()}; + int8_t dRefContSnp{std::numeric_limits::min()}; + int8_t dRefContTgl{std::numeric_limits::min()}; + int8_t dRefContQ2Pt{std::numeric_limits::min()}; + int8_t dRefGloY{std::numeric_limits::min()}; + int8_t dRefGloZ{std::numeric_limits::min()}; + int8_t dRefGloSnp{std::numeric_limits::min()}; + int8_t dRefGloTgl{std::numeric_limits::min()}; + int8_t dRefGloQ2Pt{std::numeric_limits::min()}; + int8_t dTofdX{std::numeric_limits::min()}; + int8_t dTofdZ{std::numeric_limits::min()}; }; // helper struct for addToFwdTracksTable() @@ -420,8 +494,6 @@ class AODProducerWorkflowDPL : public Task // using -1 as dummies for AOD struct MCLabels { uint32_t labelID = -1; - uint32_t labelITS = -1; - uint32_t labelTPC = -1; uint16_t labelMask = 0; uint8_t fwdLabelMask = 0; }; @@ -443,7 +515,6 @@ class AODProducerWorkflowDPL : public Task const std::vector& mcRecords, std::map& bcsMap); - uint64_t getTFNumber(const o2::InteractionRecord& tfStartIR, int runNumber); template void addToTracksTable(TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, const o2::track::TrackParCov& track, int collisionID, aod::track::TrackTypeEnum type = aod::track::TrackIU); @@ -451,25 +522,31 @@ class AODProducerWorkflowDPL : public Task template void addToTracksExtraTable(TracksExtraCursorType& tracksExtraCursor, TrackExtraInfo& extraInfoHolder); + template + void addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder); + template void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); - template - void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + template + void addToFwdTracksTable(fwdTracksCursorType& fwdTracksCursor, fwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap); TrackExtraInfo processBarrelTrack(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, const o2::globaltracking::RecoContainer& data, const std::map& bcsMap); + TrackQA processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, const o2::globaltracking::RecoContainer& data, const std::map& bcsMap); + + bool propagateTrackToPV(o2::track::TrackParametrizationWithError& trackPar, const o2::globaltracking::RecoContainer& data, int colID); void extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track); void cacheTriggers(const o2::globaltracking::RecoContainer& recoData); // helper for track tables // * fills tables collision by collision // * interaction time is for TOF information - template + template void fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, const o2::dataformats::VtxTrackRef& trackRef, @@ -478,65 +555,138 @@ class AODProducerWorkflowDPL : public Task TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, + TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + FwdTrkClsCursorType& fwdTrkClsCursor, const std::map& bcsMap); + template + void addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId); + void fillIndexTablesPerCollision(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data); template void fillSecondaryVertices(const o2::globaltracking::RecoContainer& data, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3bodyCursorType& decay3bodyCursor); + template + void fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor); + void prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData); template void fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& data, V0C& v0Cursor, CC& cascadeCursor, D3BC& decay3bodyCursor); - template + /** some other types we will use */ + using MCCollisionCursor = aodmchelpers::CollisionCursor; + using XSectionCursor = aodmchelpers::XSectionCursor; + using PdfInfoCursor = aodmchelpers::PdfInfoCursor; + using HeavyIonCursor = aodmchelpers::HeavyIonCursor; + using MCParticlesCursor = aodmchelpers::ParticleCursor; + using HepMCUpdate = aodmchelpers::HepMCUpdate; + using MCEventHeader = dataformats::MCEventHeader; + /** Rules for when to update HepMC tables */ + HepMCUpdate mXSectionUpdate = HepMCUpdate::anyKey; + HepMCUpdate mPdfInfoUpdate = HepMCUpdate::anyKey; + HepMCUpdate mHeavyIonUpdate = HepMCUpdate::anyKey; + /** + * Update the header (collision and HepMC aux) information. + * + * When updating the HepMC aux tables, we take the relevant policies + * into account (mXSectionUpdate, mPdfInfoUpdate, mHeavyIonUpdate). + * + * - If a policy is "never", then the corresponding table is never + * updated. + * + * - If the policy is "always", then the table is always + * update. + * + * - If the policy is either "anyKey" or "allKeys", _and_ + * this is the first event, then we check if any or all keys, + * respectively are present in the header. + * + * - If that check fails, then we do not update and set the + * corresponding policy to be "never". + * + * - If the check succeeds, then we do update the table, and set + * the corresponding policty to "always". + * + * In this way, we will let the first event decide what to do for + * subsequent events and thus avoid too many string comparisions. + * + * @param collisionCursor Cursor over aod::McCollisions + * @param xSectionCursor Cursor over aod::HepMCXSections + * @param pdfInfoCursor Cursor over aod::HepMCPdfInfos + * @param heavyIonCursor Cursor over aod::HepMCHeavyIons + * @param header Header to read information from + * @param collisionID Index of collision in the table + * @param bcID Current event identifier (bcID) + * @param time Time of event + * @param generatorID Generator identifier, if any + * @param sourceID Source identifier + * + */ + void updateMCHeader(MCCollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, + int bcID, + float time, + short generatorID, + int sourceID); + void fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor, + MCParticlesCursor& mcParticlesCursor, const gsl::span& primVer2TRefs, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data, const std::vector>& mcColToEvSrc); template - void fillMCTrackLabelsTable(const MCTrackLabelCursorType& mcTrackLabelCursor, - const MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, - const MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, + void fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor, + MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, + MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& primVerGIs, - const o2::globaltracking::RecoContainer& data); + const o2::globaltracking::RecoContainer& data, + int vertexId = -1); std::uint64_t fillBCSlice(int (&slice)[2], double tmin, double tmax, const std::map& bcsMap) const; + std::vector fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map& bcsMap) const; + // helper for tpc clusters void countTPCClusters(const o2::globaltracking::RecoContainer& data); // helper for trd pattern uint8_t getTRDPattern(const o2::trd::TrackTRD& track); - template - void addToCaloTable(const TCaloHandler& caloHandler, const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - int eventID, int bcID, int8_t caloType); + template + void addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType); - template - void fillCaloTable(const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - const std::map& bcsMap, + template + void fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map& bcsMap, const o2::globaltracking::RecoContainer& data); + + std::set filterEMCALIncomplete(const gsl::span triggers); }; /// create a processor spec -framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun); +framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun, bool enableFITextra); // helper interface for calo cells to "befriend" emcal and phos cells class CellHelper { public: - static int8_t getTriggerBits(const o2::emcal::Cell& cell) + static int8_t getTriggerBits(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } @@ -569,7 +719,7 @@ class CellHelper return cell.getTRU(); } - static int16_t getFastOrAbsID(const o2::emcal::Cell& cell) + static int16_t getFastOrAbsID(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } @@ -589,7 +739,7 @@ class CellHelper return cell.getEnergy(); } - static int16_t getLnAmplitude(const o2::emcal::Cell& cell) + static int16_t getLnAmplitude(const o2::emcal::Cell& /*cell*/) { return 0; // dummy value } diff --git a/Detectors/AOD/src/AODMcProducerHelpers.cxx b/Detectors/AOD/src/AODMcProducerHelpers.cxx new file mode 100644 index 0000000000000..a7093e0048c25 --- /dev/null +++ b/Detectors/AOD/src/AODMcProducerHelpers.cxx @@ -0,0 +1,436 @@ +// Copyright 2023-2099 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AODMcProducerHelpers.h +/// @author Christian Holm Christensen +/// common helpers for AOD MC producers +#include "AODProducerWorkflow/AODMcProducerHelpers.h" +#include +#include +#include +#include +#define verbosity debug + +namespace o2::aodmchelpers +{ +//================================================================== +bool hasKeys(o2::dataformats::MCEventHeader const& header, + const std::vector& keys, + bool anyNotAll) +{ + auto check = [&header](const std::string& key) { // Do not format + return header.hasInfo(key); + }; + return (anyNotAll ? // Do not format + std::any_of(keys.cbegin(), keys.cend(), check) + : // Do not format + std::all_of(keys.cbegin(), keys.cend(), check)); +} +//==================================================================== +short updateMCCollisions(const CollisionCursor& cursor, + int bcId, + float time, + o2::dataformats::MCEventHeader const& header, + short generatorId, + int sourceId, + unsigned int mask) +{ + using Key = o2::dataformats::MCInfoKeys; + using GenProp = o2::mcgenid::GeneratorProperty; + using namespace o2::math_utils; + + int subGenId = getEventInfo(header, GenProp::SUBGENERATORID, -1); + int genId = getEventInfo(header, GenProp::GENERATORID, + int(generatorId)); + float weight = getEventInfo(header, Key::weight, 1.f); + + auto encodedGeneratorId = o2::mcgenid::getEncodedGenId(genId, + sourceId, + subGenId); + + LOG(verbosity) << "Updating MC Collisions table w/bcId=" << bcId; + cursor(0, + bcId, + encodedGeneratorId, + truncateFloatFraction(header.GetX(), mask), + truncateFloatFraction(header.GetY(), mask), + truncateFloatFraction(header.GetZ(), mask), + truncateFloatFraction(time, mask), + truncateFloatFraction(weight, mask), + header.GetB(), + getEventInfo(header, Key::planeAngle, header.GetRotZ())); + return encodedGeneratorId; +} +//-------------------------------------------------------------------- +bool updateHepMCXSection(const XSectionCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = o2::dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and + not hasKeys(header, { // Do not + Key::acceptedEvents, // mess with + Key::attemptedEvents, // with + Key::xSection, // my + Key::xSectionError}, // formatting + when == HepMCUpdate::anyKey))) { + return false; + } + + LOG(verbosity) << "Updating HepMC cross-section table w/collisionId " + << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::acceptedEvents, 0), + getEventInfo(header, Key::attemptedEvents, 0), + getEventInfo(header, Key::xSection, 0.f), + getEventInfo(header, Key::xSectionError, 0.f), + getEventInfo(header, Key::eventScale, 1.f), + getEventInfo(header, Key::mpi, -1), + getEventInfo(header, Key::processCode, -1)); + return true; +} +//-------------------------------------------------------------------- +bool updateHepMCPdfInfo(const PdfInfoCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = o2::dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and // Do + not hasKeys(header, {Key::pdfParton1Id, // not + Key::pdfParton2Id, // mess + Key::pdfCode1, // with + Key::pdfCode2, // my + Key::pdfX1, // formatting + Key::pdfX2, // . + Key::pdfScale, // It + Key::pdfXF1, // is + Key::pdfXF2}, // better + when == HepMCUpdate::anyKey))) { + return false; + } + + LOG(verbosity) << "Updating HepMC PDF table w/collisionId " << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::pdfParton1Id, int(0)), + getEventInfo(header, Key::pdfParton2Id, int(0)), + getEventInfo(header, Key::pdfCode1, int(0)), + getEventInfo(header, Key::pdfCode2, int(0)), + getEventInfo(header, Key::pdfX1, 0.f), + getEventInfo(header, Key::pdfX2, 0.f), + getEventInfo(header, Key::pdfScale, 1.f), + getEventInfo(header, Key::pdfXF1, 0.f), + getEventInfo(header, Key::pdfXF2, 0.f)); + + return true; +} +//-------------------------------------------------------------------- +bool updateHepMCHeavyIon(const HeavyIonCursor& cursor, + int collisionID, + short generatorID, + o2::dataformats::MCEventHeader const& header, + HepMCUpdate when) +{ + using Key = dataformats::MCInfoKeys; + + if (when == HepMCUpdate::never or + (when != HepMCUpdate::always and // clang + not hasKeys(header, {Key::nCollHard, // format + Key::nPartProjectile, // is + Key::nPartTarget, // so + Key::nColl, // annoying + Key::nCollNNWounded, // . + Key::nCollNWoundedN, // It + Key::nCollNWoundedNwounded, // messes + Key::nSpecProjectileNeutron, // up + Key::nSpecTargetNeutron, // the + Key::nSpecProjectileProton, // clarity + Key::nSpecTargetProton, // of + Key::planeAngle, // the + "eccentricity", // code + Key::sigmaInelNN, // to + Key::centrality}, // noavail + when == HepMCUpdate::anyKey))) { + return false; + } + + int specNeutrons = (getEventInfo(header, Key::nSpecProjectileNeutron, -1) + + getEventInfo(header, Key::nSpecTargetNeutron, -1)); + int specProtons = (getEventInfo(header, Key::nSpecProjectileProton, -1) + + getEventInfo(header, Key::nSpecTargetProton, -1)); + + LOG(verbosity) << "Updating HepMC heavy-ion table w/collisionID " + << collisionID; + cursor(0, + collisionID, + generatorID, + getEventInfo(header, Key::nCollHard, -1), + getEventInfo(header, Key::nPartProjectile, -1), + getEventInfo(header, Key::nPartTarget, -1), + getEventInfo(header, Key::nColl, -1), + getEventInfo(header, Key::nCollNNWounded, -1), + getEventInfo(header, Key::nCollNWoundedN, -1), + getEventInfo(header, Key::nCollNWoundedNwounded, -1), + specNeutrons, + specProtons, + header.GetB(), + getEventInfo(header, Key::planeAngle, header.GetRotZ()), + getEventInfo(header, "eccentricity", 0), + getEventInfo(header, Key::sigmaInelNN, 0.), + getEventInfo(header, Key::centrality, -1)); + + return true; +} +//-------------------------------------------------------------------- +void updateParticle(const ParticleCursor& cursor, + const TrackToIndex& toStore, + int collisionID, + o2::MCTrack const& track, + std::vector const& tracks, + uint8_t flags, + uint32_t weightMask, + uint32_t momentumMask, + uint32_t positionMask) +{ + using o2::mcutils::MCTrackNavigator; + using namespace o2::aod::mcparticle::enums; + using namespace o2::math_utils; + using namespace o2::mcgenstatus; + + auto mapping = [&toStore](int trackNo) { + auto iter = toStore.find(trackNo); + if (iter == toStore.end()) { + return -1; + } + return iter->second; + }; + + auto statusCode = track.getStatusCode().fullEncoding; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + if (not track.isPrimary()) { + flags |= ProducedByTransport; + statusCode = track.getProcess(); + } + if (MCTrackNavigator::isPhysicalPrimary(track, tracks)) { + flags |= PhysicalPrimary; + } + + int daughters[2] = {-1, -1}; + std::vector mothers; + int id; + if ((id = mapping(track.getMotherTrackId())) >= 0) { + mothers.push_back(id); + } + if ((id = mapping(track.getSecondMotherTrackId())) >= 0) { + mothers.push_back(id); + } + if ((id = mapping(track.getFirstDaughterTrackId())) >= 0) { + daughters[0] = id; + } + if ((id = mapping(track.getLastDaughterTrackId())) >= 0) { + daughters[1] = id; + } else { + daughters[1] = daughters[0]; + } + if (daughters[0] < 0 and daughters[1] >= 0) { + LOG(error) << "Problematic daughters: " << daughters[0] << " and " + << daughters[1]; + daughters[0] = daughters[1]; + } + if (daughters[0] > daughters[1]) { + std::swap(daughters[0], daughters[1]); + } + + float weight = track.getWeight(); + float pX = float(track.Px()); + float pY = float(track.Py()); + float pZ = float(track.Pz()); + float energy = float(track.GetEnergy()); + float vX = float(track.Vx()); + float vY = float(track.Vy()); + float vZ = float(track.Vz()); + float time = float(track.T()); + + LOG(verbosity) << "McParticle collisionId=" << collisionID << "," + << "status=" << statusCode << "," + << "hepmc=" << hepmc << "," + << "pdg=" << track.GetPdgCode() << "," + << "nMothers=" << mothers.size() << "," + << "daughters=" << daughters[0] << "," + << daughters[1]; + + cursor(0, + collisionID, + track.GetPdgCode(), + statusCode, + flags, + mothers, + daughters, + truncateFloatFraction(weight, weightMask), + truncateFloatFraction(pX, momentumMask), + truncateFloatFraction(pY, momentumMask), + truncateFloatFraction(pZ, momentumMask), + truncateFloatFraction(energy, momentumMask), + truncateFloatFraction(vX, positionMask), + truncateFloatFraction(vY, positionMask), + truncateFloatFraction(vZ, positionMask), + truncateFloatFraction(time, positionMask)); +} +//-------------------------------------------------------------------- +uint32_t updateParticles(const ParticleCursor& cursor, + int collisionID, + std::vector const& tracks, + TrackToIndex& preselect, + uint32_t offset, + bool filter, + bool background, + uint32_t weightMask, + uint32_t momentumMask, + uint32_t positionMask, + bool signalFilter) +{ + using o2::mcutils::MCTrackNavigator; + using namespace o2::aod::mcparticle::enums; + using namespace o2::mcgenstatus; + + // First loop over particles to find out which to store + // TrackToIndex toStore(preselect.begin(), preselect.end()); + // + // Guess we need to modifiy the passed in mapping so that MC labels + // can be set correctly + TrackToIndex& toStore = preselect; + + // Mapping lambda. This maps the track number to the index into + // the table exported. + auto mapping = [&toStore](int trackNo) { + auto iter = toStore.find(trackNo); + if (iter == toStore.end()) { + return -1; + } + return iter->second; + }; + + LOG(verbosity) << "Got a total of " << tracks.size(); + for (int trackNo = tracks.size() - 1; trackNo >= 0; trackNo--) { + auto& track = tracks[trackNo]; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + if (filter) { + if (toStore.find(trackNo) == toStore.end() and + /* The above test is in-correct. The track may be stored in + the list, but with a negative value. In that case, the + above test will still check mothers and daughters, and + possible store them in the output. This is however how + it is done in current `dev` branch, and so to enable + comparison on closure, we do this test for now. The + correct way it commented out below. */ + // mapping(trackNo) < 0 and + not track.isPrimary() and + not MCTrackNavigator::isPhysicalPrimary(track, tracks) and + not MCTrackNavigator::isKeepPhysics(track, tracks)) { + LOG(verbosity) << "Skipping track " << trackNo << " " << hepmc << " " + << mapping(trackNo) << " " + << track.isPrimary() << " " + << MCTrackNavigator::isPhysicalPrimary(track, tracks) + << " " + << MCTrackNavigator::isKeepPhysics(track, tracks); + continue; + } + } + if (background && signalFilter) { + continue; + } + + // Store this particle. We mark that putting a 1 in the + // `toStore` mapping. This will later on be updated with the + // actual index into the table + toStore[trackNo] = 1; + + // If we're filtering tracks, then also mark mothers and + // daughters(?) to be stored. + if (filter) { + int id; + if ((id = track.getMotherTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getSecondMotherTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getFirstDaughterTrackId()) >= 0) { + toStore[id] = 1; + } + if ((id = track.getLastDaughterTrackId()) >= 0) { + toStore[id] = 1; + } + } + } + + // Second loop to set indexes. This is needed to be done before + // we actually update the table, because a particle may point to a + // later particle. + LOG(verbosity) << "Will store " << toStore.size() << " particles"; + size_t index = 0; + + for (size_t trackNo = 0U; trackNo < tracks.size(); trackNo++) { + auto storeIt = mapping(trackNo); + if (storeIt < 0) { + continue; + } + + toStore[trackNo] = offset + index; + index++; + } + + // Make sure we have the right number + assert(index == toStore.size()); + LOG(verbosity) << "Starting index " << offset << ", last index " + << offset + index << " " + << "Selected " << toStore.size() << " tracks out of " + << tracks.size() << " " + << "Collision # " << collisionID; + // Third loop to actually store the particles in the order given + for (size_t trackNo = 0U; trackNo < tracks.size(); trackNo++) { + auto storeIt = mapping(trackNo); + if (storeIt < 0) { + continue; + } + + auto& track = tracks[trackNo]; + auto hepmc = getHepMCStatusCode(track.getStatusCode()); + uint8_t flags = (background ? FromBackgroundEvent : 0); + updateParticle(cursor, + toStore, + collisionID, + track, + tracks, + flags, + weightMask, + momentumMask, + positionMask); + } + LOG(verbosity) << "Return new offset " << offset + index; + return offset + index; +} +} // namespace o2::aodmchelpers + +// Local Variables: +// mode: C++ +// End: diff --git a/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx index ca7c089983605..8136bd3e6d268 100644 --- a/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODMcProducerWorkflowSpec.cxx @@ -13,164 +13,18 @@ #include "AODProducerWorkflow/AODMcProducerWorkflowSpec.h" #include "AODProducerWorkflow/AODProducerHelpers.h" -#include "MathUtils/Utils.h" -#include "CCDB/BasicCCDBManager.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "Framework/AnalysisDataModel.h" #include "Framework/ControlService.h" #include "Framework/DataTypes.h" -#include "Framework/InputRecordWalker.h" -#include "Framework/Logger.h" -#include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" -#include "Framework/CCDBParamSpec.h" -#include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCUtils.h" #include "O2Version.h" -#include "TMath.h" #include "TString.h" -#include using namespace o2::framework; -using namespace o2::math_utils::detail; namespace o2::aodmcproducer { -template -void AODMcProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor) -{ - using o2::aodhelpers::Triplet_t; - - int tableIndex = 1; - for (auto& colInfo : mMCColToEvSrc) { // loop over " <-> combined MC col. ID" key pairs - int event = colInfo[2]; - int source = colInfo[1]; - int mcColId = colInfo[0]; - std::vector const& mcParticles = mcReader.getTracks(source, event); - // mark tracks to be stored per event - // loop over stack of MC particles from end to beginning: daughters are stored after mothers - if (mFilterMC) { - for (int particle = mcParticles.size() - 1; particle >= 0; particle--) { - // we store all primary particles == particles put by generator - if (mcParticles[particle].isPrimary() || - o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles) || - o2::mcutils::MCTrackNavigator::isKeepPhysics(mcParticles[particle], mcParticles)) { - mToStore[Triplet_t(source, event, particle)] = 1; - } else { - continue; - } - // we store mothers and daughters of particles that are to be saved - int mother0 = mcParticles[particle].getMotherTrackId(); - if (mother0 != -1) { - mToStore[Triplet_t(source, event, mother0)] = 1; - } - int mother1 = mcParticles[particle].getSecondMotherTrackId(); - if (mother1 != -1) { - mToStore[Triplet_t(source, event, mother1)] = 1; - } - int daughter0 = mcParticles[particle].getFirstDaughterTrackId(); - if (daughter0 != -1) { - mToStore[Triplet_t(source, event, daughter0)] = 1; - } - int daughterL = mcParticles[particle].getLastDaughterTrackId(); - if (daughterL != -1) { - mToStore[Triplet_t(source, event, daughterL)] = 1; - } - } - // enumerate saved mc particles and their relatives to get mother/daughter relations - for (int particle = 0; particle < mcParticles.size(); particle++) { - auto mapItem = mToStore.find(Triplet_t(source, event, particle)); - if (mapItem != mToStore.end()) { - mapItem->second = tableIndex - 1; - tableIndex++; - } - } - } else { - // if all mc particles are stored, all mc particles will be enumerated - for (int particle = 0; particle < mcParticles.size(); particle++) { - mToStore[Triplet_t(source, event, particle)] = tableIndex - 1; - tableIndex++; - } - } - - // second part: fill survived mc tracks into the AOD table - for (int particle = 0; particle < mcParticles.size(); particle++) { - if (mToStore.find(Triplet_t(source, event, particle)) == mToStore.end()) { - continue; - } - int statusCode = 0; - uint8_t flags = 0; - if (!mcParticles[particle].isPrimary()) { - flags |= o2::aod::mcparticle::enums::ProducedByTransport; // mark as produced by transport - statusCode = mcParticles[particle].getProcess(); - } else { - statusCode = mcParticles[particle].getStatusCode().fullEncoding; - } - if (source == 0) { - flags |= o2::aod::mcparticle::enums::FromBackgroundEvent; // mark as particle from background event - } - if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; // mark as physical primary - } - float weight = mcParticles[particle].getWeight(); - std::vector mothers; - int mcMother0 = mcParticles[particle].getMotherTrackId(); - auto item = mToStore.find(Triplet_t(source, event, mcMother0)); - if (item != mToStore.end()) { - mothers.push_back(item->second); - } - int mcMother1 = mcParticles[particle].getSecondMotherTrackId(); - item = mToStore.find(Triplet_t(source, event, mcMother1)); - if (item != mToStore.end()) { - mothers.push_back(item->second); - } - int daughters[2] = {-1, -1}; // slice - int mcDaughter0 = mcParticles[particle].getFirstDaughterTrackId(); - item = mToStore.find(Triplet_t(source, event, mcDaughter0)); - if (item != mToStore.end()) { - daughters[0] = item->second; - } - int mcDaughterL = mcParticles[particle].getLastDaughterTrackId(); - item = mToStore.find(Triplet_t(source, event, mcDaughterL)); - if (item != mToStore.end()) { - daughters[1] = item->second; - if (daughters[0] < 0) { - LOG(error) << "AOD problematic daughter case observed"; - daughters[0] = daughters[1]; /// Treat the case of first negative label (pruned in the kinematics) - } - } else { - daughters[1] = daughters[0]; - } - if (daughters[0] > daughters[1]) { - std::swap(daughters[0], daughters[1]); - } - auto pX = (float)mcParticles[particle].Px(); - auto pY = (float)mcParticles[particle].Py(); - auto pZ = (float)mcParticles[particle].Pz(); - auto energy = (float)mcParticles[particle].GetEnergy(); - mcParticlesCursor(0, - mcColId, - mcParticles[particle].GetPdgCode(), - statusCode, - flags, - mothers, - daughters, - truncateFloatFraction(weight, mMcParticleW), - truncateFloatFraction(pX, mMcParticleMom), - truncateFloatFraction(pY, mMcParticleMom), - truncateFloatFraction(pZ, mMcParticleMom), - truncateFloatFraction(energy, mMcParticleMom), - truncateFloatFraction((float)mcParticles[particle].Vx(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vy(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vz(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].T(), mMcParticlePos)); - } - mcReader.releaseTracksForSourceAndEvent(source, event); - } -} - +//------------------------------------------------------------------ void AODMcProducerWorkflowDPL::init(InitContext& ic) { mTimer.Stop(); @@ -180,13 +34,13 @@ void AODMcProducerWorkflowDPL::init(InitContext& ic) mRecoPass = ic.options().get("reco-pass"); mTFNumber = ic.options().get("aod-timeframe-id"); mFilterMC = ic.options().get("filter-mctracks"); - mTruncate = ic.options().get("enable-truncation"); + int truncate = ic.options().get("enable-truncation"); if (mTFNumber == -1L) { LOG(info) << "TFNumber will be obtained from CCDB"; } // set no truncation if selected by user - if (mTruncate != 1) { + if (truncate == 0) { LOG(info) << "Truncation is not used!"; mCollisionPosition = 0xFFFFFFFF; mMcParticleW = 0xFFFFFFFF; @@ -200,112 +54,223 @@ void AODMcProducerWorkflowDPL::init(InitContext& ic) // parse list of sim prefixes into vector mSimPrefix = ic.options().get("mckine-fname"); } + std::string hepmcUpdate = ic.options().get("hepmc-update"); + HepMCUpdate when = (hepmcUpdate == "never" // + ? HepMCUpdate::never // + : hepmcUpdate == "always" // + ? HepMCUpdate::always // + : hepmcUpdate == "all" // + ? HepMCUpdate::allKeys // + : HepMCUpdate::anyKey); + mXSectionUpdate = when; + mPdfInfoUpdate = when; + mHeavyIonUpdate = when; mTimer.Reset(); } +//------------------------------------------------------------------ +void AODMcProducerWorkflowDPL::updateHeader(CollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, // Index + int bcID, + float time, + short generatorID, + int sourceID) +{ + using aodmchelpers::updateHepMCHeavyIon; + using aodmchelpers::updateHepMCPdfInfo; + using aodmchelpers::updateHepMCXSection; + using aodmchelpers::updateMCCollisions; + + auto genID = updateMCCollisions(collisionCursor, + bcID, + time, + header, + generatorID, + sourceID, + mCollisionPosition); + mXSectionUpdate = (updateHepMCXSection(xSectionCursor, // + collisionID, // + genID, // + header, // + mXSectionUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); + mPdfInfoUpdate = (updateHepMCPdfInfo(pdfInfoCursor, // + collisionID, // + genID, // + header, // + mPdfInfoUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); + mHeavyIonUpdate = (updateHepMCHeavyIon(heavyIonCursor, // + collisionID, // + genID, // + header, // + mHeavyIonUpdate) // + ? HepMCUpdate::always // + : HepMCUpdate::never); +} + +//------------------------------------------------------------------ void AODMcProducerWorkflowDPL::run(ProcessingContext& pc) { mTimer.Start(false); uint64_t tfNumber = mTFNumber; - auto& mcCollisionsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISION"}); - auto& mcParticlesBuilder = pc.outputs().make(Output{"AOD", "MCPARTICLE_001"}); - auto& originTableBuilder = pc.outputs().make(Output{"AOD", "ORIGIN"}); - - auto mcCollisionsCursor = mcCollisionsBuilder.cursor(); - auto mcParticlesCursor = mcParticlesBuilder.cursor(); - auto originCursor = originTableBuilder.cursor(); - - std::unique_ptr mcReader; - - if (!mEnableEmbed) { - mcReader = std::make_unique(mSimPrefix, steer::MCKinematicsReader::Mode::kMCKine); + using namespace o2::aodmchelpers; + using namespace o2::aodhelpers; + + auto collisionsCursor = createTableCursor(pc); + auto particlesCursor = createTableCursor(pc); + auto originCursor = createTableCursor(pc); + auto xSectionCursor = createTableCursor(pc); + auto pdfInfoCursor = createTableCursor(pc); + auto heavyIonCursor = createTableCursor(pc); + + // --- Create our reader ------------------------------------------- + std::unique_ptr reader; + if (not mEnableEmbed) { + reader = + std::make_unique(mSimPrefix, + MCKinematicsReader::Mode::kMCKine); } else { - mcReader = std::make_unique("collisioncontext.root"); + reader = std::make_unique("collisioncontext.root"); } - // filling mcCollision table - - // TODO: figure out collision weight - float mcColWeight = 1.; + // --- Container of event indexes --------------------------------- + using EventInfo = std::vector>; + EventInfo eventInfo; + // --- Fill collision and HepMC aux tables ------------------------ // dummy time information - int bcID = 0; float time = 0; - auto updateMCCollisions = [this, mcColWeight, bcID, time, &mcCollisionsCursor](dataformats::MCEventHeader const& header, short generatorID) { - mcCollisionsCursor(0, - bcID, - generatorID, - truncateFloatFraction(header.GetX(), mCollisionPosition), - truncateFloatFraction(header.GetY(), mCollisionPosition), - truncateFloatFraction(header.GetZ(), mCollisionPosition), - truncateFloatFraction(time, mCollisionPosition), - truncateFloatFraction(mcColWeight, mCollisionPosition), - header.GetB()); - }; - - if (!mEnableEmbed) { // simply store all MC events into table + if (not mEnableEmbed) { + // simply store all MC events into table int icol = 0; - int nSources = mcReader->getNSources(); + int nSources = reader->getNSources(); for (int isrc = 0; isrc < nSources; isrc++) { short generatorID = isrc; - int nEvents = mcReader->getNEvents(isrc); + int nEvents = reader->getNEvents(isrc); for (int ievt = 0; ievt < nEvents; ievt++) { - auto& header = mcReader->getMCEventHeader(isrc, ievt); - updateMCCollisions(header, generatorID); - mMCColToEvSrc.emplace_back(std::vector{icol, isrc, ievt}); + auto& header = reader->getMCEventHeader(isrc, ievt); + updateHeader(collisionsCursor.cursor, + xSectionCursor.cursor, + pdfInfoCursor.cursor, + heavyIonCursor.cursor, + header, + ievt, + ievt, // BC is the same as collision index + time, + generatorID, + isrc); + + eventInfo.emplace_back(std::make_tuple(icol, isrc, ievt)); icol++; } } - } else { // treat embedded events using collisioncontext: injected events will be stored together with background events into the same collisions - int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions(); - const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords(); - const auto& mcParts = mcReader->getDigitizationContext()->getEventParts(); - for (int icol = 0; icol < nMCCollisions; icol++) { - auto& colParts = mcParts[icol]; + } else { + // treat embedded events using collisioncontext: injected events + // will be stored together with background events into the same + // collisions + int nCollisions = reader->getDigitizationContext()->getNCollisions(); + const auto& records = reader->getDigitizationContext()->getEventRecords(); + const auto& parts = reader->getDigitizationContext()->getEventParts(); + for (int icol = 0; icol < nCollisions; icol++) { + auto& colParts = parts[icol]; auto nParts = colParts.size(); for (auto colPart : colParts) { auto eventID = colPart.entryID; auto sourceID = colPart.sourceID; - // enable embedding: if several colParts exist, then they are saved as one collision + // enable embedding: if several colParts exist, then they are + // saved as one collision if (nParts == 1 || sourceID == 0) { + // Make collision header from first source only short generatorID = sourceID; - auto& header = mcReader->getMCEventHeader(sourceID, eventID); - updateMCCollisions(header, generatorID); + auto& header = reader->getMCEventHeader(sourceID, eventID); + + updateHeader(collisionsCursor.cursor, + xSectionCursor.cursor, + pdfInfoCursor.cursor, + heavyIonCursor.cursor, + header, + icol, + icol, // BC is the same as collision index + time, + generatorID, + sourceID); } - mMCColToEvSrc.emplace_back(std::vector{icol, sourceID, eventID}); // point background and injected signal events to one collision + // point background and injected signal events to one collision + eventInfo.emplace_back(std::make_tuple(icol, sourceID, eventID)); } } } - std::sort(mMCColToEvSrc.begin(), mMCColToEvSrc.end(), - [](const std::vector& left, const std::vector& right) { return (left[0] < right[0]); }); - - // filling mc particles table - fillMCParticlesTable(*mcReader, mcParticlesCursor); - - mMCColToEvSrc.clear(); - mToStore.clear(); - - originCursor(0, tfNumber); + // Sort the event information + std::sort(eventInfo.begin(), eventInfo.end(), + [](typename EventInfo::const_reference left, + typename EventInfo::const_reference right) { // + return (std::get<0>(left) < std::get<0>(right)); + }); + + // Loop over available events and update the tracks table + size_t offset = 0; + for (auto& colInfo : eventInfo) { + int event = std::get<2>(colInfo); + int source = std::get<1>(colInfo); + int collisionID = std::get<0>(colInfo); + auto tracks = reader->getTracks(source, event); + + TrackToIndex preselect; + offset = updateParticles(particlesCursor.cursor, + collisionID, + tracks, + preselect, + offset, + mFilterMC, + source == 0, + mMcParticleW, + mMcParticleMom, + mMcParticlePos); + + reader->releaseTracksForSourceAndEvent(source, event); + } - // sending metadata to writer - if (!mIsMDSent) { - TString dataType = "MC"; - TString O2Version = o2::fullVersion(); - TString ROOTVersion = ROOT_RELEASE; - mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag"}; - mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag}; - pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0, Lifetime::Timeframe}, mMetaDataKeys); - pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0, Lifetime::Timeframe}, mMetaDataVals); + // --- Update the origin and the time-frame ------------------------ + originCursor(tfNumber); + + // --- Sending metadata to writer if not done already -------------- + if (not mIsMDSent) { + TString o2Version = o2::fullVersion(); + std::vector metaDataKeys = {"DataType", + "Run", + "O2Version", + "ROOTVersion", + "RecoPassName", + "AnchorProduction", + "AnchorPassName", + "LPMProductionTag"}; + std::vector metaDataVals = {"MC", + "3", + TString{o2::fullVersion()}, + ROOT_RELEASE, + mRecoPass, + mAnchorProd, + mAnchorPass, + mLPMProdTag}; + pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, metaDataKeys); + pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, metaDataVals); mIsMDSent = true; } - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0, Lifetime::Timeframe}, ""); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); @@ -313,7 +278,7 @@ void AODMcProducerWorkflowDPL::run(ProcessingContext& pc) mTimer.Stop(); } -void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) +void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext&) { LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); @@ -321,15 +286,24 @@ void AODMcProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) DataProcessorSpec getAODMcProducerWorkflowSpec() { - std::vector outputs; - - outputs.emplace_back(OutputLabel{"O2mccollision"}, "AOD", "MCCOLLISION", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcparticle_001"}, "AOD", "MCPARTICLE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2origin"}, "AOD", "ORIGIN", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); - outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataKeys"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataVals"}); + using McCollisions = AODMcProducerWorkflowDPL::McCollisions; + using McParticles = AODMcProducerWorkflowDPL::McParticles; + using Origins = AODMcProducerWorkflowDPL::Origins; + using XSections = AODMcProducerWorkflowDPL::XSections; + using PdfInfos = AODMcProducerWorkflowDPL::PdfInfos; + using HeavyIons = AODMcProducerWorkflowDPL::HeavyIons; + + std::vector outputs{ + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + OutputSpec{"AMD", "AODMetadataKeys"}, + OutputSpec{"AMD", "AODMetadataVals"}}; return DataProcessorSpec{ "aod-mc-producer-workflow", @@ -345,7 +319,11 @@ DataProcessorSpec getAODMcProducerWorkflowSpec() ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}}, ConfigParamSpec{"filter-mctracks", VariantType::Int, 1, {"Store only physical primary MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}}, ConfigParamSpec{"enable-embedding", VariantType::Int, 0, {"Use collisioncontext.root to process embedded events"}}, - ConfigParamSpec{"mckine-fname", VariantType::String, "o2sim", {"MC kinematics file name prefix: e.g. 'o2sim', 'bkg', 'sgn_1'. Used only if 'enable-embedding' is 0"}}}}; + ConfigParamSpec{"mckine-fname", VariantType::String, "o2sim", {"MC kinematics file name prefix: e.g. 'o2sim', 'bkg', 'sgn_1'. Used only if 'enable-embedding' is 0"}}, + ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}}}; } } // namespace o2::aodmcproducer +// +// EOF +// diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 6a6863c0dd2b7..852419a9895eb 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -12,8 +12,13 @@ /// @file AODProducerWorkflowSpec.cxx #include "AODProducerWorkflow/AODProducerWorkflowSpec.h" +#include "AODProducerWorkflow/AODMcProducerHelpers.h" +#include "AODProducerWorkflow/AODProducerHelpers.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "DataFormatsEMCAL/EventHandler.h" #include "DataFormatsFT0/RecPoints.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFV0/RecPoints.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/Configuration.h" @@ -25,14 +30,15 @@ #include "DataFormatsMCH/Cluster.h" #include "DataFormatsMID/Track.h" #include "DataFormatsMFT/TrackMFT.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "DataFormatsPHOS/EventHandler.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsZDC/BCRecData.h" #include "DataFormatsZDC/ZDCEnergy.h" #include "DataFormatsZDC/ZDCTDCData.h" #include "DataFormatsParameters/GRPECSObject.h" -#include "CommonUtils/NameConf.h" #include "MathUtils/Utils.h" -#include "DetectorsBase/GeometryManager.h" #include "CCDB/BasicCCDBManager.h" #include "CommonConstants/Triggers.h" #include "CommonConstants/PhysicsConstants.h" @@ -43,26 +49,25 @@ #include "Framework/AnalysisDataModel.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DataTypes.h" -#include "Framework/InputRecordWalker.h" -#include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/CCDBParamSpec.h" -#include "FDDBase/Constants.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "FT0Base/Geometry.h" -#include "FV0Base/Geometry.h" #include "GlobalTracking/MatchTOF.h" #include "ReconstructionDataFormats/Cascade.h" +#include "GlobalTracking/MatchGlobalFwd.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" #include "ITSMFTBase/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalFwdTrack.h" #include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/StrangeTrack.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/TrackMCHMID.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/VtxTrackIndex.h" #include "ReconstructionDataFormats/VtxTrackRef.h" @@ -71,6 +76,7 @@ #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/MCGenProperties.h" #include "ZDCBase/Constants.h" #include "TPCBase/ParameterElectronics.h" #include "GPUTPCGMMergedTrackHit.h" @@ -79,18 +85,25 @@ #include "TMath.h" #include "MathUtils/Utils.h" #include "Math/SMatrix.h" -#include "TMatrixD.h" #include "TString.h" -#include "TObjString.h" +#include #include +#include +#include #include #include #include #include #include +#include "TLorentzVector.h" +#include "TVector3.h" +#include "MathUtils/Tsallis.h" +#include #ifdef WITH_OPENMP #include #endif +#include +#include using namespace o2::framework; using namespace o2::math_utils::detail; @@ -98,6 +111,7 @@ using PVertex = o2::dataformats::PrimaryVertex; using GIndex = o2::dataformats::VtxTrackIndex; using DataRequest = o2::globaltracking::DataRequest; using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; using SMatrix55Sym = ROOT::Math::SMatrix>; namespace o2::aodproducer @@ -172,7 +186,7 @@ void AODProducerWorkflowDPL::createCTPReadout(const o2::globaltracking::RecoCont uint64_t globalBC = trigger.getBCData().toLong(); auto t0entry = bcsMapT0triggers.find(globalBC); if (t0entry != bcsMapT0triggers.end()) { - auto ctpdig = std::find_if(ctpDigits.begin(), ctpDigits.end(), [globalBC](const o2::ctp::CTPDigit& dig) { return dig.intRecord.toLong() == globalBC; }); + auto ctpdig = std::find_if(ctpDigits.begin(), ctpDigits.end(), [globalBC](const o2::ctp::CTPDigit& dig) { return static_cast(dig.intRecord.toLong()) == globalBC; }); if (ctpdig != ctpDigits.end()) { // CTP digit existing from other trigger, merge detector class mask ctpdig->CTPClassMask |= std::bitset<64>(classmask); @@ -277,43 +291,11 @@ void AODProducerWorkflowDPL::collectBCs(const o2::globaltracking::RecoContainer& } } -uint64_t AODProducerWorkflowDPL::getTFNumber(const o2::InteractionRecord& tfStartIR, int runNumber) -{ - auto& mgr = o2::ccdb::BasicCCDBManager::instance(); - o2::ccdb::CcdbApi ccdb_api; - const std::string rct_path = "RCT/Info/RunInformation/"; - const std::string start_orbit_path = "Trigger/StartOrbit"; - - mgr.setURL(o2::base::NameConf::getCCDBServer()); - ccdb_api.init(o2::base::NameConf::getCCDBServer()); - - std::map* mapStartOrbit = mgr.get>(start_orbit_path); - int64_t ts = 0; - std::map metadata; - std::map headers; - const std::string run_path = Form("%s/%i", rct_path.data(), runNumber); - headers = ccdb_api.retrieveHeaders(run_path, metadata, -1); - ts = atol(headers["SOR"].c_str()); - - // ccdb returns timestamp in mus - // mus to ms - ts = ts / 1000; - - uint32_t initialOrbit = mapStartOrbit->at(runNumber); - uint16_t firstRecBC = tfStartIR.bc; - uint32_t firstRecOrbit = tfStartIR.orbit; - const o2::InteractionRecord firstRec(firstRecBC, firstRecOrbit); - ts += firstRec.bc2ns() / 1000000; - - return ts; -}; - template void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, const o2::track::TrackParCov& track, int collisionID, aod::track::TrackTypeEnum type) { - tracksCursor(0, - collisionID, + tracksCursor(collisionID, type, truncateFloatFraction(track.getX(), mTrackX), truncateFloatFraction(track.getAlpha(), mTrackAlpha), @@ -325,8 +307,7 @@ void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, Tr // trackscov float sY = TMath::Sqrt(track.getSigmaY2()), sZ = TMath::Sqrt(track.getSigmaZ2()), sSnp = TMath::Sqrt(track.getSigmaSnp2()), sTgl = TMath::Sqrt(track.getSigmaTgl2()), sQ2Pt = TMath::Sqrt(track.getSigma1Pt2()); - tracksCovCursor(0, - truncateFloatFraction(sY, mTrackCovDiag), + tracksCovCursor(truncateFloatFraction(sY, mTrackCovDiag), truncateFloatFraction(sZ, mTrackCovDiag), truncateFloatFraction(sSnp, mTrackCovDiag), truncateFloatFraction(sTgl, mTrackCovDiag), @@ -346,13 +327,20 @@ void AODProducerWorkflowDPL::addToTracksTable(TracksCursorType& tracksCursor, Tr template void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracksExtraCursor, TrackExtraInfo& extraInfoHolder) { + // In case of TPC-only tracks, do not truncate the time error since we encapsulate there a special encoding of + // the deltaFwd/Bwd times + auto trackTimeRes = extraInfoHolder.trackTimeRes; + if (!extraInfoHolder.isTPConly) { + trackTimeRes = truncateFloatFraction(trackTimeRes, mTrackTimeError); + } + // extra - tracksExtraCursor(0, - truncateFloatFraction(extraInfoHolder.tpcInnerParam, mTrack1Pt), + tracksExtraCursor(truncateFloatFraction(extraInfoHolder.tpcInnerParam, mTrack1Pt), extraInfoHolder.flags, - extraInfoHolder.itsClusterMap, + extraInfoHolder.itsClusterSizes, extraInfoHolder.tpcNClsFindable, extraInfoHolder.tpcNClsFindableMinusFound, + extraInfoHolder.tpcNClsFindableMinusPID, extraInfoHolder.tpcNClsFindableMinusCrossedRows, extraInfoHolder.tpcNClsShared, extraInfoHolder.trdPattern, @@ -367,7 +355,39 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks truncateFloatFraction(extraInfoHolder.trackEtaEMCAL, mTrackPosEMCAL), truncateFloatFraction(extraInfoHolder.trackPhiEMCAL, mTrackPosEMCAL), truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), - truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError)); + trackTimeRes); +} + +template +void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) +{ + tracksQACursor( + trackQAInfoHolder.trackID, + mTrackQCRetainOnlydEdx ? 0.0f : truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + truncateFloatFraction(trackQAInfoHolder.tpcdEdxNorm, mTrackSignal), + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaR, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + trackQAInfoHolder.tpcdEdxTot1R, + trackQAInfoHolder.tpcdEdxTot2R, + trackQAInfoHolder.tpcdEdxTot3R, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdX, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdZ); } template @@ -379,7 +399,7 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC int bcSlice[2] = {-1, -1}; const auto& track = data.getMFTTrack(trackID); const auto& rof = data.getMFTTracksROFRecords()[mMFTROFs[trackID.getIndex()]]; - float trackTime = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mMFTROFrameHalfLengthNS; + float trackTime = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mMFTROFrameHalfLengthNS + mMFTROFBiasNS; float trackTimeRes = mMFTROFrameHalfLengthNS; bool needBCSlice = collisionID < 0; std::uint64_t bcOfTimeRef; @@ -391,26 +411,29 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC } trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS; - mftTracksCursor(0, - collisionID, + // the Cellular Automaton track-finding algorithm flag is stored in first of the 4 bits not used for the cluster size + uint64_t mftClusterSizesAndTrackFlags = track.getClusterSizes(); + mftClusterSizesAndTrackFlags |= (track.isCA()) ? (1ULL << (60)) : 0; + + mftTracksCursor(collisionID, track.getX(), track.getY(), truncateFloatFraction(track.getZ(), mTrackX), // for the forward tracks Z has the same role as X in barrel truncateFloatFraction(track.getPhi(), mTrackAlpha), truncateFloatFraction(track.getTanl(), mTrackTgl), truncateFloatFraction(track.getInvQPt(), mTrack1Pt), - track.getNumberOfPoints(), + mftClusterSizesAndTrackFlags, truncateFloatFraction(track.getTrackChi2(), mTrackChi2), truncateFloatFraction(trackTime, mTrackTime), truncateFloatFraction(trackTimeRes, mTrackTimeError)); if (needBCSlice) { - ambigMFTTracksCursor(0, mTableTrMFTID, bcSlice); + ambigMFTTracksCursor(mTableTrMFTID, bcSlice); } } -template +template void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, const o2::dataformats::VtxTrackRef& trackRef, @@ -419,12 +442,15 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, TracksCursorType& tracksCursor, TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, + TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, AmbigFwdTracksCursorType& ambigFwdTracksCursor, + FwdTrkClsCursorType& fwdTrkClsCursor, const std::map& bcsMap) { for (int src = GIndex::NSources; src--;) { @@ -433,8 +459,22 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } int start = trackRef.getFirstEntryOfSource(src); int end = start + trackRef.getEntriesOfSource(src); + int nToReserve = end - start; // + last index for a given table + if (src == GIndex::Source::MFT) { + mftTracksCursor.reserve(nToReserve + mftTracksCursor.lastIndex()); + } else if (src == GIndex::Source::MCH || src == GIndex::Source::MFTMCH || src == GIndex::Source::MCHMID) { + fwdTracksCursor.reserve(nToReserve + fwdTracksCursor.lastIndex()); + fwdTracksCovCursor.reserve(nToReserve + fwdTracksCovCursor.lastIndex()); + if (src == GIndex::Source::MFTMCH) { + mftTracksCovCursor.reserve(nToReserve + mftTracksCovCursor.lastIndex()); + } + } else { + tracksCursor.reserve(nToReserve + tracksCursor.lastIndex()); + tracksCovCursor.reserve(nToReserve + tracksCovCursor.lastIndex()); + tracksExtraCursor.reserve(nToReserve + tracksExtraCursor.lastIndex()); + } for (int ti = start; ti < end; ti++) { - auto& trackIndex = GIndices[ti]; + const auto& trackIndex = GIndices[ti]; if (GIndex::includesSource(src, mInputSources)) { if (src == GIndex::Source::MFT) { // MFT tracks are treated separately since they are stored in a different table if (trackIndex.isAmbiguous() && mGIDToTableMFTID.find(trackIndex) != mGIDToTableMFTID.end()) { // was it already stored ? @@ -447,25 +487,76 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, if (trackIndex.isAmbiguous() && mGIDToTableFwdID.find(trackIndex) != mGIDToTableFwdID.end()) { // was it already stored ? continue; } - addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, trackIndex, data, collisionID, collisionBC, bcsMap); + addToFwdTracksTable(fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, mftTracksCovCursor, trackIndex, data, collisionID, collisionBC, bcsMap); mGIDToTableFwdID.emplace(trackIndex, mTableTrFwdID); + addClustersToFwdTrkClsTable(data, fwdTrkClsCursor, trackIndex, mTableTrFwdID); mTableTrFwdID++; } else { // barrel track: normal tracks table if (trackIndex.isAmbiguous() && mGIDToTableID.find(trackIndex) != mGIDToTableID.end()) { // was it already stored ? continue; } + + float weight = 0; + static std::uniform_real_distribution<> distr(0., 1.); + bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || ((src != GIndex::TPC || mGIDUsedBySVtx.find(trackIndex) != mGIDUsedBySVtx.end() || mGIDUsedByStr.find(trackIndex) != mGIDUsedByStr.end()) && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); - if (extraInfoHolder.trackTimeRes < 0.f) { // failed or rejected? + + if (writeQAData) { + auto trackQAInfoHolder = processBarrelTrackQA(collisionID, collisionBC, trackIndex, data, bcsMap); + if (std::bitset<8>(trackQAInfoHolder.tpcClusterByteMask).count() >= mTrackQCNTrCut) { + trackQAInfoHolder.trackID = mTableTrID; + // LOGP(info, "orig time0 in bc: {} diffBCRef: {}, ttime: {} -> {}", trackQAInfoHolder.tpcTime0*8, extraInfoHolder.diffBCRef, extraInfoHolder.trackTime, (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime); + trackQAInfoHolder.tpcTime0 = (trackQAInfoHolder.tpcTime0 * 8 - extraInfoHolder.diffBCRef) * o2::constants::lhc::LHCBunchSpacingNS - extraInfoHolder.trackTime; + // difference between TPC track time0 and stored track nominal time in ns instead of TF start + addToTracksQATable(tracksQACursor, trackQAInfoHolder); + } else { + writeQAData = false; + } + } + + // include specific selection of tpc standalone tracks if thinning is active + if (mThinTracks && extraInfoHolder.isTPConly && !writeQAData) { // if trackQA is written then no check has to be done + auto trk = data.getTPCTrack(trackIndex); + if (trk.getNClusters() >= mTrackQCNCls && trk.getPt() >= mTrackQCPt) { + o2::dataformats::DCA dcaInfo{999.f, 999.f, 999.f, 999.f, 999.f}; + o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); + if (o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trk, 2., mMatCorr, &dcaInfo) && std::abs(dcaInfo.getY()) < mTrackQCDCAxy) { + writeQAData = true; // just setting this to not thin the track + } + } + } + + // Skip thinning if not enabled or track is not tpc standalone or assoc. to a V0 or qa'ed + if (mThinTracks && src == GIndex::Source::TPC && mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() && mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end() && !writeQAData) { + mGIDToTableID.emplace(trackIndex, -1); // pretend skipped tracks are stored; this is safe since they are are not written to disk and -1 indicates to all users to not use this track + continue; + } + + if (!extraInfoHolder.isTPConly && extraInfoHolder.trackTimeRes < 0.f) { // failed or rejected? LOG(warning) << "Barrel track " << trackIndex << " has no time set, rejection is not expected : time=" << extraInfoHolder.trackTime << " timeErr=" << extraInfoHolder.trackTimeRes << " BCSlice: " << extraInfoHolder.bcSlice[0] << ":" << extraInfoHolder.bcSlice[1]; continue; } - addToTracksTable(tracksCursor, tracksCovCursor, data.getTrackParam(trackIndex), collisionID); + const auto& trOrig = data.getTrackParam(trackIndex); + bool isProp = false; + if (mPropTracks && trOrig.getX() < mMaxPropXiu && + mGIDUsedBySVtx.find(trackIndex) == mGIDUsedBySVtx.end() && + mGIDUsedByStr.find(trackIndex) == mGIDUsedByStr.end()) { // Do not propagate track assoc. to V0s and str. tracking + auto trackPar(trOrig); + isProp = propagateTrackToPV(trackPar, data, collisionID); + if (isProp) { + addToTracksTable(tracksCursor, tracksCovCursor, trackPar, collisionID, aod::track::Track); + } + } + if (!isProp) { + addToTracksTable(tracksCursor, tracksCovCursor, trOrig, collisionID, aod::track::TrackIU); + } addToTracksExtraTable(tracksExtraCursor, extraInfoHolder); - // collecting table indices of barrel tracks for V0s table + + // collecting table indices of barrel tracks for V0s table if (extraInfoHolder.bcSlice[0] >= 0 && collisionID < 0) { - ambigTracksCursor(0, mTableTrID, extraInfoHolder.bcSlice); + ambigTracksCursor(mTableTrID, extraInfoHolder.bcSlice); } mGIDToTableID.emplace(trackIndex, mTableTrID); mTableTrID++; @@ -473,21 +564,24 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } } } + if (collisionID < 0) { + return; + } /// Add strangeness tracks to the table auto sTracks = data.getStrangeTracks(); - for (auto& collStrTrk : mCollisionStrTrk) { - if (collStrTrk.first < collisionID) { - continue; - } - if (collStrTrk.first > collisionID) { - break; - } + tracksCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCursor.lastIndex()); + tracksCovCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksCovCursor.lastIndex()); + tracksExtraCursor.reserve(mVertexStrLUT[collisionID + 1] + tracksExtraCursor.lastIndex()); + for (int iS{mVertexStrLUT[collisionID]}; iS < mVertexStrLUT[collisionID + 1]; ++iS) { + auto& collStrTrk = mCollisionStrTrk[iS]; auto& sTrk = sTracks[collStrTrk.second]; TrackExtraInfo extraInfo; extraInfo.itsChi2NCl = sTrk.mTopoChi2; // TODO: this is the total chi2 of adding the ITS clusters, the topology chi2 meaning might change in the future + extraInfo.itsClusterSizes = sTrk.getClusterSizes(); addToTracksTable(tracksCursor, tracksCovCursor, sTrk.mMother, collisionID, aod::track::StrangeTrack); addToTracksExtraTable(tracksExtraCursor, extraInfo); - mStrTrkIndices[collStrTrk.second] = mTableTrID++; + mStrTrkIndices[collStrTrk.second] = mTableTrID; + mTableTrID++; } } @@ -527,9 +621,9 @@ void AODProducerWorkflowDPL::fillIndexTablesPerCollision(const o2::dataformats:: } } -template +template void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, - AmbigFwdTracksCursorType& ambigFwdTracksCursor, GIndex trackID, + AmbigFwdTracksCursorType& ambigFwdTracksCursor, mftTracksCovCursorType& mftTracksCovCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, std::uint64_t collisionBC, const std::map& bcsMap) { @@ -582,10 +676,12 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC } o2::mch::TrackParam trackParamAtVertex(track.getZ(), track.getParameters(), track.getCovariances()); - double errVtx{0.0}; // FIXME: get errors associated with vertex if available - double errVty{0.0}; - if (!o2::mch::TrackExtrap::extrapToVertex(trackParamAtVertex, vx, vy, vz, errVtx, errVty)) { - return false; + if (mPropMuons) { + double errVtx{0.0}; // FIXME: get errors associated with vertex if available + double errVty{0.0}; + if (!o2::mch::TrackExtrap::extrapToVertex(trackParamAtVertex, vx, vy, vz, errVtx, errVty)) { + return false; + } } // extrapolate to DCA @@ -607,43 +703,37 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC double xAbs = trackParamAtRAbs.getNonBendingCoor(); double yAbs = trackParamAtRAbs.getBendingCoor(); - double px = trackParamAtVertex.px(); - double py = trackParamAtVertex.py(); - double pz = trackParamAtVertex.pz(); - - double pt = std::sqrt(px * px + py * py); - double dphi = std::atan2(py, px); - double dtanl = pz / pt; - double dinvqpt = 1.0 / (trackParamAtVertex.getCharge() * pt); double dpdca = track.getP() * dca; double dchi2 = track.getChi2OverNDF(); - fwdInfo.x = trackParamAtVertex.getNonBendingCoor(); - fwdInfo.y = trackParamAtVertex.getBendingCoor(); - fwdInfo.z = trackParamAtVertex.getZ(); + auto fwdmuon = mMatching.MCHtoFwd(trackParamAtVertex); + + fwdInfo.x = fwdmuon.getX(); + fwdInfo.y = fwdmuon.getY(); + fwdInfo.z = fwdmuon.getZ(); + fwdInfo.phi = fwdmuon.getPhi(); + fwdInfo.tanl = fwdmuon.getTgl(); + fwdInfo.invqpt = fwdmuon.getInvQPt(); fwdInfo.rabs = std::sqrt(xAbs * xAbs + yAbs * yAbs); - fwdInfo.phi = dphi; - fwdInfo.tanl = dtanl; - fwdInfo.invqpt = dinvqpt; fwdInfo.chi2 = dchi2; fwdInfo.pdca = dpdca; fwdInfo.nClusters = track.getNClusters(); - fwdCovInfo.sigX = TMath::Sqrt(trackParamAtVertex.getCovariances()(0, 0)); - fwdCovInfo.sigY = TMath::Sqrt(trackParamAtVertex.getCovariances()(1, 1)); - fwdCovInfo.sigPhi = TMath::Sqrt(trackParamAtVertex.getCovariances()(2, 2)); - fwdCovInfo.sigTgl = TMath::Sqrt(trackParamAtVertex.getCovariances()(3, 3)); - fwdCovInfo.sig1Pt = TMath::Sqrt(trackParamAtVertex.getCovariances()(4, 4)); - fwdCovInfo.rhoXY = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY)); - fwdCovInfo.rhoPhiX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX)); - fwdCovInfo.rhoPhiY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY)); - fwdCovInfo.rhoTglX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX)); - fwdCovInfo.rhoTglY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY)); - fwdCovInfo.rhoTglPhi = (Char_t)(128. * trackParamAtVertex.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi)); - fwdCovInfo.rho1PtX = (Char_t)(128. * trackParamAtVertex.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX)); - fwdCovInfo.rho1PtY = (Char_t)(128. * trackParamAtVertex.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY)); - fwdCovInfo.rho1PtPhi = (Char_t)(128. * trackParamAtVertex.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi)); - fwdCovInfo.rho1PtTgl = (Char_t)(128. * trackParamAtVertex.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); + fwdCovInfo.sigX = TMath::Sqrt(fwdmuon.getCovariances()(0, 0)); + fwdCovInfo.sigY = TMath::Sqrt(fwdmuon.getCovariances()(1, 1)); + fwdCovInfo.sigPhi = TMath::Sqrt(fwdmuon.getCovariances()(2, 2)); + fwdCovInfo.sigTgl = TMath::Sqrt(fwdmuon.getCovariances()(3, 3)); + fwdCovInfo.sig1Pt = TMath::Sqrt(fwdmuon.getCovariances()(4, 4)); + fwdCovInfo.rhoXY = (Char_t)(128. * fwdmuon.getCovariances()(0, 1) / (fwdCovInfo.sigX * fwdCovInfo.sigY)); + fwdCovInfo.rhoPhiX = (Char_t)(128. * fwdmuon.getCovariances()(0, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigX)); + fwdCovInfo.rhoPhiY = (Char_t)(128. * fwdmuon.getCovariances()(1, 2) / (fwdCovInfo.sigPhi * fwdCovInfo.sigY)); + fwdCovInfo.rhoTglX = (Char_t)(128. * fwdmuon.getCovariances()(0, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigX)); + fwdCovInfo.rhoTglY = (Char_t)(128. * fwdmuon.getCovariances()(1, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigY)); + fwdCovInfo.rhoTglPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 3) / (fwdCovInfo.sigTgl * fwdCovInfo.sigPhi)); + fwdCovInfo.rho1PtX = (Char_t)(128. * fwdmuon.getCovariances()(0, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigX)); + fwdCovInfo.rho1PtY = (Char_t)(128. * fwdmuon.getCovariances()(1, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigY)); + fwdCovInfo.rho1PtPhi = (Char_t)(128. * fwdmuon.getCovariances()(2, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigPhi)); + fwdCovInfo.rho1PtTgl = (Char_t)(128. * fwdmuon.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); return true; }; @@ -675,6 +765,8 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdInfo.trackTimeRes = time.getTimeStampError() * 1.e3; } else { // This is a GlobalMuonTrack or a GlobalForwardTrack const auto& track = data.getGlobalFwdTrack(trackID); + const auto& mftTracks = data.getMFTTracks(); + const auto& mfttrack = mftTracks[track.getMFTTrackID()]; if (!extrapMCHTrack(track.getMCHTrackID())) { LOGF(warn, "Unable to extrapolate MCH track with ID %d! Dummy parameters will be used", track.getMCHTrackID()); } @@ -714,6 +806,26 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdCovInfo.rho1PtTgl = (Char_t)(128. * track.getCovariances()(3, 4) / (fwdCovInfo.sig1Pt * fwdCovInfo.sigTgl)); fwdInfo.trackTypeId = (fwdInfo.chi2matchmchmid >= 0) ? o2::aod::fwdtrack::GlobalMuonTrack : o2::aod::fwdtrack::GlobalForwardTrack; + + float sX = TMath::Sqrt(mfttrack.getSigma2X()), sY = TMath::Sqrt(mfttrack.getSigma2Y()), sPhi = TMath::Sqrt(mfttrack.getSigma2Phi()), + sTgl = TMath::Sqrt(mfttrack.getSigma2Tanl()), sQ2Pt = TMath::Sqrt(mfttrack.getSigma2InvQPt()); + + mftTracksCovCursor(fwdInfo.matchmfttrackid, + truncateFloatFraction(sX, mTrackCovDiag), + truncateFloatFraction(sY, mTrackCovDiag), + truncateFloatFraction(sPhi, mTrackCovDiag), + truncateFloatFraction(sTgl, mTrackCovDiag), + truncateFloatFraction(sQ2Pt, mTrackCovDiag), + (Char_t)(128. * mfttrack.getCovariances()(0, 1) / (sX * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 2) / (sPhi * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 2) / (sPhi * sY)), + (Char_t)(128. * mfttrack.getCovariances()(0, 3) / (sTgl * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 3) / (sTgl * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 3) / (sTgl * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(0, 4) / (sQ2Pt * sX)), + (Char_t)(128. * mfttrack.getCovariances()(1, 4) / (sQ2Pt * sY)), + (Char_t)(128. * mfttrack.getCovariances()(2, 4) / (sQ2Pt * sPhi)), + (Char_t)(128. * mfttrack.getCovariances()(3, 4) / (sQ2Pt * sTgl))); } std::uint64_t bcOfTimeRef; @@ -726,8 +838,7 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC } fwdInfo.trackTime -= bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS; - fwdTracksCursor(0, - collisionID, + fwdTracksCursor(collisionID, fwdInfo.trackTypeId, fwdInfo.x, fwdInfo.y, @@ -750,8 +861,7 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC truncateFloatFraction(fwdInfo.trackTime, mTrackTime), truncateFloatFraction(fwdInfo.trackTimeRes, mTrackTimeError)); - fwdTracksCovCursor(0, - truncateFloatFraction(fwdCovInfo.sigX, mTrackCovDiag), + fwdTracksCovCursor(truncateFloatFraction(fwdCovInfo.sigX, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigY, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigPhi, mTrackCovDiag), truncateFloatFraction(fwdCovInfo.sigTgl, mTrackCovDiag), @@ -768,41 +878,90 @@ void AODProducerWorkflowDPL::addToFwdTracksTable(FwdTracksCursorType& fwdTracksC fwdCovInfo.rho1PtTgl); if (needBCSlice) { - ambigFwdTracksCursor(0, mTableTrFwdID, bcSlice); + ambigFwdTracksCursor(mTableTrFwdID, bcSlice); } } -void dimensionMCKeepStore(std::vector*>>& store, int Nsources, int NEvents) +//------------------------------------------------------------------ +void AODProducerWorkflowDPL::updateMCHeader(MCCollisionCursor& collisionCursor, + XSectionCursor& xSectionCursor, + PdfInfoCursor& pdfInfoCursor, + HeavyIonCursor& heavyIonCursor, + const MCEventHeader& header, + int collisionID, + int bcID, + float time, + short generatorID, + int sourceID) +{ + using aodmchelpers::updateHepMCHeavyIon; + using aodmchelpers::updateHepMCPdfInfo; + using aodmchelpers::updateHepMCXSection; + using aodmchelpers::updateMCCollisions; + + auto genID = updateMCCollisions(collisionCursor, + bcID, + time, + header, + generatorID, + sourceID, + mCollisionPosition); + mXSectionUpdate = (updateHepMCXSection(xSectionCursor, // + collisionID, // + genID, // + header, // + mXSectionUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); + mPdfInfoUpdate = (updateHepMCPdfInfo(pdfInfoCursor, // + collisionID, // + genID, // + header, // + mPdfInfoUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); + mHeavyIonUpdate = (updateHepMCHeavyIon(heavyIonCursor, // + collisionID, // + genID, // + header, + mHeavyIonUpdate) + ? HepMCUpdate::always + : HepMCUpdate::never); +} + +void dimensionMCKeepStore(std::vector>>& store, int Nsources, int NEvents) { store.resize(Nsources); for (int s = 0; s < Nsources; ++s) { - store[s].resize(NEvents, nullptr); + store[s].resize(NEvents); } } -void clearMCKeepStore(std::vector*>>& store) +void clearMCKeepStore(std::vector>>& store) { - for (int s = 0; s < store.size(); ++s) { - for (int e = 0; e < store[s].size(); ++e) { - if (store[s][e]) { - store[s][e]->clear(); - } + for (auto s = 0U; s < store.size(); ++s) { + for (auto e = 0U; e < store[s].size(); ++e) { + store[s][e].clear(); } } } // helper function to add a particle/track to the MC keep store -void keepMCParticle(std::vector*>>& store, int source, int event, int track, int value = 1) +void keepMCParticle(std::vector>>& store, int source, int event, int track, int value = 1, bool useSigFilt = false) { - if (!store[source][event]) { - store[source][event] = new std::unordered_map; + if (track < 0) { + LOG(warn) << "trackID is smaller than 0. Neglecting"; + return; + } + if (useSigFilt && source == 0) { + store[source][event][track] = -1; + } else { + store[source][event][track] = value; } - (*store[source][event])[track] = value; } -template void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, - const MCParticlesCursorType& mcParticlesCursor, + MCParticlesCursor& mcParticlesCursor, const gsl::span& primVer2TRefs, const gsl::span& GIndices, const o2::globaltracking::RecoContainer& data, @@ -827,7 +986,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { return; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); }; // mark reconstructed MC particles to store them into the table @@ -842,7 +1001,7 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcTruth.isValid()) { continue; } - keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID()); + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); // treating contributors of global tracks auto contributorsGID = data.getSingleDetectorRefs(trackIndex); if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { @@ -857,167 +1016,81 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& if (!mcLabel.isValid()) { continue; } - keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID()); + keepMCParticle(mToStore, mcLabel.getSourceID(), mcLabel.getEventID(), mcLabel.getTrackID(), 1, mUseSigFiltMC); } } } } } } - int tableIndex = 1; + // mark calorimeter signals as reconstructed particles + if (mInputSources[GIndex::EMC]) { + auto& mcCaloEMCCellLabels = data.getEMCALCellsMCLabels()->getTruthArray(); + for (auto& mcTruth : mcCaloEMCCellLabels) { + if (!mcTruth.isValid()) { + continue; + } + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); + } + } + if (mInputSources[GIndex::PHS]) { + auto& mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels()->getTruthArray(); + for (auto& mcTruth : mcCaloPHOSCellLabels) { + if (!mcTruth.isValid()) { + continue; + } + keepMCParticle(mToStore, mcTruth.getSourceID(), mcTruth.getEventID(), mcTruth.getTrackID(), 1, mUseSigFiltMC); + } + } + using namespace aodmchelpers; + using MCTrackNavigator = o2::mcutils::MCTrackNavigator; + + size_t offset = 0; for (auto& colInfo : mcColToEvSrc) { // loop over " <-> combined MC col. ID" key pairs int event = colInfo[2]; int source = colInfo[1]; int mcColId = colInfo[0]; std::vector const& mcParticles = mcReader.getTracks(source, event); - // mark tracks to be stored per event - // loop over stack of MC particles from end to beginning: daughters are stored after mothers - if (mRecoOnly) { - for (int particle = mcParticles.size() - 1; particle >= 0; particle--) { - // we store all primary particles == particles put by generator - if (mcParticles[particle].isPrimary()) { - keepMCParticle(mToStore, source, event, particle); - } else if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - keepMCParticle(mToStore, source, event, particle); - } else if (o2::mcutils::MCTrackNavigator::isKeepPhysics(mcParticles[particle], mcParticles)) { - keepMCParticle(mToStore, source, event, particle); - } + LOG(debug) << "Event=" << event << " source=" << source << " collision=" << mcColId; + auto& preselect = mToStore[source][event]; + + offset = updateParticles(mcParticlesCursor, + mcColId, + mcParticles, + preselect, + offset, + mRecoOnly, + source == 0, // background + mMcParticleW, + mMcParticleMom, + mMcParticlePos, + mUseSigFiltMC); - // skip treatment if this particle has not been marked during reconstruction - // or based on criteria just above - if (mToStore[source][event] && mToStore[source][event]->find(particle) == mToStore[source][event]->end()) { - continue; - } - - int mother0 = mcParticles[particle].getMotherTrackId(); - // we store mothers and daughters of particles that are reconstructed - if (mother0 != -1) { - keepMCParticle(mToStore, source, event, mother0); - } - int mother1 = mcParticles[particle].getSecondMotherTrackId(); - if (mother1 != -1) { - keepMCParticle(mToStore, source, event, mother1); - } - int daughter0 = mcParticles[particle].getFirstDaughterTrackId(); - if (daughter0 != -1) { - keepMCParticle(mToStore, source, event, daughter0); - } - int daughterL = mcParticles[particle].getLastDaughterTrackId(); - if (daughterL != -1) { - keepMCParticle(mToStore, source, event, daughterL); - } - } - LOG(info) << "The fraction of MC particles kept is " << mToStore[source][event]->size() / (1. * mcParticles.size()) << " for source " << source << " and event " << event; - - particleIDsToKeep.clear(); - for (auto& p : *mToStore[source][event]) { - particleIDsToKeep.push_back(p.first); - } - std::sort(particleIDsToKeep.begin(), particleIDsToKeep.end()); - for (auto pid : particleIDsToKeep) { - (*mToStore[source][event])[pid] = tableIndex - 1; - tableIndex++; - } - } else { - // if all mc particles are stored, all mc particles will be enumerated - particleIDsToKeep.clear(); - for (int particle = 0; particle < mcParticles.size(); particle++) { - keepMCParticle(mToStore, source, event, particle, tableIndex - 1); - tableIndex++; - particleIDsToKeep.push_back(particle); - } - } - - // second part: fill survived mc tracks into the AOD table - for (auto particle : particleIDsToKeep) { - int statusCode = 0; - uint8_t flags = 0; - if (!mcParticles[particle].isPrimary()) { - flags |= o2::aod::mcparticle::enums::ProducedByTransport; // mark as produced by transport - statusCode = mcParticles[particle].getProcess(); - } else { - statusCode = mcParticles[particle].getStatusCode().fullEncoding; - } - if (source == 0) { - flags |= o2::aod::mcparticle::enums::FromBackgroundEvent; // mark as particle from background event - } - if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcParticles[particle], mcParticles)) { - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; // mark as physical primary - } - float weight = mcParticles[particle].getWeight(); - std::vector mothers; - int mcMother0 = mcParticles[particle].getMotherTrackId(); - auto item = mToStore[source][event]->find(mcMother0); - if (item != mToStore[source][event]->end()) { - mothers.push_back(item->second); - } - int mcMother1 = mcParticles[particle].getSecondMotherTrackId(); - item = mToStore[source][event]->find(mcMother1); - if (item != mToStore[source][event]->end()) { - mothers.push_back(item->second); - } - int daughters[2] = {-1, -1}; // slice - int mcDaughter0 = mcParticles[particle].getFirstDaughterTrackId(); - item = mToStore[source][event]->find(mcDaughter0); - if (item != mToStore[source][event]->end()) { - daughters[0] = item->second; - } - int mcDaughterL = mcParticles[particle].getLastDaughterTrackId(); - item = mToStore[source][event]->find(mcDaughterL); - if (item != mToStore[source][event]->end()) { - daughters[1] = item->second; - if (daughters[0] < 0) { - LOG(error) << "AOD problematic daughter case observed"; - daughters[0] = daughters[1]; /// Treat the case of first negative label (pruned in the kinematics) - } - } else { - daughters[1] = daughters[0]; - } - if (daughters[0] > daughters[1]) { - std::swap(daughters[0], daughters[1]); - } - auto pX = (float)mcParticles[particle].Px(); - auto pY = (float)mcParticles[particle].Py(); - auto pZ = (float)mcParticles[particle].Pz(); - auto energy = (float)mcParticles[particle].GetEnergy(); - mcParticlesCursor(0, - mcColId, - mcParticles[particle].GetPdgCode(), - statusCode, - flags, - mothers, - daughters, - truncateFloatFraction(weight, mMcParticleW), - truncateFloatFraction(pX, mMcParticleMom), - truncateFloatFraction(pY, mMcParticleMom), - truncateFloatFraction(pZ, mMcParticleMom), - truncateFloatFraction(energy, mMcParticleMom), - truncateFloatFraction((float)mcParticles[particle].Vx(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vy(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].Vz(), mMcParticlePos), - truncateFloatFraction((float)mcParticles[particle].T(), mMcParticlePos)); - } mcReader.releaseTracksForSourceAndEvent(source, event); } } template -void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType& mcTrackLabelCursor, - const MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, - const MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, +void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTrackLabelCursor, + MCMFTTrackLabelCursorType& mcMFTTrackLabelCursor, + MCFwdTrackLabelCursorType& mcFwdTrackLabelCursor, const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& primVerGIs, - const o2::globaltracking::RecoContainer& data) + const o2::globaltracking::RecoContainer& data, + int vertexId) { // labelMask (temporary) usage: - // bit 13 -- ITS/TPC or TPC/TOF labels are not equal + // bit 13 -- ITS/TPC with ITS label (track of AB tracklet) different from TPC // bit 14 -- isNoise() == true - // bit 15 -- isFake() == true + // bit 15 -- isFake() == true (defined by the fakeness of the top level global track, i.e. if TOF is present, fake means that the track of the TPC label does not contribute to TOF cluster) // labelID = -1 -- label is not set for (int src = GIndex::NSources; src--;) { int start = trackRef.getFirstEntryOfSource(src); int end = start + trackRef.getEntriesOfSource(src); + mcMFTTrackLabelCursor.reserve(end - start + mcMFTTrackLabelCursor.lastIndex()); + mcFwdTrackLabelCursor.reserve(end - start + mcFwdTrackLabelCursor.lastIndex()); + mcTrackLabelCursor.reserve(end - start + mcTrackLabelCursor.lastIndex()); for (int ti = start; ti < end; ti++) { const auto trackIndex = primVerGIs[ti]; @@ -1033,13 +1106,13 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType if (GIndex::includesSource(src, mInputSources)) { auto mcTruth = data.getTrackMCLabel(trackIndex); - MCLabels labelHolder; + MCLabels labelHolder{}; if ((src == GIndex::Source::MFT) || (src == GIndex::Source::MFTMCH) || (src == GIndex::Source::MCH) || (src == GIndex::Source::MCHMID)) { // treating mft and fwd labels separately if (!needToStore(src == GIndex::Source::MFT ? mGIDToTableMFTID : mGIDToTableFwdID)) { continue; } if (mcTruth.isValid()) { // if not set, -1 will be stored - labelHolder.labelID = (*mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; + labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; } if (mcTruth.isFake()) { labelHolder.fwdLabelMask |= (0x1 << 7); @@ -1048,84 +1121,84 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(const MCTrackLabelCursorType labelHolder.fwdLabelMask |= (0x1 << 6); } if (src == GIndex::Source::MFT) { - mcMFTTrackLabelCursor(0, - labelHolder.labelID, + mcMFTTrackLabelCursor(labelHolder.labelID, labelHolder.fwdLabelMask); } else { - mcFwdTrackLabelCursor(0, - labelHolder.labelID, + mcFwdTrackLabelCursor(labelHolder.labelID, labelHolder.fwdLabelMask); } } else { if (!needToStore(mGIDToTableID)) { continue; } - if (mcTruth.isValid()) { // if not set, -1 will be stored - labelHolder.labelID = (*mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; - } - // treating possible mismatches and fakes for global tracks - auto contributorsGID = data.getSingleDetectorRefs(trackIndex); - bool isSetTPC = contributorsGID[GIndex::Source::TPC].isIndexSet(); - bool isSetITS = contributorsGID[GIndex::Source::ITS].isIndexSet(); - bool isSetTOF = contributorsGID[GIndex::Source::TOF].isIndexSet(); - bool isTOFFake = true; - if (isSetTPC && (isSetITS || isSetTOF)) { - auto mcTruthTPC = data.getTrackMCLabel(contributorsGID[GIndex::Source::TPC]); - if (mcTruthTPC.isValid()) { - labelHolder.labelTPC = (*mToStore[mcTruthTPC.getSourceID()][mcTruthTPC.getEventID()])[mcTruthTPC.getTrackID()]; - labelHolder.labelID = labelHolder.labelTPC; + if (mcTruth.isValid()) { // if not set, -1 will be stored + labelHolder.labelID = (mToStore[mcTruth.getSourceID()][mcTruth.getEventID()])[mcTruth.getTrackID()]; // defined by TPC if it contributes, otherwise: by ITS + if (mcTruth.isFake()) { + labelHolder.labelMask |= (0x1 << 15); } - if (isSetITS) { - auto mcTruthITS = data.getTrackMCLabel(contributorsGID[GIndex::Source::ITS]); - if (mcTruthITS.isValid()) { - labelHolder.labelITS = (*mToStore[mcTruthITS.getSourceID()][mcTruthITS.getEventID()])[mcTruthITS.getTrackID()]; - } - if (labelHolder.labelITS != labelHolder.labelTPC) { - LOG(debug) << "ITS-TPC MCTruth: labelIDs do not match at " << trackIndex.getIndex() << ", src = " << src; - labelHolder.labelMask |= (0x1 << 13); + if (trackIndex.includesDet(DetID::TPC) && trackIndex.getSource() != GIndex::Source::TPC) { // this is global track + auto contributorsGID = data.getSingleDetectorRefs(trackIndex); + if (contributorsGID[GIndex::Source::ITSTPC].isIndexSet()) { // there is a match to ITS tracks or ITSAB tracklet! + if (data.getTrackMCLabel(contributorsGID[GIndex::Source::ITSTPC]).isFake()) { + labelHolder.labelMask |= (0x1 << 13); + } } } - if (isSetTOF) { - const auto& labelsTOF = data.getTOFClustersMCLabels()->getLabels(contributorsGID[GIndex::Source::TOF]); - for (auto& mcLabel : labelsTOF) { - if (!mcLabel.isValid()) { - continue; - } - if (mcLabel == labelHolder.labelTPC) { - isTOFFake = false; - break; + if (trackIndex.includesDet(DetID::ITS)) { + auto itsGID = data.getITSContributorGID(trackIndex); + auto itsSource = itsGID.getSource(); + if (itsSource == GIndex::ITS) { + auto& itsTrack = data.getITSTrack(itsGID); + for (unsigned int iL = 0; iL < 7; ++iL) { + if (itsTrack.isFakeOnLayer(iL)) { + labelHolder.labelMask |= (0x1 << iL); + } } + } else if (itsSource == GIndex::ITSAB) { + labelHolder.labelMask |= (data.getTrackMCLabel(itsGID).isFake() << 12); } } - } - if (mcTruth.isFake() || (isSetTOF && isTOFFake)) { - labelHolder.labelMask |= (0x1 << 15); - } - if (mcTruth.isNoise()) { + + } else if (mcTruth.isNoise()) { labelHolder.labelMask |= (0x1 << 14); } - mcTrackLabelCursor(0, - labelHolder.labelID, - labelHolder.labelMask); + mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask); } } } } + + // filling the tables with the strangeness tracking labels + auto sTrackLabels = data.getStrangeTracksMCLabels(); + // check if vertexId and vertexId + 1 maps into mVertexStrLUT + if (!(vertexId < 0 || vertexId >= mVertexStrLUT.size() - 1)) { + mcTrackLabelCursor.reserve(mVertexStrLUT[vertexId + 1] + mcTrackLabelCursor.lastIndex()); + for (int iS{mVertexStrLUT[vertexId]}; iS < mVertexStrLUT[vertexId + 1]; ++iS) { + auto& collStrTrk = mCollisionStrTrk[iS]; + auto& label = sTrackLabels[collStrTrk.second]; + MCLabels labelHolder; + labelHolder.labelID = label.isValid() ? (mToStore[label.getSourceID()][label.getEventID()])[label.getTrackID()] : -1; + labelHolder.labelMask = (label.isFake() << 15) | (label.isNoise() << 14); + mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask); + } + } } template void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::RecoContainer& recoData, V0CursorType& v0Cursor, CascadeCursorType& cascadeCursor, Decay3BodyCursorType& decay3BodyCursor) { - auto v0s = recoData.getV0s(); - auto cascades = recoData.getCascades(); - auto decays3Body = recoData.getDecays3Body(); + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); + v0Cursor.reserve(v0s.size()); // filling v0s table for (size_t iv0 = 0; iv0 < v0s.size(); iv0++) { const auto& v0 = v0s[iv0]; auto trPosID = v0.getProngID(0); auto trNegID = v0.getProngID(1); + uint8_t v0flags = v0.getBits(); int posTableIdx = -1, negTableIdx = -1, collID = -1; auto item = mGIDToTableID.find(trPosID); if (item != mGIDToTableID.end()) { @@ -1146,12 +1219,13 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec collID = itemV->second; } if (posTableIdx != -1 and negTableIdx != -1 and collID != -1) { - v0Cursor(0, collID, posTableIdx, negTableIdx); + v0Cursor(collID, posTableIdx, negTableIdx, v0flags); mV0ToTableID[int(iv0)] = mTableV0ID++; } } // filling cascades table + cascadeCursor.reserve(cascades.size()); for (auto& cascade : cascades) { auto itemV0 = mV0ToTableID.find(cascade.getV0ID()); if (itemV0 == mV0ToTableID.end()) { @@ -1173,10 +1247,11 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec LOG(warn) << "Could not find cascade collisionID for the vertex ID " << cascade.getVertexID(); continue; } - cascadeCursor(0, collID, v0tableID, bachTableIdx); + cascadeCursor(collID, v0tableID, bachTableIdx); } // filling 3 body decays table + decay3BodyCursor.reserve(decays3Body.size()); for (size_t i3Body = 0; i3Body < decays3Body.size(); i3Body++) { const auto& decay3Body = decays3Body[i3Body]; GIndex trIDs[3]{ @@ -1190,7 +1265,7 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec if (item != mGIDToTableID.end()) { tableIdx[i] = item->second; } else { - LOG(warn) << fmt::format("Could not find a track index for prong ID {}", trIDs[i]); + LOG(warn) << fmt::format("Could not find a track index for prong ID {}", (int)trIDs[i]); missing = true; } } @@ -1204,19 +1279,87 @@ void AODProducerWorkflowDPL::fillSecondaryVertices(const o2::globaltracking::Rec if (missing) { continue; } - decay3BodyCursor(0, collID, tableIdx[0], tableIdx[1], tableIdx[2]); + decay3BodyCursor(collID, tableIdx[0], tableIdx[1], tableIdx[2]); + } +} + +template +void AODProducerWorkflowDPL::addClustersToFwdTrkClsTable(const o2::globaltracking::RecoContainer& recoData, FwdTrkClsCursorType& fwdTrkClsCursor, GIndex trackID, int fwdTrackId) +{ + const auto& mchTracks = recoData.getMCHTracks(); + const auto& mchmidMatches = recoData.getMCHMIDMatches(); + const auto& mchClusters = recoData.getMCHTrackClusters(); + + int mchTrackID = -1; + if (trackID.getSource() == GIndex::MCH) { // This is an MCH track + mchTrackID = trackID.getIndex(); + } else if (trackID.getSource() == GIndex::MCHMID) { // This is an MCH-MID track + auto mchmidMatch = mchmidMatches[trackID.getIndex()]; + mchTrackID = mchmidMatch.getMCHRef().getIndex(); + } // Others are Global Forward Tracks, their clusters will be or were added with the corresponding MCH track + + if (mchTrackID > -1 && mchTrackID < mchTracks.size()) { + const auto& mchTrack = mchTracks[mchTrackID]; + fwdTrkClsCursor.reserve(mchTrack.getNClusters() + fwdTrkClsCursor.lastIndex()); + int first = mchTrack.getFirstClusterIdx(); + int last = mchTrack.getLastClusterIdx(); + for (int i = first; i <= last; i++) { + const auto& cluster = mchClusters[i]; + fwdTrkClsCursor(fwdTrackId, + truncateFloatFraction(cluster.x, mMuonCl), + truncateFloatFraction(cluster.y, mMuonCl), + truncateFloatFraction(cluster.z, mMuonCl), + (((cluster.ey < 5.) & 0x1) << 12) | (((cluster.ex < 5.) & 0x1) << 11) | cluster.getDEId()); + } + } +} + +template +void AODProducerWorkflowDPL::fillHMPID(const o2::globaltracking::RecoContainer& recoData, HMPCursorType& hmpCursor) +{ + auto hmpMatches = recoData.getHMPMatches(); + + hmpCursor.reserve(hmpMatches.size()); + + // filling HMPs table + for (size_t iHmp = 0; iHmp < hmpMatches.size(); iHmp++) { + + const auto& match = hmpMatches[iHmp]; + + float xTrk, yTrk, theta, phi; + float xMip, yMip; + int charge, nph; + + match.getHMPIDtrk(xTrk, yTrk, theta, phi); + match.getHMPIDmip(xMip, yMip, charge, nph); + + auto photChargeVec = match.getPhotCharge(); + + float photChargeVec2[10]; // = {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.}; + + for (Int_t i = 0; i < 10; i++) { + photChargeVec2[i] = photChargeVec[i]; + } + auto tref = mGIDToTableID.find(match.getTrackRef()); + if (tref != mGIDToTableID.end()) { + hmpCursor(tref->second, match.getHMPsignal(), xTrk, yTrk, xMip, yMip, nph, charge, match.getIdxHMPClus(), match.getHmpMom(), photChargeVec2); + } else { + LOG(error) << "Could not find AOD track table entry for HMP-matched track " << match.getTrackRef().asString(); + } } } void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking::RecoContainer& recoData) { - auto v0s = recoData.getV0s(); - auto cascades = recoData.getCascades(); - auto decays3Body = recoData.getDecays3Body(); + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); int sTrkID = 0; mCollisionStrTrk.clear(); mCollisionStrTrk.reserve(recoData.getStrangeTracks().size()); + mVertexStrLUT.clear(); + mVertexStrLUT.resize(recoData.getPrimaryVertices().size() + 1, 0); for (auto& sTrk : recoData.getStrangeTracks()) { auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS}; int vtxId{0}; @@ -1227,10 +1370,10 @@ void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking } else { vtxId = decays3Body[sTrk.mDecayRef].getVertexID(); } - auto itemV = mVtxToTableCollID.find(vtxId); - int collisionId = itemV != mVtxToTableCollID.end() ? itemV->second : -1; - mCollisionStrTrk.emplace_back(collisionId, sTrkID++); + mCollisionStrTrk.emplace_back(vtxId, sTrkID++); + mVertexStrLUT[vtxId]++; } + std::exclusive_scan(mVertexStrLUT.begin(), mVertexStrLUT.end(), mVertexStrLUT.begin(), 0); // sort by collision ID std::sort(mCollisionStrTrk.begin(), mCollisionStrTrk.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); @@ -1241,32 +1384,72 @@ void AODProducerWorkflowDPL::prepareStrangenessTracking(const o2::globaltracking template void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltracking::RecoContainer& recoData, V0C& v0Curs, CC& cascCurs, D3BC& d3BodyCurs) { - auto v0s = recoData.getV0s(); - auto cascades = recoData.getCascades(); - auto decays3Body = recoData.getDecays3Body(); int itsTableIdx = -1; int sTrkID = 0; - for (auto& sTrk : recoData.getStrangeTracks()) { + int nV0 = 0; + int nCasc = 0; + int nD3Body = 0; + + for (const auto& sTrk : recoData.getStrangeTracks()) { + if (sTrk.mPartType == dataformats::kStrkV0) { + nV0++; + } else if (sTrk.mPartType == dataformats::kStrkCascade) { + nCasc++; + } else { + nD3Body++; + } + } + + v0Curs.reserve(nV0); + cascCurs.reserve(nCasc); + d3BodyCurs.reserve(nD3Body); + + for (const auto& sTrk : recoData.getStrangeTracks()) { auto ITSIndex = GIndex{sTrk.mITSRef, GIndex::ITS}; auto item = mGIDToTableID.find(ITSIndex); if (item != mGIDToTableID.end()) { itsTableIdx = item->second; } else { - LOG(warn) << "Could not find a ITS strange track index"; + LOG(warn) << "Could not find a ITS strange track index " << ITSIndex; continue; } - (sTrk.mPartType == dataformats::kStrkV0 ? v0Curs : (sTrk.mPartType == dataformats::kStrkCascade ? cascCurs : d3BodyCurs))(0, - mStrTrkIndices[sTrkID++], - itsTableIdx, - sTrk.mDecayRef, - sTrk.mDecayVtx[0], - sTrk.mDecayVtx[1], - sTrk.mDecayVtx[2], - sTrk.mMasses[0], - sTrk.mMasses[1], - sTrk.mMatchChi2, - sTrk.mTopoChi2, - sTrk.mITSClusSize); + if (sTrk.mPartType == dataformats::kStrkV0) { + v0Curs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } else if (sTrk.mPartType == dataformats::kStrkCascade) { + cascCurs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } else { + d3BodyCurs(mStrTrkIndices[sTrkID++], + itsTableIdx, + sTrk.mDecayRef, + sTrk.mDecayVtx[0], + sTrk.mDecayVtx[1], + sTrk.mDecayVtx[2], + sTrk.mMasses[0], + sTrk.mMasses[1], + sTrk.mMatchChi2, + sTrk.mTopoChi2, + sTrk.getAverageClusterSize()); + } } } @@ -1295,7 +1478,7 @@ void AODProducerWorkflowDPL::countTPCClusters(const o2::globaltracking::RecoCont o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; clMap[rowIndex] = true; - if (tpcClusShMap[absoluteIndex] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { if (!shMap[rowIndex]) { counters.shared++; } @@ -1340,54 +1523,116 @@ uint8_t AODProducerWorkflowDPL::getTRDPattern(const o2::trd::TrackTRD& track) return pattern; } -template -void AODProducerWorkflowDPL::addToCaloTable(const TCaloHandler& caloHandler, const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - int eventID, int bcID, int8_t caloType) +template +void AODProducerWorkflowDPL::addToCaloTable(TCaloHandler& caloHandler, TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, int eventID, int bcID, int8_t caloType) { auto inputEvent = caloHandler.buildEvent(eventID); - auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event - for (auto& cell : cellsInEvent) { // loop over all cells in collision - caloCellCursor(0, - bcID, - CellHelper::getCellNumber(cell), - truncateFloatFraction(CellHelper::getAmplitude(cell), mCaloAmp), - truncateFloatFraction(CellHelper::getTimeStamp(cell), mCaloTime), - cell.getType(), + auto cellsInEvent = inputEvent.mCells; // get cells belonging to current event + auto cellMClabels = inputEvent.mMCCellLabels; // get MC labels belonging to current event (only implemented for EMCal currently!) + caloCellCursor.reserve(cellsInEvent.size() + caloCellCursor.lastIndex()); + caloTRGCursor.reserve(cellsInEvent.size() + caloTRGCursor.lastIndex()); + if (mUseMC) { + mcCaloCellLabelCursor.reserve(cellsInEvent.size() + mcCaloCellLabelCursor.lastIndex()); + } + for (auto iCell = 0U; iCell < cellsInEvent.size(); iCell++) { + caloCellCursor(bcID, + CellHelper::getCellNumber(cellsInEvent[iCell]), + truncateFloatFraction(CellHelper::getAmplitude(cellsInEvent[iCell]), mCaloAmp), + truncateFloatFraction(CellHelper::getTimeStamp(cellsInEvent[iCell]), mCaloTime), + cellsInEvent[iCell].getType(), caloType); // 1 = emcal, -1 = undefined, 0 = phos // todo: fix dummy values in CellHelper when it is clear what is filled for trigger information - if (CellHelper::isTRU(cell)) { // Write only trigger cells into this table - caloTRGCursor(0, - bcID, - CellHelper::getFastOrAbsID(cell), - CellHelper::getLnAmplitude(cell), - CellHelper::getTriggerBits(cell), + if (CellHelper::isTRU(cellsInEvent[iCell])) { // Write only trigger cells into this table + caloTRGCursor(bcID, + CellHelper::getFastOrAbsID(cellsInEvent[iCell]), + CellHelper::getLnAmplitude(cellsInEvent[iCell]), + CellHelper::getTriggerBits(cellsInEvent[iCell]), caloType); } - } + if (mUseMC) { + // Common for PHOS and EMCAL + // loop over all MC Labels for the current cell + std::vector particleIds; + std::vector amplitudeFraction; + if (!mEMCselectLeading) { + particleIds.reserve(cellMClabels.size()); + amplitudeFraction.reserve(cellMClabels.size()); + } + float tmpMaxAmplitude = 0; + int32_t tmpindex = 0; + for (auto& mclabel : cellMClabels[iCell]) { + // do not fill noise lables! + if (mclabel.isValid()) { + if (mEMCselectLeading) { + if (mclabel.getAmplitudeFraction() > tmpMaxAmplitude) { + // Check if this MCparticle added to be kept? + if (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).find(mclabel.getTrackID()) != + mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()).end()) { + tmpMaxAmplitude = mclabel.getAmplitudeFraction(); + tmpindex = (mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID())).at(mclabel.getTrackID()); + } + } + } else { + auto trackStore = mToStore.at(mclabel.getSourceID()).at(mclabel.getEventID()); + auto iter = trackStore.find(mclabel.getTrackID()); + if (iter != trackStore.end()) { + amplitudeFraction.emplace_back(mclabel.getAmplitudeFraction()); + particleIds.emplace_back(iter->second); + } else { + particleIds.emplace_back(-1); // should the mc particle not be in mToStore make sure something (e.g. -1) is saved in particleIds so the length of particleIds is the same es amplitudeFraction! + amplitudeFraction.emplace_back(0.f); + LOG(warn) << "CaloTable: Could not find track for mclabel (" << mclabel.getSourceID() << "," << mclabel.getEventID() << "," << mclabel.getTrackID() << ") in the AOD MC store"; + if (mMCKineReader) { + auto mctrack = mMCKineReader->getTrack(mclabel); + TVector3 vec; + mctrack->GetStartVertex(vec); + LOG(warn) << " ... this track is of PDG " << mctrack->GetPdgCode() << " produced by " << mctrack->getProdProcessAsString() << " at (" << vec.X() << "," << vec.Y() << "," << vec.Z() << ")"; + } + } + } + } + } // end of loop over all MC Labels for the current cell + if (mEMCselectLeading) { + amplitudeFraction.emplace_back(tmpMaxAmplitude); + particleIds.emplace_back(tmpindex); + } + if (particleIds.size() == 0) { + particleIds.emplace_back(-1); + amplitudeFraction.emplace_back(0.f); + } + mcCaloCellLabelCursor(particleIds, + amplitudeFraction); + } + } // end of loop over cells in current event } // fill calo related tables (cells and calotrigger table) -template -void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, const TCaloTRGCursor& caloTRGCursor, - const std::map& bcsMap, +template +void AODProducerWorkflowDPL::fillCaloTable(TCaloCursor& caloCellCursor, TCaloTRGCursor& caloTRGCursor, + TMCCaloLabelCursor& mcCaloCellLabelCursor, const std::map& bcsMap, const o2::globaltracking::RecoContainer& data) { // get calo information auto caloEMCCells = data.getEMCALCells(); auto caloEMCCellsTRGR = data.getEMCALTriggers(); + auto mcCaloEMCCellLabels = data.getEMCALCellsMCLabels(); auto caloPHOSCells = data.getPHOSCells(); auto caloPHOSCellsTRGR = data.getPHOSTriggers(); + auto mcCaloPHOSCellLabels = data.getPHOSCellsMCLabels(); if (!mInputSources[GIndex::PHS]) { caloPHOSCells = {}; caloPHOSCellsTRGR = {}; + mcCaloPHOSCellLabels = {}; } if (!mInputSources[GIndex::EMC]) { caloEMCCells = {}; caloEMCCellsTRGR = {}; + mcCaloEMCCellLabels = {}; } o2::emcal::EventHandler emcEventHandler; @@ -1396,9 +1641,11 @@ void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, co // get cell belonging to an eveffillnt instead of timeframe emcEventHandler.reset(); emcEventHandler.setCellData(caloEMCCells, caloEMCCellsTRGR); + emcEventHandler.setCellMCTruthContainer(mcCaloEMCCellLabels); phsEventHandler.reset(); phsEventHandler.setCellData(caloPHOSCells, caloPHOSCellsTRGR); + phsEventHandler.setCellMCTruthContainer(mcCaloPHOSCellLabels); int emcNEvents = emcEventHandler.getNumberOfEvents(); int phsNEvents = phsEventHandler.getNumberOfEvents(); @@ -1432,11 +1679,11 @@ void AODProducerWorkflowDPL::fillCaloTable(const TCaloCursor& caloCellCursor, co } else { LOG(warn) << "Error: could not find a corresponding BC ID for a calo point; globalBC = " << globalBC << ", caloType = " << (int)caloType; } - if (caloType == 0) { // phs - addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, eventID, bcID, caloType); + if (caloType == 0) { // phos + addToCaloTable(phsEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType); } if (caloType == 1) { // emc - addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, eventID, bcID, caloType); + addToCaloTable(emcEventHandler, caloCellCursor, caloTRGCursor, mcCaloCellLabelCursor, eventID, bcID, caloType); } } @@ -1447,16 +1694,49 @@ void AODProducerWorkflowDPL::init(InitContext& ic) { mTimer.Stop(); o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mLPMProdTag = ic.options().get("lpmp-prod-tag"); - mAnchorPass = ic.options().get("anchor-pass"); - mAnchorProd = ic.options().get("anchor-prod"); - mRecoPass = ic.options().get("reco-pass"); + mLPMProdTag = ic.options().get("lpmp-prod-tag"); + mAnchorPass = ic.options().get("anchor-pass"); + mAnchorProd = ic.options().get("anchor-prod"); + mUser = ic.options().get("created-by"); + mRecoPass = ic.options().get("reco-pass"); mTFNumber = ic.options().get("aod-timeframe-id"); mRecoOnly = ic.options().get("reco-mctracks-only"); mTruncate = ic.options().get("enable-truncation"); mRunNumber = ic.options().get("run-number"); mCTPReadout = ic.options().get("ctpreadout-create"); mNThreads = std::max(1, ic.options().get("nthreads")); + mEMCselectLeading = ic.options().get("emc-select-leading"); + mThinTracks = ic.options().get("thin-tracks"); + mPropTracks = ic.options().get("propagate-tracks"); + mMaxPropXiu = ic.options().get("propagate-tracks-max-xiu"); + mPropMuons = ic.options().get("propagate-muons"); + if (auto s = ic.options().get("with-streamers"); !s.empty()) { + mStreamerFlags.set(s); + if (mStreamerFlags) { + LOGP(info, "Writing streamer data with mask:"); + LOG(info) << mStreamerFlags; + } else { + LOGP(warn, "Specified non-default empty streamer mask!"); + } + } + mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); + mTrackQCRetainOnlydEdx = ic.options().get("trackqc-retainonlydedx"); + mTrackQCFraction = ic.options().get("trackqc-fraction"); + mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); + mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); + mTrackQCPt = ic.options().get("trackqc-tpc-pt"); + mTrackQCNCls = ic.options().get("trackqc-tpc-cls"); + if (auto seed = ic.options().get("seed"); seed == 0) { + LOGP(info, "Using random device for seeding"); + std::random_device rd; + std::array seed_data{}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + mGenerator = std::mt19937(seq); + } else { + LOGP(info, "Using seed {} for sampling", seed); + mGenerator.seed(seed); + } #ifdef WITH_OPENMP LOGP(info, "Multi-threaded parts will run with {} OpenMP threads", mNThreads); #else @@ -1470,6 +1750,8 @@ void AODProducerWorkflowDPL::init(InitContext& ic) LOG(info) << "The Run number will be obtained from DPL headers"; } + mUseSigFiltMC = ic.options().get("mc-signal-filt"); + // set no truncation if selected by user if (mTruncate != 1) { LOG(info) << "Truncation is not used!"; @@ -1485,6 +1767,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mTrackCovOffDiag = 0xFFFFFFFF; mTrackSignal = 0xFFFFFFFF; mTrackTime = 0xFFFFFFFF; + mTPCTime0 = 0xFFFFFFFF; mTrackTimeError = 0xFFFFFFFF; mTrackPosEMCAL = 0xFFFFFFFF; mTracklets = 0xFFFFFFFF; @@ -1505,13 +1788,15 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mMuonCl = 0xFFFFFFFF; mMuonClErr = 0xFFFFFFFF; mV0Time = 0xFFFFFFFF; + mV0ChannelTime = 0xFFFFFFFF; mFDDTime = 0xFFFFFFFF; + mFDDChannelTime = 0xFFFFFFFF; mT0Time = 0xFFFFFFFF; + mT0ChannelTime = 0xFFFFFFFF; mV0Amplitude = 0xFFFFFFFF; mFDDAmplitude = 0xFFFFFFFF; mT0Amplitude = 0xFFFFFFFF; } -#ifdef O2_ZDC_NEWDATAMODEL // Initialize ZDC helper maps for (int ic = 0; ic < o2::zdc::NChannels; ic++) { mZDCEnergyMap[ic] = -std::numeric_limits::infinity(); @@ -1519,11 +1804,54 @@ void AODProducerWorkflowDPL::init(InitContext& ic) for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) { mZDCTDCMap[ic] = -std::numeric_limits::infinity(); } -#endif + + std::string hepmcUpdate = ic.options().get("hepmc-update"); + HepMCUpdate when = (hepmcUpdate == "never" ? HepMCUpdate::never : hepmcUpdate == "always" ? HepMCUpdate::always + : hepmcUpdate == "all" ? HepMCUpdate::allKeys + : HepMCUpdate::anyKey); + mXSectionUpdate = when; + mPdfInfoUpdate = when; + mHeavyIonUpdate = when; mTimer.Reset(); + + if (mStreamerFlags) { + mStreamer = std::make_unique("AO2DStreamer.root", "RECREATE"); + } } +namespace +{ +void add_additional_meta_info(std::vector& keys, std::vector& values) +{ + // see if we should put additional meta info (e.g. from MC) + auto aod_external_meta_info_file = getenv("AOD_ADDITIONAL_METADATA_FILE"); + if (aod_external_meta_info_file != nullptr) { + LOG(info) << "Trying to inject additional AOD meta-data from " << aod_external_meta_info_file; + if (std::filesystem::exists(aod_external_meta_info_file)) { + std::ifstream input_file(aod_external_meta_info_file); + if (input_file) { + nlohmann::json json_data; + try { + input_file >> json_data; + } catch (nlohmann::json::parse_error& e) { + std::cerr << "JSON Parse Error: " << e.what() << "\n"; + std::cerr << "Exception ID: " << e.id << "\n"; + std::cerr << "Byte position: " << e.byte << "\n"; + return; + } + // If parsing succeeds, iterate over key-value pairs + for (const auto& [key, value] : json_data.items()) { + LOG(info) << "Adding AOD MetaData" << key << " : " << value; + keys.push_back(key.c_str()); + values.push_back(value.get()); + } + } + } + } +} +} // namespace + void AODProducerWorkflowDPL::run(ProcessingContext& pc) { mTimer.Start(false); @@ -1569,78 +1897,72 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) LOG(debug) << "FOUND " << cpvTrigRecs.size() << " CPV trigger records"; LOG(info) << "FOUND " << primVertices.size() << " primary vertices"; - auto& bcBuilder = pc.outputs().make(Output{"AOD", "BC"}); - auto& cascadesBuilder = pc.outputs().make(Output{"AOD", "CASCADE_001"}); - auto& collisionsBuilder = pc.outputs().make(Output{"AOD", "COLLISION_001"}); - auto& decay3BodyBuilder = pc.outputs().make(Output{"AOD", "DECAY3BODY"}); - auto& trackedCascadeBuilder = pc.outputs().make(Output{"AOD", "TRACKEDCASCADE"}); - auto& trackedV0Builder = pc.outputs().make(Output{"AOD", "TRACKEDV0"}); - auto& tracked3BodyBuilder = pc.outputs().make(Output{"AOD", "TRACKED3BODY"}); - auto& fddBuilder = pc.outputs().make(Output{"AOD", "FDD_001"}); - auto& ft0Builder = pc.outputs().make(Output{"AOD", "FT0"}); - auto& fv0aBuilder = pc.outputs().make(Output{"AOD", "FV0A"}); - auto& fwdTracksBuilder = pc.outputs().make(Output{"AOD", "FWDTRACK"}); - auto& fwdTracksCovBuilder = pc.outputs().make(Output{"AOD", "FWDTRACKCOV"}); - auto& mcColLabelsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISIONLABEL"}); - auto& mcCollisionsBuilder = pc.outputs().make(Output{"AOD", "MCCOLLISION"}); - auto& mcMFTTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCMFTTRACKLABEL"}); - auto& mcFwdTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCFWDTRACKLABEL"}); - auto& mcParticlesBuilder = pc.outputs().make(Output{"AOD", "MCPARTICLE_001"}); - auto& mcTrackLabelBuilder = pc.outputs().make(Output{"AOD", "MCTRACKLABEL"}); - auto& mftTracksBuilder = pc.outputs().make(Output{"AOD", "MFTTRACK"}); - auto& tracksBuilder = pc.outputs().make(Output{"AOD", "TRACK_IU"}); - auto& tracksCovBuilder = pc.outputs().make(Output{"AOD", "TRACKCOV_IU"}); - auto& tracksExtraBuilder = pc.outputs().make(Output{"AOD", "TRACKEXTRA"}); - auto& ambigTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSTRACK"}); - auto& ambigMFTTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSMFTTR"}); - auto& ambigFwdTracksBuilder = pc.outputs().make(Output{"AOD", "AMBIGUOUSFWDTR"}); - auto& v0sBuilder = pc.outputs().make(Output{"AOD", "V0_001"}); -#ifdef O2_ZDC_NEWDATAMODEL - auto& zdcBuilder = pc.outputs().make(Output{"AOD", "ZDC_001"}); -#else - auto& zdcBuilder = pc.outputs().make(Output{"AOD", "ZDC"}); -#endif - auto& caloCellsBuilder = pc.outputs().make(Output{"AOD", "CALO"}); - auto& caloCellsTRGTableBuilder = pc.outputs().make(Output{"AOD", "CALOTRIGGER"}); - auto& cpvClustersBuilder = pc.outputs().make(Output{"AOD", "CPVCLUSTER"}); - auto& originTableBuilder = pc.outputs().make(Output{"AOD", "ORIGIN"}); - - auto bcCursor = bcBuilder.cursor(); - auto cascadesCursor = cascadesBuilder.cursor(); - auto collisionsCursor = collisionsBuilder.cursor(); - auto decay3BodyCursor = decay3BodyBuilder.cursor(); - auto trackedCascadeCursor = trackedCascadeBuilder.cursor(); - auto trackedV0Cursor = trackedV0Builder.cursor(); - auto tracked3BodyCurs = tracked3BodyBuilder.cursor(); - auto fddCursor = fddBuilder.cursor(); - auto ft0Cursor = ft0Builder.cursor(); - auto fv0aCursor = fv0aBuilder.cursor(); - auto fwdTracksCursor = fwdTracksBuilder.cursor(); - auto fwdTracksCovCursor = fwdTracksCovBuilder.cursor(); - auto mcColLabelsCursor = mcColLabelsBuilder.cursor(); - auto mcCollisionsCursor = mcCollisionsBuilder.cursor(); - auto mcMFTTrackLabelCursor = mcMFTTrackLabelBuilder.cursor(); - auto mcFwdTrackLabelCursor = mcFwdTrackLabelBuilder.cursor(); - auto mcParticlesCursor = mcParticlesBuilder.cursor(); - auto mcTrackLabelCursor = mcTrackLabelBuilder.cursor(); - auto mftTracksCursor = mftTracksBuilder.cursor(); - auto tracksCovCursor = tracksCovBuilder.cursor(); - auto tracksCursor = tracksBuilder.cursor(); - auto tracksExtraCursor = tracksExtraBuilder.cursor(); - auto ambigTracksCursor = ambigTracksBuilder.cursor(); - auto ambigMFTTracksCursor = ambigMFTTracksBuilder.cursor(); - auto ambigFwdTracksCursor = ambigFwdTracksBuilder.cursor(); - auto v0sCursor = v0sBuilder.cursor(); - auto zdcCursor = zdcBuilder.cursor(); - auto caloCellsCursor = caloCellsBuilder.cursor(); - auto caloCellsTRGTableCursor = caloCellsTRGTableBuilder.cursor(); - auto cpvClustersCursor = cpvClustersBuilder.cursor(); - auto originCursor = originTableBuilder.cursor(); + + using namespace o2::aodhelpers; + + auto bcCursor = createTableCursor(pc); + auto bcFlagsCursor = createTableCursor(pc); + auto cascadesCursor = createTableCursor(pc); + auto collisionsCursor = createTableCursor(pc); + auto decay3BodyCursor = createTableCursor(pc); + auto trackedCascadeCursor = createTableCursor(pc); + auto trackedV0Cursor = createTableCursor(pc); + auto tracked3BodyCurs = createTableCursor(pc); + auto fddCursor = createTableCursor(pc); + auto fddExtraCursor = createTableCursor(pc); + auto ft0Cursor = createTableCursor(pc); + auto ft0ExtraCursor = createTableCursor(pc); + auto fv0aCursor = createTableCursor(pc); + auto fv0aExtraCursor = createTableCursor(pc); + auto fwdTracksCursor = createTableCursor(pc); + auto fwdTracksCovCursor = createTableCursor(pc); + auto fwdTrkClsCursor = createTableCursor(pc); + auto mftTracksCursor = createTableCursor(pc); + auto mftTracksCovCursor = createTableCursor(pc); + auto tracksCursor = createTableCursor(pc); + auto tracksCovCursor = createTableCursor(pc); + auto tracksExtraCursor = createTableCursor(pc); + auto tracksQACursor = createTableCursor(pc); + auto ambigTracksCursor = createTableCursor(pc); + auto ambigMFTTracksCursor = createTableCursor(pc); + auto ambigFwdTracksCursor = createTableCursor(pc); + auto v0sCursor = createTableCursor(pc); + auto zdcCursor = createTableCursor(pc); + auto hmpCursor = createTableCursor(pc); + auto caloCellsCursor = createTableCursor(pc); + auto caloCellsTRGTableCursor = createTableCursor(pc); + auto cpvClustersCursor = createTableCursor(pc); + auto originCursor = createTableCursor(pc); + + // Declare MC cursors type without adding the output for a table + o2::framework::Produces mcColLabelsCursor; + o2::framework::Produces mcCollisionsCursor; + o2::framework::Produces hepmcXSectionsCursor; + o2::framework::Produces hepmcPdfInfosCursor; + o2::framework::Produces hepmcHeavyIonsCursor; + o2::framework::Produces mcMFTTrackLabelCursor; + o2::framework::Produces mcFwdTrackLabelCursor; + o2::framework::Produces mcParticlesCursor; + o2::framework::Produces mcTrackLabelCursor; + o2::framework::Produces mcCaloLabelsCursor; + if (mUseMC) { // This creates the actual writercursor + mcColLabelsCursor = createTableCursor(pc); + mcCollisionsCursor = createTableCursor(pc); + hepmcXSectionsCursor = createTableCursor(pc); + hepmcPdfInfosCursor = createTableCursor(pc); + hepmcHeavyIonsCursor = createTableCursor(pc); + mcMFTTrackLabelCursor = createTableCursor(pc); + mcFwdTrackLabelCursor = createTableCursor(pc); + mcParticlesCursor = createTableCursor(pc); + mcTrackLabelCursor = createTableCursor(pc); + mcCaloLabelsCursor = createTableCursor(pc); + } std::unique_ptr mcReader; if (mUseMC) { mcReader = std::make_unique("collisioncontext.root"); } + mMCKineReader = mcReader.get(); // for use in different functions std::map bcsMap; collectBCs(recoData, mUseMC ? mcReader->getDigitizationContext()->getEventRecords() : std::vector{}, bcsMap); if (!primVer2TRefs.empty()) { // if the vertexing was done, the last slot refers to orphan tracks @@ -1658,15 +1980,18 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) tfNumber = mTFNumber; } - std::vector aAmplitudes; + std::vector aAmplitudes, aTimes; std::vector aChannels; + fv0aCursor.reserve(fv0RecPoints.size()); for (auto& fv0RecPoint : fv0RecPoints) { aAmplitudes.clear(); aChannels.clear(); + aTimes.clear(); const auto channelData = fv0RecPoint.getBunchChannelData(fv0ChData); for (auto& channel : channelData) { if (channel.charge > 0) { aAmplitudes.push_back(truncateFloatFraction(channel.charge, mV0Amplitude)); + aTimes.push_back(truncateFloatFraction(channel.time * 1.E-3, mV0ChannelTime)); aChannels.push_back(channel.channel); } } @@ -1678,91 +2003,21 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FV0 rec. point; BC = " << bc; } - fv0aCursor(0, - bcID, + fv0aCursor(bcID, aAmplitudes, aChannels, truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns fv0RecPoint.getTrigger().getTriggersignals()); - } -#ifndef O2_ZDC_NEWDATAMODEL - for (auto zdcRecData : zdcBCRecData) { - uint64_t bc = zdcRecData.ir.toLong(); - auto item = bcsMap.find(bc); - int bcID = -1; - if (item != bcsMap.end()) { - bcID = item->second; - } else { - LOG(fatal) << "Error: could not find a corresponding BC ID for a ZDC rec. point; BC = " << bc; + if (mEnableFITextra) { + fv0aExtraCursor(bcID, + aTimes); } - int fe, ne, ft, nt, fi, ni; - zdcRecData.getRef(fe, ne, ft, nt, fi, ni); - // initialize zdc helper maps - for (int ic = 0; ic < o2::zdc::NChannels; ic++) { - mZDCEnergyMap[ic] = -std::numeric_limits::infinity(); - } - for (int ic = 0; ic < o2::zdc::NTDCChannels; ic++) { - mZDCTDCMap[ic] = -std::numeric_limits::infinity(); - } - for (int ie = 0; ie < ne; ie++) { - auto& zdcEnergyData = zdcEnergies[fe + ie]; - int ch = zdcEnergyData.ch(); - float energy = zdcEnergyData.energy(); - if (ch >= 0 && ch < o2::zdc::NChannels) { - mZDCEnergyMap[ch] = energy; - } - } - for (int it = 0; it < nt; it++) { - auto& tdc = zdcTDCData[ft + it]; - int ch = tdc.ch(); - float tdcValue = tdc.value(); - if (ch >= 0 && ch < o2::zdc::NTDCChannels) { - mZDCTDCMap[ch] = tdcValue; - } - } - float energySectorZNA[4]; - float energySectorZNC[4]; - float energySectorZPA[4]; - float energySectorZPC[4]; - energySectorZNA[0] = mZDCEnergyMap[o2::zdc::IdZNA1]; - energySectorZNA[1] = mZDCEnergyMap[o2::zdc::IdZNA2]; - energySectorZNA[2] = mZDCEnergyMap[o2::zdc::IdZNA3]; - energySectorZNA[3] = mZDCEnergyMap[o2::zdc::IdZNA4]; - energySectorZNC[0] = mZDCEnergyMap[o2::zdc::IdZNC1]; - energySectorZNC[1] = mZDCEnergyMap[o2::zdc::IdZNC2]; - energySectorZNC[2] = mZDCEnergyMap[o2::zdc::IdZNC3]; - energySectorZNC[3] = mZDCEnergyMap[o2::zdc::IdZNC4]; - energySectorZPA[0] = mZDCEnergyMap[o2::zdc::IdZPA1]; - energySectorZPA[1] = mZDCEnergyMap[o2::zdc::IdZPA2]; - energySectorZPA[2] = mZDCEnergyMap[o2::zdc::IdZPA3]; - energySectorZPA[3] = mZDCEnergyMap[o2::zdc::IdZPA4]; - energySectorZPC[0] = mZDCEnergyMap[o2::zdc::IdZPC1]; - energySectorZPC[1] = mZDCEnergyMap[o2::zdc::IdZPC2]; - energySectorZPC[2] = mZDCEnergyMap[o2::zdc::IdZPC3]; - energySectorZPC[3] = mZDCEnergyMap[o2::zdc::IdZPC4]; - zdcCursor(0, - bcID, - mZDCEnergyMap[o2::zdc::IdZEM1], - mZDCEnergyMap[o2::zdc::IdZEM2], - mZDCEnergyMap[o2::zdc::IdZNAC], - mZDCEnergyMap[o2::zdc::IdZNCC], - mZDCEnergyMap[o2::zdc::IdZPAC], - mZDCEnergyMap[o2::zdc::IdZPCC], - energySectorZNA, - energySectorZNC, - energySectorZPA, - energySectorZPC, - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZEM1], - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZEM2], - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZNAC], - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZNCC], - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZPAC], - mZDCTDCMap[o2::zdc::TDCChannelID::TDCZPCC]); } -#else + std::vector zdcEnergy, zdcAmplitudes, zdcTime; std::vector zdcChannelsE, zdcChannelsT; + zdcCursor.reserve(zdcBCRecData.size()); for (auto zdcRecData : zdcBCRecData) { uint64_t bc = zdcRecData.ir.toLong(); auto item = bcsMap.find(bc); @@ -1790,15 +2045,13 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) zdcTime.emplace_back(tdc.value()); zdcChannelsT.emplace_back(o2::zdc::TDCSignal[tdc.ch()]); } - zdcCursor(0, - bcID, + zdcCursor(bcID, zdcEnergy, zdcChannelsE, zdcAmplitudes, zdcTime, zdcChannelsT); } -#endif // keep track event/source id for each mc-collision // using map and not unordered_map to ensure @@ -1806,42 +2059,75 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) std::vector> mcColToEvSrc; if (mUseMC) { - // TODO: figure out collision weight - float mcColWeight = 1.; + using namespace o2::aodmchelpers; + // filling mcCollision table int nMCCollisions = mcReader->getDigitizationContext()->getNCollisions(); const auto& mcRecords = mcReader->getDigitizationContext()->getEventRecords(); const auto& mcParts = mcReader->getDigitizationContext()->getEventParts(); + + // if signal filtering enabled, let's check if there are more than one source; otherwise fatalise + if (mUseSigFiltMC) { + std::vector sourceIDs{}; + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + for (auto const& colPart : mcParts[iCol]) { + int sourceID = colPart.sourceID; + if (std::find(sourceIDs.begin(), sourceIDs.end(), sourceID) == sourceIDs.end()) { + sourceIDs.push_back(sourceID); + } + if (sourceIDs.size() > 1) { // we found more than one, exit + break; + } + } + if (sourceIDs.size() > 1) { // we found more than one, exit + break; + } + } + if (sourceIDs.size() <= 1) { + LOGP(fatal, "Signal filtering cannot be enabled without embedding. Please fix the configuration either enabling the embedding, or turning off the signal filtering."); + } + } + + // count all parts + int totalNParts = 0; for (int iCol = 0; iCol < nMCCollisions; iCol++) { - auto time = mcRecords[iCol].getTimeNS(); + totalNParts += mcParts[iCol].size(); + } + mcCollisionsCursor.reserve(totalNParts); + + for (int iCol = 0; iCol < nMCCollisions; iCol++) { + const auto time = mcRecords[iCol].getTimeOffsetWrtBC(); auto globalBC = mcRecords[iCol].toLong(); auto item = bcsMap.find(globalBC); int bcID = -1; if (item != bcsMap.end()) { bcID = item->second; } else { - LOG(fatal) << "Error: could not find a corresponding BC ID for MC collision; BC = " << globalBC << ", mc collision = " << iCol; + LOG(fatal) << "Error: could not find a corresponding BC ID " + << "for MC collision; BC = " << globalBC + << ", mc collision = " << iCol; } auto& colParts = mcParts[iCol]; auto nParts = colParts.size(); for (auto colPart : colParts) { auto eventID = colPart.entryID; auto sourceID = colPart.sourceID; - // enable embedding: if several colParts exist, then they are saved as one collision + // enable embedding: if several colParts exist, then they are + // saved as one collision if (nParts == 1 || sourceID == 0) { // FIXME: // use generators' names for generatorIDs (?) - short generatorID = sourceID; auto& header = mcReader->getMCEventHeader(sourceID, eventID); - mcCollisionsCursor(0, - bcID, - generatorID, - truncateFloatFraction(header.GetX(), mCollisionPosition), - truncateFloatFraction(header.GetY(), mCollisionPosition), - truncateFloatFraction(header.GetZ(), mCollisionPosition), - truncateFloatFraction(time, mCollisionPosition), - truncateFloatFraction(mcColWeight, mCollisionPosition), - header.GetB()); + updateMCHeader(mcCollisionsCursor.cursor, + hepmcXSectionsCursor.cursor, + hepmcPdfInfosCursor.cursor, + hepmcHeavyIonsCursor.cursor, + header, + iCol, + bcID, + time, + 0, + sourceID); } mcColToEvSrc.emplace_back(std::vector{iCol, sourceID, eventID}); // point background and injected signal events to one collision } @@ -1852,24 +2138,17 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) [](const std::vector& left, const std::vector& right) { return (left[0] < right[0]); }); // vector of FDD amplitudes - int16_t aFDDAmplitudesA[8] = {0u}; - int16_t aFDDAmplitudesC[8] = {0u}; + int16_t aFDDAmplitudesA[8] = {0u}, aFDDAmplitudesC[8] = {0u}; + float aFDDTimesA[8] = {0.f}, aFDDTimesC[8] = {0.f}; // filling FDD table + fddCursor.reserve(fddRecPoints.size()); for (const auto& fddRecPoint : fddRecPoints) { for (int i = 0; i < 8; i++) { aFDDAmplitudesA[i] = 0; aFDDAmplitudesC[i] = 0; + aFDDTimesA[i] = 0.f; + aFDDTimesC[i] = 0.f; } - - const auto channelData = fddRecPoint.getBunchChannelData(fddChData); - for (const auto& channel : channelData) { - if (channel.mPMNumber < 8) { - aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude - } else { - aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude - } - } - uint64_t globalBC = fddRecPoint.getInteractionRecord().toLong(); uint64_t bc = globalBC; auto item = bcsMap.find(bc); @@ -1879,21 +2158,39 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FDD rec. point; BC = " << bc; } - fddCursor(0, - bcID, + const auto channelData = fddRecPoint.getBunchChannelData(fddChData); + for (const auto& channel : channelData) { + if (channel.mPMNumber < 8) { + aFDDAmplitudesC[channel.mPMNumber] = channel.mChargeADC; // amplitude + aFDDTimesC[channel.mPMNumber] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time + } else { + aFDDAmplitudesA[channel.mPMNumber - 8] = channel.mChargeADC; // amplitude + aFDDTimesA[channel.mPMNumber - 8] = truncateFloatFraction(channel.mTime * 1E-3, mFDDChannelTime); // time + } + } + + fddCursor(bcID, aFDDAmplitudesA, aFDDAmplitudesC, truncateFloatFraction(fddRecPoint.getCollisionTimeA() * 1E-3, mFDDTime), // ps to ns truncateFloatFraction(fddRecPoint.getCollisionTimeC() * 1E-3, mFDDTime), // ps to ns fddRecPoint.getTrigger().getTriggersignals()); + if (mEnableFITextra) { + fddExtraCursor(bcID, + aFDDTimesA, + aFDDTimesC); + } } // filling FT0 table - std::vector aAmplitudesA, aAmplitudesC; + std::vector aAmplitudesA, aAmplitudesC, aTimesA, aTimesC; std::vector aChannelsA, aChannelsC; + ft0Cursor.reserve(ft0RecPoints.size()); for (auto& ft0RecPoint : ft0RecPoints) { aAmplitudesA.clear(); aAmplitudesC.clear(); + aTimesA.clear(); + aTimesC.clear(); aChannelsA.clear(); aChannelsC.clear(); const auto channelData = ft0RecPoint.getBunchChannelData(ft0ChData); @@ -1904,9 +2201,11 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) if (channel.ChId < nFT0ChannelsAside) { aChannelsA.push_back(channel.ChId); aAmplitudesA.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude)); + aTimesA.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime)); } else { aChannelsC.push_back(channel.ChId - nFT0ChannelsAside); aAmplitudesC.push_back(truncateFloatFraction(channel.QTCAmpl, mT0Amplitude)); + aTimesC.push_back(truncateFloatFraction(channel.CFDTime * 1E-3, mT0ChannelTime)); } } } @@ -1919,8 +2218,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; } - ft0Cursor(0, - bcID, + ft0Cursor(bcID, aAmplitudesA, aChannelsA, aAmplitudesC, @@ -1928,10 +2226,16 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) truncateFloatFraction(ft0RecPoint.getCollisionTimeA() * 1E-3, mT0Time), // ps to ns truncateFloatFraction(ft0RecPoint.getCollisionTimeC() * 1E-3, mT0Time), // ps to ns ft0RecPoint.getTrigger().getTriggersignals()); + if (mEnableFITextra) { + ft0ExtraCursor(bcID, + aTimesA, + aTimesC); + } } if (mUseMC) { // filling MC collision labels + mcColLabelsCursor.reserve(primVerLabels.size()); for (auto& label : primVerLabels) { auto it = std::find_if(mcColToEvSrc.begin(), mcColToEvSrc.end(), [&label](const auto& mcColInfo) { return mcColInfo[1] == label.getSourceID() && mcColInfo[2] == label.getEventID(); }); @@ -1940,7 +2244,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) mcCollisionID = it->at(0); } uint16_t mcMask = 0; // todo: set mask using normalized weights? - mcColLabelsCursor(0, mcCollisionID, mcMask); + mcColLabelsCursor(mcCollisionID, mcMask); } } @@ -1966,16 +2270,41 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) mGIDToTableFwdID.clear(); // reset the tables to be used by 'fillTrackTablesPerCollision' mGIDToTableMFTID.clear(); + if (mPropTracks || mThinTracks) { + auto v0s = recoData.getV0sIdx(); + auto cascades = recoData.getCascadesIdx(); + auto decays3Body = recoData.getDecays3BodyIdx(); + mGIDUsedBySVtx.reserve(v0s.size() * 2 + cascades.size() + decays3Body.size() * 3); + for (const auto& v0 : v0s) { + mGIDUsedBySVtx.insert(v0.getProngID(0)); + mGIDUsedBySVtx.insert(v0.getProngID(1)); + } + for (const auto& cascade : cascades) { + mGIDUsedBySVtx.insert(cascade.getBachelorID()); + } + for (const auto& id3Body : decays3Body) { + mGIDUsedBySVtx.insert(id3Body.getProngID(0)); + mGIDUsedBySVtx.insert(id3Body.getProngID(1)); + mGIDUsedBySVtx.insert(id3Body.getProngID(2)); + } + + mGIDUsedByStr.reserve(recoData.getStrangeTracks().size()); + for (const auto& sTrk : recoData.getStrangeTracks()) { + mGIDUsedByStr.emplace(sTrk.mITSRef, GIndex::ITS); + } + } + // filling unassigned tracks first // so that all unassigned tracks are stored in the beginning of the table together auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end // fixme: interaction time is undefined for unassigned tracks (?) - fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, - ambigTracksCursor, mftTracksCursor, ambigMFTTracksCursor, - fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, bcsMap); + fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, + ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, + fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); // filling collisions and tracks into tables collisionID = 0; + collisionsCursor.reserve(primVertices.size()); for (auto& vertex : primVertices) { auto& cov = vertex.getCov(); auto& timeStamp = vertex.getTimeStamp(); // this is a relative time @@ -1992,8 +2321,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } else { LOG(fatal) << "Error: could not find a corresponding BC ID for a collision; BC = " << globalBC << ", collisionID = " << collisionID; } - collisionsCursor(0, - bcID, + collisionsCursor(bcID, truncateFloatFraction(vertex.getX(), mCollisionPosition), truncateFloatFraction(vertex.getY(), mCollisionPosition), truncateFloatFraction(vertex.getZ(), mCollisionPosition), @@ -2012,54 +2340,66 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs[collisionID]; // passing interaction time in [ps] - fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, ambigTracksCursor, - mftTracksCursor, ambigMFTTracksCursor, - fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, bcsMap); + fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor, + mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, + fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); collisionID++; } fillSecondaryVertices(recoData, v0sCursor, cascadesCursor, decay3BodyCursor); + fillHMPID(recoData, hmpCursor); fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs); // helper map for fast search of a corresponding class mask for a bc - std::unordered_map bcToClassMask; + auto emcalIncomplete = filterEMCALIncomplete(recoData.getEMCALTriggers()); + std::unordered_map> bcToClassMask; if (mInputSources[GID::CTP]) { LOG(debug) << "CTP input available"; for (auto& ctpDigit : ctpDigits) { uint64_t bc = ctpDigit.intRecord.toLong(); uint64_t classMask = ctpDigit.CTPClassMask.to_ulong(); - bcToClassMask[bc] = classMask; + uint64_t inputMask = ctpDigit.CTPInputMask.to_ulong(); + if (emcalIncomplete.find(bc) != emcalIncomplete.end()) { + // reject EMCAL triggers as BC was rejected as incomplete at readout level + auto classMaskOrig = classMask; + classMask = classMask & ~mEMCALTrgClassMask; + LOG(debug) << "Found EMCAL incomplete event, mask before " << std::bitset<64>(classMaskOrig) << ", after " << std::bitset<64>(classMask); + } + bcToClassMask[bc] = {classMask, inputMask}; // LOG(debug) << Form("classmask:0x%llx", classMask); } } // filling BC table - uint64_t triggerMask = 0; + bcCursor.reserve(bcsMap.size()); for (auto& item : bcsMap) { uint64_t bc = item.first; + std::pair masks{0, 0}; if (mInputSources[GID::CTP]) { auto bcClassPair = bcToClassMask.find(bc); if (bcClassPair != bcToClassMask.end()) { - triggerMask = bcClassPair->second; - } else { - triggerMask = 0; + masks = bcClassPair->second; } } - bcCursor(0, - runNumber, + bcCursor(runNumber, bc, - triggerMask); + masks.first, + masks.second); } bcToClassMask.clear(); - if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) { - fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, bcsMap, recoData); + // filling BC flags table: + auto bcFlags = fillBCFlags(recoData, bcsMap); + bcFlagsCursor.reserve(bcFlags.size()); + for (auto f : bcFlags) { + bcFlagsCursor(f); } // fill cpvcluster table if (mInputSources[GIndex::CPV]) { float posX, posZ; + cpvClustersCursor.reserve(cpvTrigRecs.size()); for (auto& cpvEvent : cpvTrigRecs) { uint64_t bc = cpvEvent.getBCData().toLong(); auto item = bcsMap.find(bc); @@ -2072,8 +2412,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) for (int iClu = cpvEvent.getFirstEntry(); iClu < cpvEvent.getFirstEntry() + cpvEvent.getNumberOfObjects(); iClu++) { auto& clu = cpvClusters[iClu]; clu.getLocalPosition(posX, posZ); - cpvClustersCursor(0, - bcID, + cpvClustersCursor(bcID, truncateFloatFraction(posX, mCPVPos), truncateFloatFraction(posZ, mCPVPos), truncateFloatFraction(clu.getEnergy(), mCPVAmpl), @@ -2082,14 +2421,12 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } } - bcsMap.clear(); - if (mUseMC) { TStopwatch timer; timer.Start(); // filling mc particles table fillMCParticlesTable(*mcReader, - mcParticlesCursor, + mcParticlesCursor.cursor, primVer2TRefs, primVerGIs, recoData, @@ -2103,18 +2440,18 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) // need to go through labels in the same order as for tracks fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, primVer2TRefs.back(), primVerGIs, recoData); - for (int iref = 0; iref < primVer2TRefs.size() - 1; iref++) { + for (auto iref = 0U; iref < primVer2TRefs.size() - 1; iref++) { auto& trackRef = primVer2TRefs[iref]; - fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData); + fillMCTrackLabelsTable(mcTrackLabelCursor, mcMFTTrackLabelCursor, mcFwdTrackLabelCursor, trackRef, primVerGIs, recoData, iref); } + } - for (auto& label : recoData.getStrangeTracksMCLabels()) { - MCLabels labelHolder; - labelHolder.labelID = label.isValid() ? (*mToStore[label.getSourceID()][label.getEventID()])[label.getTrackID()] : -1; - labelHolder.labelMask = (label.isFake() << 15) | (label.isNoise() << 14); - mcTrackLabelCursor(0, labelHolder.labelID, labelHolder.labelMask); - } + // Fill calo tables and if MC also the MCCaloTable, therefore, has to be after fillMCParticlesTable call! + if (mInputSources[GIndex::PHS] || mInputSources[GIndex::EMC]) { + fillCaloTable(caloCellsCursor, caloCellsTRGTableCursor, mcCaloLabelsCursor, bcsMap, recoData); } + + bcsMap.clear(); clearMCKeepStore(mToStore); mGIDToTableID.clear(); mTableTrID = 0; @@ -2134,19 +2471,24 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) mBCLookup.clear(); - originCursor(0, tfNumber); + mGIDUsedBySVtx.clear(); + mGIDUsedByStr.clear(); + + originCursor(tfNumber); // sending metadata to writer TString dataType = mUseMC ? "MC" : "RAW"; TString O2Version = o2::fullVersion(); TString ROOTVersion = ROOT_RELEASE; - mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag"}; - mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag}; - pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0, Lifetime::Timeframe}, mMetaDataKeys); - pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0, Lifetime::Timeframe}, mMetaDataVals); + mMetaDataKeys = {"DataType", "Run", "O2Version", "ROOTVersion", "RecoPassName", "AnchorProduction", "AnchorPassName", "LPMProductionTag", "CreatedBy"}; + mMetaDataVals = {dataType, "3", O2Version, ROOTVersion, mRecoPass, mAnchorProd, mAnchorPass, mLPMProdTag, mUser}; + add_additional_meta_info(mMetaDataKeys, mMetaDataVals); + + pc.outputs().snapshot(Output{"AMD", "AODMetadataKeys", 0}, mMetaDataKeys); + pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals); - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0, Lifetime::Timeframe}, ""); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); mTimer.Stop(); } @@ -2242,6 +2584,7 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - error, t + error, bcsMap); } extraInfoHolder.trackTime = float(t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS); + extraInfoHolder.diffBCRef = int(bcOfTimeRef); LOGP(debug, "time : {}/{} -> {}/{} -> trunc: {}/{} CollID: {} Amb: {}", t, terr, t - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS, terr, truncateFloatFraction(extraInfoHolder.trackTime, mTrackTime), truncateFloatFraction(extraInfoHolder.trackTimeRes, mTrackTimeError), collisionID, trackIndex.isAmbiguous()); @@ -2258,7 +2601,10 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac extraInfoHolder.length = intLen; const float mass = o2::constants::physics::MassPionCharged; // default pid = pion if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) { - const float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + if (expBeta > o2::constants::math::Almost1) { + expBeta = o2::constants::math::Almost1; + } extraInfoHolder.tofExpMom = mass * expBeta / std::sqrt(1.f - expBeta * expBeta); } // correct the time of the track @@ -2285,30 +2631,52 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac int nClusters = itsTrack.getNClusters(); float chi2 = itsTrack.getChi2(); extraInfoHolder.itsChi2NCl = nClusters != 0 ? chi2 / (float)nClusters : 0; - extraInfoHolder.itsClusterMap = itsTrack.getPattern(); + extraInfoHolder.itsClusterSizes = itsTrack.getClusterSizes(); if (src == GIndex::ITS) { // standalone ITS track should set its time from the ROF const auto& rof = data.getITSTracksROFRecords()[mITSROFs[trackIndex.getIndex()]]; - double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS; + double t = rof.getBCData().differenceInBC(mStartIR) * o2::constants::lhc::LHCBunchSpacingNS + mITSROFrameHalfLengthNS + mITSROFBiasNS; setTrackTime(t, mITSROFrameHalfLengthNS, false); } } else if (contributorsGID[GIndex::Source::ITSAB].isIndexSet()) { // this is an ITS-TPC afterburner contributor - extraInfoHolder.itsClusterMap = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].pattern; + extraInfoHolder.itsClusterSizes = data.getITSABRefs()[contributorsGID[GIndex::Source::ITSAB].getIndex()].getClusterSizes(); } if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]); const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]]; - extraInfoHolder.tpcInnerParam = tpcOrig.getP(); + const auto& dEdx = tpcOrig.getdEdx().dEdxTotTPC > 0 ? tpcOrig.getdEdx() : tpcOrig.getdEdxAlt(); + if (tpcOrig.getdEdx().dEdxTotTPC == 0) { + extraInfoHolder.flags |= o2::aod::track::TPCdEdxAlt; + } + if (tpcOrig.hasASideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideA; + } + if (tpcOrig.hasCSideClusters()) { + extraInfoHolder.flags |= o2::aod::track::TPCSideC; + } + extraInfoHolder.tpcInnerParam = tpcOrig.getP() / tpcOrig.getAbsCharge(); extraInfoHolder.tpcChi2NCl = tpcOrig.getNClusters() ? tpcOrig.getChi2() / tpcOrig.getNClusters() : 0; - extraInfoHolder.tpcSignal = tpcOrig.getdEdx().dEdxTotTPC; + extraInfoHolder.tpcSignal = dEdx.dEdxTotTPC; extraInfoHolder.tpcNClsFindable = tpcOrig.getNClusters(); extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found; extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed; extraInfoHolder.tpcNClsShared = tpcClData.shared; - if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range - double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS; // half-span of the interval - double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value - LOG(debug) << "TPC tracks t0:" << tpcOrig.getTime0() << " tbwd: " << tpcOrig.getDeltaTBwd() << " tfwd: " << tpcOrig.getDeltaTFwd() << " t: " << t << " te: " << terr; - setTrackTime(t, terr, false); + uint32_t clsUsedForPID = dEdx.NHitsIROC + dEdx.NHitsOROC1 + dEdx.NHitsOROC2 + dEdx.NHitsOROC3; + extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID; + if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range + if (needBCSlice) { + double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value + double terr = 0.5 * (tpcOrig.getDeltaTFwd() + tpcOrig.getDeltaTBwd()) * mTPCBinNS; + double err = mTimeMarginTrackTime + terr; + bcOfTimeRef = fillBCSlice(extraInfoHolder.bcSlice, t - err, t + err, bcsMap); + } + aod::track::extensions::TPCTimeErrEncoding p; + p.setDeltaTFwd(tpcOrig.getDeltaTFwd()); + p.setDeltaTBwd(tpcOrig.getDeltaTBwd()); + extraInfoHolder.trackTimeRes = p.getTimeErr(); + extraInfoHolder.trackTime = float(tpcOrig.getTime0() * mTPCBinNS - bcOfTimeRef * o2::constants::lhc::LHCBunchSpacingNS); + extraInfoHolder.diffBCRef = int(bcOfTimeRef); + extraInfoHolder.isTPConly = true; // no truncation + extraInfoHolder.flags |= o2::aod::track::TrackTimeAsym; } else if (src == GIndex::ITSTPC) { // its-tpc matched tracks have gaussian time error and the time was not set above const auto& trITSTPC = data.getTPCITSTrack(trackIndex); auto ts = trITSTPC.getTimeMUS(); @@ -2324,6 +2692,181 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac return extraInfoHolder; } +AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int collisionID, std::uint64_t collisionBC, GIndex trackIndex, + const o2::globaltracking::RecoContainer& data, const std::map& bcsMap) +{ + TrackQA trackQAHolder; + auto contributorsGID = data.getTPCContributorGID(trackIndex); + const auto& trackPar = data.getTrackParam(trackIndex); + if (contributorsGID.isIndexSet()) { + auto prop = o2::base::Propagator::Instance(); + const auto& tpcOrig = data.getTPCTrack(contributorsGID); + /// getDCA - should be done with the copy of TPC only track + o2::track::TrackParametrization tpcTMP = tpcOrig; /// get backup of the track + const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// should be parameterized + const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); + std::array dcaInfo{-999., -999.}; + if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) { + trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); + trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); + } + // This allows to safely clamp any float to one byte, using the + // minmal/maximum values as under-/overflow borders and rounding to the nearest integer + auto safeInt8Clamp = [](auto value) -> int8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + auto safeUInt8Clamp = [](auto value) -> uint8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + + /// get tracklet byteMask + uint8_t clusterCounters[8] = {0}; + { + uint8_t sectorIndex, rowIndex; + uint32_t clusterIndex; + const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); + for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, tpcOrig.getClusterRef()); + char indexTracklet = (rowIndex % 152) / 19; + clusterCounters[indexTracklet]++; + } + } + uint8_t byteMask = 0; + for (int i = 0; i < 8; i++) { + if (clusterCounters[i] > 5) { + byteMask |= (1 << i); + } + } + trackQAHolder.tpcTime0 = tpcOrig.getTime0(); + trackQAHolder.tpcClusterByteMask = byteMask; + const auto& dEdxInfoAlt = tpcOrig.getdEdxAlt(); // tpcOrig.getdEdx() + const float dEdxNorm = (dEdxInfoAlt.dEdxTotTPC > 0) ? 100. / dEdxInfoAlt.dEdxTotTPC : 0; + trackQAHolder.tpcdEdxNorm = dEdxInfoAlt.dEdxTotTPC; + trackQAHolder.tpcdEdxMax0R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxIROC * dEdxNorm); + trackQAHolder.tpcdEdxMax1R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC1 * dEdxNorm); + trackQAHolder.tpcdEdxMax2R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC2 * dEdxNorm); + trackQAHolder.tpcdEdxMax3R = safeUInt8Clamp(dEdxInfoAlt.dEdxMaxOROC3 * dEdxNorm); + // + trackQAHolder.tpcdEdxTot0R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotIROC * dEdxNorm); + trackQAHolder.tpcdEdxTot1R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC1 * dEdxNorm); + trackQAHolder.tpcdEdxTot2R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC2 * dEdxNorm); + trackQAHolder.tpcdEdxTot3R = safeUInt8Clamp(dEdxInfoAlt.dEdxTotOROC3 * dEdxNorm); + /// + float scaleTOF{0}; + auto contributorsGIDA = data.getSingleDetectorRefs(trackIndex); + if (contributorsGIDA[GIndex::Source::TOF].isIndexSet()) { // ITS-TPC-TRD-TOF, ITS-TPC-TOF, TPC-TRD-TOF, TPC-TOF + const auto& tofMatch = data.getTOFMatch(trackIndex); + const float qpt = trackPar.getQ2Pt(); + scaleTOF = std::sqrt(o2::aod::track::trackQAScaledTOF[0] * o2::aod::track::trackQAScaledTOF[0] + qpt * qpt * o2::aod::track::trackQAScaledTOF[1] * o2::aod::track::trackQAScaledTOF[1]) / (2. * o2::aod::track::trackQAScaleBins); + trackQAHolder.dTofdX = safeInt8Clamp(tofMatch.getDXatTOF() / scaleTOF); + trackQAHolder.dTofdZ = safeInt8Clamp(tofMatch.getDZatTOF() / scaleTOF); + } + + // Add matching information at a reference point (defined by + // o2::aod::track::trackQARefRadius) in the same frame as the global track + // without material corrections and error propagation + if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { + const auto& itsOrig = data.getITSTrack(itsContGID); + o2::track::TrackPar gloCopy = trackPar; + o2::track::TrackPar itsCopy = itsOrig.getParamOut(); + o2::track::TrackPar tpcCopy = tpcOrig; + if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && + prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && + prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { + // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track + // The scale is defined by the global track scaling depending on beta0 + const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); + const float qpt = gloCopy.getQ2Pt(); + const float x = qpt / beta0; + // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) + auto scaleCont = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); + }; + auto scaleGlo = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); + }; + + // Calculate deltas for contributors + trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); + trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); + trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); + trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); + trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); + // Calculate deltas for global track against averaged contributors + trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); + trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); + trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); + trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); + trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); + // + + if (mStreamerFlags[AODProducerStreamerFlags::TrackQA]) { + (*mStreamer) << "trackQA" + << "trackITSOrig=" << itsOrig + << "trackTPCOrig=" << tpcOrig + << "trackITSTPCOrig=" << trackPar + << "trackITSProp=" << itsCopy + << "trackTPCProp=" << tpcCopy + << "trackITSTPCProp=" << gloCopy + << "refRadius=" << o2::aod::track::trackQARefRadius + << "scaleBins=" << o2::aod::track::trackQAScaleBins + << "scaleCont0=" << scaleCont(0) + << "scaleCont1=" << scaleCont(1) + << "scaleCont2=" << scaleCont(2) + << "scaleCont3=" << scaleCont(3) + << "scaleCont4=" << scaleCont(4) + << "scaleGlo0=" << scaleGlo(0) + << "scaleGlo1=" << scaleGlo(1) + << "scaleGlo2=" << scaleGlo(2) + << "scaleGlo3=" << scaleGlo(3) + << "scaleGlo4=" << scaleGlo(4) + << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 + << "trackQAHolder.tpcdEdxNorm=" << trackQAHolder.tpcdEdxNorm + << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR + << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ + << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask + << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R + << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R + << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R + << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R + << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R + << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R + << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R + << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R + << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY + << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ + << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp + << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl + << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt + << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY + << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ + << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp + << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl + << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt + << "trackQAHolder.dTofdX=" << trackQAHolder.dTofdX + << "trackQAHolder.dTofdZ=" << trackQAHolder.dTofdZ + << "scaleTOF=" << scaleTOF + << "\n"; + } + } + } + } + + return trackQAHolder; +} + +bool AODProducerWorkflowDPL::propagateTrackToPV(o2::track::TrackParametrizationWithError& trackPar, + const o2::globaltracking::RecoContainer& data, + int colID) +{ + o2::dataformats::DCA dcaInfo; + dcaInfo.set(999.f, 999.f, 999.f, 999.f, 999.f); + o2::dataformats::VertexBase v = mVtx.getMeanVertex(colID < 0 ? 0.f : data.getPrimaryVertex(colID).getZ()); + return o2::base::Propagator::Instance()->propagateToDCABxByBz(v, trackPar, 2.f, mMatCorr, &dcaInfo); +} + void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfoHolder, const o2::track::TrackPar& track) { constexpr float XEMCAL = 440.f, XPHOS = 460.f, XEMCAL2 = XEMCAL * XEMCAL; @@ -2341,7 +2884,8 @@ void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfo SPHOS, // 12 PHOS only SPHOS | SEMCAL, SPHOS | SEMCAL, SPHOS | SEMCAL, // 13:15 PHOS & DCAL SEMCAL, // 16 DCAL only - SNONE}; // 17 + SNONE // 17 + }; o2::track::TrackPar outTr{track}; auto prop = o2::base::Propagator::Instance(); @@ -2420,6 +2964,18 @@ void AODProducerWorkflowDPL::extrapolateToCalorimeters(TrackExtraInfo& extraInfo // } +std::set AODProducerWorkflowDPL::filterEMCALIncomplete(const gsl::span triggers) +{ + std::set emcalIncompletes; + for (const auto& trg : triggers) { + if (trg.getTriggerBits() & o2::emcal::triggerbits::Inc) { + // trigger record masked at incomplete at readout level + emcalIncompletes.insert(trg.getBCData().toLong()); + } + } + return emcalIncompletes; +} + void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); @@ -2427,12 +2983,12 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; // Note: DPLAlpideParam for ITS and MFT will be loaded by the RecoContainer - + mSqrtS = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS(); // apply settings auto grpECS = o2::base::GRPGeomHelper::instance().getGRPECS(); o2::BunchFilling bcf = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getBunchFilling(); std::bitset<3564> bs = bcf.getBCPattern(); - for (int i = 0; i < bs.size(); i++) { + for (auto i = 0U; i < bs.size(); i++) { if (bs.test(i)) { o2::tof::Utils::addInteractionBC(i); } @@ -2440,9 +2996,10 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); mITSROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsITS.roFrameLengthTrig); - + mITSROFBiasNS = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS; const auto& alpParamsMFT = o2::itsmft::DPLAlpideParam::Instance(); mMFTROFrameHalfLengthNS = 0.5 * (grpECS->isDetContinuousReadOut(o2::detectors::DetID::MFT) ? alpParamsMFT.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS : alpParamsMFT.roFrameLengthTrig); + mMFTROFBiasNS = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS; // RS FIXME: this is not yet fetched from the CCDB auto& elParam = o2::tpc::ParameterElectronics::Instance(); @@ -2452,6 +3009,11 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) mNSigmaTimeTrack = pvParams.nSigmaTimeTrack; mTimeMarginTrackTime = pvParams.timeMarginTrackTime * 1.e3; mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01; + + pc.inputs().get("ctpconfig"); + } + if (mPropTracks) { + pc.inputs().get("meanvtx"); } } @@ -2460,6 +3022,9 @@ void AODProducerWorkflowDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob { // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) { + o2::mch::TrackExtrap::setField(); + } return; } if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { @@ -2474,6 +3039,22 @@ void AODProducerWorkflowDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob par.printKeyValues(); return; } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } + if (matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) { + // construct mask with EMCAL trigger classes for rejection of incomplete triggers + auto ctpconfig = *(const o2::ctp::CTPConfiguration*)obj; + mEMCALTrgClassMask = 0; + for (const auto& trgclass : ctpconfig.getCTPClasses()) { + if (trgclass.cluster->maskCluster[o2::detectors::DetID::EMC]) { + mEMCALTrgClassMask |= trgclass.classMask; + } + } + LOG(info) << "Loaded EMCAL trigger class mask: " << std::bitset<64>(mEMCALTrgClassMask); + } } void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTrackRef& trackRef, const gsl::span& GIndices, @@ -2516,6 +3097,9 @@ void AODProducerWorkflowDPL::addRefGlobalBCsForTOF(const o2::dataformats::VtxTra float tofExpMom = 0.; if (tofInt.getTOF(o2::track::PID::Pion) > 0.f) { float expBeta = (intLen / (tofInt.getTOF(o2::track::PID::Pion) * cSpeed)); + if (expBeta > o2::constants::math::Almost1) { + expBeta = o2::constants::math::Almost1; + } tofExpMom = o2::constants::physics::MassPionCharged * expBeta / std::sqrt(1.f - expBeta * expBeta); } else { continue; @@ -2600,30 +3184,64 @@ std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, return bcOfTimeRef; } -void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& ec) +std::vector AODProducerWorkflowDPL::fillBCFlags(const o2::globaltracking::RecoContainer& data, std::map& bcsMap) const +{ + std::vector flags(bcsMap.size()); + + // flag BCs belonging to UPC mode ITS ROFs + auto bcIt = bcsMap.begin(); + auto itsrofs = data.getITSTracksROFRecords(); + auto lROF = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + auto bROF = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + for (auto& rof : itsrofs) { + if (!rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) { + continue; + } + uint64_t globalBC0 = rof.getBCData().toLong() + bROF, globalBC1 = globalBC0 + lROF - 1; + // BCs are sorted, iterate until the start of ROF + while (bcIt != bcsMap.end()) { + if (bcIt->first < globalBC0) { + ++bcIt; + continue; + } + if (bcIt->first > globalBC1) { + break; + } + flags[bcIt->second] |= o2::aod::bc::ITSUPCMode; + ++bcIt; + } + } + return flags; +} + +void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& /*ec*/) { LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + + mStreamer.reset(); } -DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun) +DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra) { - std::vector outputs; auto dataRequest = std::make_shared(); dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun)); dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (src[GID::CTP]) { dataRequest->requestCTPDigits(useMC); } if (enableSV) { - dataRequest->requestSecondaryVertertices(useMC); + dataRequest->requestSecondaryVertices(useMC); } if (enableStrangenessTracking) { dataRequest->requestStrangeTracks(useMC); LOGF(info, "requestStrangeTracks Finish"); } + if (src[GID::ITS]) { + dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false); + } if (src[GID::TPC]) { dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks } @@ -2652,51 +3270,73 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo dataRequest->inputs, true); // query only once all objects except mag.field - outputs.emplace_back(OutputLabel{"O2bc"}, "AOD", "BC", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2cascade_001"}, "AOD", "CASCADE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2collision_001"}, "AOD", "COLLISION_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2decay3body"}, "AOD", "DECAY3BODY", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fdd_001"}, "AOD", "FDD_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ft0"}, "AOD", "FT0", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fv0a"}, "AOD", "FV0A", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fwdtrack"}, "AOD", "FWDTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2fwdtrackcov"}, "AOD", "FWDTRACKCOV", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mccollision"}, "AOD", "MCCOLLISION", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mccollisionlabel"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcmfttracklabel"}, "AOD", "MCMFTTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcfwdtracklabel"}, "AOD", "MCFWDTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mcparticle_001"}, "AOD", "MCPARTICLE_001", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mctracklabel"}, "AOD", "MCTRACKLABEL", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2mfttrack"}, "AOD", "MFTTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2track_iu"}, "AOD", "TRACK_IU", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackcov_iu"}, "AOD", "TRACKCOV_IU", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackextra"}, "AOD", "TRACKEXTRA", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackedcascade"}, "AOD", "TRACKEDCASCADE", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2trackedv0"}, "AOD", "TRACKEDV0", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2tracked3body"}, "AOD", "TRACKED3BODY", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguoustrack"}, "AOD", "AMBIGUOUSTRACK", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguousMFTtrack"}, "AOD", "AMBIGUOUSMFTTR", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2ambiguousFwdtrack"}, "AOD", "AMBIGUOUSFWDTR", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2v0_001"}, "AOD", "V0_001", 0, Lifetime::Timeframe); -#ifdef O2_ZDC_NEWDATAMODEL - outputs.emplace_back(OutputLabel{"O2zdc_001"}, "AOD", "ZDC", 1, Lifetime::Timeframe); -#else - outputs.emplace_back(OutputLabel{"O2zdc"}, "AOD", "ZDC", 0, Lifetime::Timeframe); -#endif - outputs.emplace_back(OutputLabel{"O2caloCell"}, "AOD", "CALO", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2caloCellTRGR"}, "AOD", "CALOTRIGGER", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2cpvCluster"}, "AOD", "CPVCLUSTER", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputLabel{"O2origin"}, "AOD", "ORIGIN", 0, Lifetime::Timeframe); - outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); - outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataKeys"}); - outputs.emplace_back(OutputSpec{"AMD", "AODMetadataVals"}); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + + using namespace o2::aod; + using namespace o2::aodproducer; + + std::vector outputs{ + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + OutputSpec{"AMD", "AODMetadataKeys"}, + OutputSpec{"AMD", "AODMetadataVals"}}; + + if (useMC) { + outputs.insert(outputs.end(), + {OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + // todo: use addTableToOuput helper? + // currently the description is MCCOLLISLABEL, so + // the name in AO2D would be O2mccollislabel + // addTableToOutput(outputs); + {OutputLabel{"McCollisionLabels"}, "AOD", "MCCOLLISIONLABEL", 0, Lifetime::Timeframe}}); + } return DataProcessorSpec{ "aod-producer-workflow", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC)}, + AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra)}, Options{ ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}}, ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}}, @@ -2706,9 +3346,26 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}}, ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}}, ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}}, + ConfigParamSpec{"created-by", VariantType::String, "", {"Who created this AO2D"}}, ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}}, ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}}, - ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}}}}; + ConfigParamSpec{"ctpreadout-create", VariantType::Int, 0, {"Create CTP digits from detector readout and CTP inputs. !=1 -- off, 1 -- on"}}, + ConfigParamSpec{"emc-select-leading", VariantType::Bool, false, {"Flag to select if only the leading contributing particle for an EMCal cell should be stored"}}, + ConfigParamSpec{"propagate-tracks", VariantType::Bool, false, {"Propagate tracks (not used for secondary vertices) to IP"}}, + ConfigParamSpec{"propagate-tracks-max-xiu", VariantType::Float, 5.0f, {"Propagate tracks to IP if X_IU smaller than this value (and if propagate tracks enabled)"}}, + ConfigParamSpec{"hepmc-update", VariantType::String, "always", {"When to update HepMC Aux tables: always - force update, never - never update, all - if all keys are present, any - when any key is present (not valid yet)"}}, + ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, + ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, + ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, + ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}}, + ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}}, + ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}}, + ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}}, + ConfigParamSpec{"trackqc-tpc-cls", VariantType::Int, 80, {"Keep TPC standalone track with this #clusters"}}, + ConfigParamSpec{"trackqc-tpc-pt", VariantType::Float, 0.2f, {"Keep TPC standalone track with this pt"}}, + ConfigParamSpec{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}}, + ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}}, + ConfigParamSpec{"mc-signal-filt", VariantType::Bool, false, {"Enable usage of signal filtering (only for MC with embedding)"}}}}; } } // namespace o2::aodproducer diff --git a/Detectors/AOD/src/StandaloneAODProducer.cxx b/Detectors/AOD/src/StandaloneAODProducer.cxx index 151038465c1d1..7ac59a556ac08 100644 --- a/Detectors/AOD/src/StandaloneAODProducer.cxx +++ b/Detectors/AOD/src/StandaloneAODProducer.cxx @@ -86,7 +86,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) // mccollision::PosX, mccollision::PosY, mccollision::PosZ, mccollision::T, mccollision::Weight, // mccollision::ImpactParameter); - mcCollCursor(0, 0 /*bcID*/, 0 /*genID*/, header.GetX(), header.GetY(), header.GetZ(), time, 1. /*weight*/, header.GetB()); + mcCollCursor(0, 0 /*bcID*/, 0 /*genID*/, header.GetX(), header.GetY(), header.GetZ(), time, 1. /*weight*/, header.GetB(), 0.0); index++; } @@ -94,7 +94,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) TFile outfile("aod.root", "UPDATE"); { - TableToTree t2t(mccoltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(mccoltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } @@ -200,12 +200,12 @@ void fillCollisionAndTrackTable() f.Close(); TFile outfile("aod.root", "RECREATE"); { - TableToTree t2t(colltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(colltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } { - TableToTree t2t(tracktable, &outfile, "Tracks" /* aod::MetadataTrait::metadata::tableLabel() */); + TableToTree t2t(tracktable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } diff --git a/Detectors/AOD/src/aod-mc-producer-workflow.cxx b/Detectors/AOD/src/aod-mc-producer-workflow.cxx index 0bfd1ff021272..4f32bacac6fef 100644 --- a/Detectors/AOD/src/aod-mc-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-mc-producer-workflow.cxx @@ -41,3 +41,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) wf.emplace_back(o2::aodmcproducer::getAODMcProducerWorkflowSpec()); return std::move(wf); } +// +// EOF +// diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index 619da0793b4a4..81e178642e403 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -36,11 +36,12 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, - {"enable-strangeness-tracking", o2::framework::VariantType::Bool, false, {"enable strangeness tracking"}}, + {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, + {"enable-FIT-extra", o2::framework::VariantType::Bool, false, {"enable FIT extra output"}}, {"info-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, - {"ctpconfig-per-run", o2::framework::VariantType::Bool, false, {"Use CTP config per run. 1 -- on (Data), 0 -- off (MC)"}}}; + {"ctpconfig-run-independent", o2::framework::VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -52,10 +53,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); bool enableSV = !configcontext.options().get("disable-secondary-vertices"); - bool enableST = configcontext.options().get("enable-strangeness-tracking"); - bool ctpcfgperrun = configcontext.options().get("ctpconfig-per-run"); + bool enableST = !configcontext.options().get("disable-strangeness-tracker"); + bool ctpcfgperrun = !configcontext.options().get("ctpconfig-run-independent"); + bool enableFITextra = configcontext.options().get("enable-FIT-extra"); - GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV"); + GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV,HMP"); GID::mask_t src = allowedSrc & GID::getSourcesMask(configcontext.options().get("info-sources")); // manually add TOF to MC mask for addInputSpecs() @@ -64,7 +66,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } WorkflowSpec specs; - specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, enableST, useMC, ctpcfgperrun)); + specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, enableST, useMC, ctpcfgperrun, enableFITextra)); auto srcCls = src & ~(GID::getSourceMask(GID::MCH) | GID::getSourceMask(GID::MID)); // Don't read global MID and MCH clusters (those attached to tracks are always read) auto srcMtc = src; diff --git a/Detectors/Align/CMakeLists.txt b/Detectors/Align/CMakeLists.txt index c04b09dce44eb..d99e1a803612f 100644 --- a/Detectors/Align/CMakeLists.txt +++ b/Detectors/Align/CMakeLists.txt @@ -18,7 +18,7 @@ o2_add_library(Align src/AlignableDetectorITS.cxx #src/AlignableDetectorHMPID.cxx src/AlignableDetectorTOF.cxx - #src/AlignableDetectorTPC.cxx + src/AlignableDetectorTPC.cxx src/AlignableDetectorTRD.cxx src/Millepede2Record.cxx src/AlignmentPoint.cxx @@ -28,7 +28,7 @@ o2_add_library(Align #src/AlignableSensorHMPID.cxx src/AlignableSensorITS.cxx src/AlignableSensorTOF.cxx - #src/AlignableSensorTPC.cxx + src/AlignableSensorTPC.cxx src/AlignableSensorTRD.cxx src/Controller.cxx src/AlignmentTrack.cxx @@ -36,6 +36,8 @@ o2_add_library(Align src/EventVertex.cxx src/AlignConfig.cxx src/Mille.cxx + src/AlgPntDbg.cxx + src/AlgTrcDbg.cxx PUBLIC_LINK_LIBRARIES O2::FrameworkLogger O2::Steer O2::ReconstructionDataFormats @@ -46,6 +48,8 @@ o2_add_library(Align O2::TRDBase O2::TOFBase O2::DataFormatsGlobalTracking + O2::TPCCalibration + O2::GPUTracking ROOT::Core ROOT::Geom ROOT::MathCore @@ -60,7 +64,7 @@ o2_target_root_dictionary( include/Align/AlignableDetector.h include/Align/AlignableDetectorITS.h include/Align/AlignableDetectorTOF.h - #include/Align/AlignableDetectorTPC.h + include/Align/AlignableDetectorTPC.h include/Align/AlignableDetectorTRD.h #include/Align/AlignableDetectorHMPID.h include/Align/Millepede2Record.h @@ -68,7 +72,7 @@ o2_target_root_dictionary( include/Align/AlignableSensor.h include/Align/AlignableSensorITS.h include/Align/AlignableSensorTOF.h - #include/Align/AlignableSensorTPC.h + include/Align/AlignableSensorTPC.h include/Align/AlignableSensorTRD.h #include/Align/AlignableSensorHMPID.h include/Align/Controller.h @@ -80,6 +84,8 @@ o2_target_root_dictionary( include/Align/GeometricalConstraint.h include/Align/utils.h include/Align/AlignConfig.h + include/Align/AlgPntDbg.h + include/Align/AlgTrcDbg.h ) add_subdirectory(Workflow) diff --git a/Detectors/Align/Workflow/CMakeLists.txt b/Detectors/Align/Workflow/CMakeLists.txt index 1198064bc8984..8ceb50f71ca29 100644 --- a/Detectors/Align/Workflow/CMakeLists.txt +++ b/Detectors/Align/Workflow/CMakeLists.txt @@ -22,6 +22,8 @@ o2_add_library(AlignmentWorkflow O2::DataFormatsTPC O2::DataFormatsTRD O2::DataFormatsTOF + O2::GPUTracking + O2::TPCCalibration ROOT::Core ROOT::Geom ROOT::MathCore diff --git a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h index fdfb62cebad5e..197ace2bd9d20 100644 --- a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h +++ b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h @@ -21,12 +21,17 @@ using namespace o2::framework; namespace o2 { +namespace tpc +{ +struct CorrectionMapsLoaderGloOpts; +} + namespace align { /// create a processor spec framework::DataProcessorSpec getBarrelAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcMP, o2::dataformats::GlobalTrackID::mask_t src, - o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC); + o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace align } // namespace o2 diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 1192377c64f47..d4ab53c8181ce 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -35,7 +35,11 @@ #include "DataFormatsITSMFT/TopologyDictionary.h" #include "TRDBase/TrackletTransformer.h" #include "CommonUtils/TreeStreamRedirector.h" - +#include "TPCCalibration/VDriftHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "GPUO2ExternalUser.h" +#include "GPUO2InterfaceUtils.h" +#include "GPUParam.h" #include "Headers/DataHeader.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Task.h" @@ -82,9 +86,14 @@ class BarrelAlignmentSpec : public Task CheckConstaints = 0x1 << 1, GenPedeFiles = 0x1 << 2, LabelPedeResults = 0x1 << 3 }; - BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, - DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC) - : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC) {} + BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, const o2::tpc::CorrectionMapsLoaderGloOpts& tpcOpt, + DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC, bool loadTPCCalib) + : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) + { + mTPCCorrMapsLoader.setLumiScaleType(tpcOpt.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(tpcOpt.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(tpcOpt.checkCTPIDCconsistency); + } ~BarrelAlignmentSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -98,6 +107,8 @@ class BarrelAlignmentSpec : public Task bool mUseMC = false; bool mIgnoreCCDBAlignment = false; bool mCosmic = false; + bool mLoadTPCCalib = false; + int mLane = 0; int mPostProcessing = 0; // special mode of extracting alignment or constraints check GTrackID::mask_t mMPsrc{}; DetID::mask_t mDetMask{}; @@ -108,6 +119,11 @@ class BarrelAlignmentSpec : public Task std::unique_ptr mUsrConfMethod; std::unique_ptr mTRDTransformer; std::unique_ptr mDBGOut; + std::unique_ptr mTPCParam; + + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + // TStopwatch mTimer; }; @@ -117,12 +133,12 @@ void BarrelAlignmentSpec::init(InitContext& ic) mTimer.Stop(); mTimer.Reset(); o2::base::GRPGeomHelper::instance().setRequest(mGRPGeomRequest); - int dbg = ic.options().get("debug-output"), inst = ic.services().get().inputTimesliceId; - mController = std::make_unique(mDetMask, mMPsrc, mCosmic, mUseMC, inst); + + int dbg = ic.options().get("debug-output"); + mLane = ic.services().get().inputTimesliceId; + mController = std::make_unique(mDetMask, mMPsrc, mCosmic, mUseMC, mLane); if (dbg) { - mDBGOut = std::make_unique(fmt::format("mpDebug_{}.root", inst).c_str(), "recreate"); mController->setDebugOutputLevel(dbg); - mController->setDebugStream(mDBGOut.get()); } mConfMacro = ic.options().get("config-macro"); @@ -132,8 +148,7 @@ void BarrelAlignmentSpec::init(InitContext& ic) } std::string tmpmacro = mConfMacro + "+"; TString cmd = gSystem->GetMakeSharedLib(); - cmd.ReplaceAll("$Opt", "-O0 -g -ggdb"); - + cmd += " -O0 -g -ggdb"; // protect macro compilation by semaphore (to avoid problems in the pipelined code) { boost::interprocess::named_semaphore* sem = nullptr; @@ -169,15 +184,25 @@ void BarrelAlignmentSpec::init(InitContext& ic) } mIgnoreCCDBAlignment = ic.options().get("ignore-ccdb-alignment"); if (!mPostProcessing) { + if (mLoadTPCCalib) { + mTPCCorrMapsLoader.init(ic); + } if (GTrackID::includesDet(DetID::TRD, mMPsrc)) { mTRDTransformer.reset(new o2::trd::TrackletTransformer); if (ic.options().get("apply-xor")) { mTRDTransformer->setApplyXOR(); } + auto prevShift = mTRDTransformer->isShiftApplied(); + if (getenv("ALIEN_JDL_LPMPRODUCTIONTYPE") && std::strcmp(getenv("ALIEN_JDL_LPMPRODUCTIONTYPE"), "MC") == 0) { + // apply artificial pad shift in case non-ideal alignment is used to compensate for shift in current alignment from real data + mTRDTransformer->setApplyShift(false); + } + LOGP(info, "Old TRD shift : {} new : {}", prevShift, mTRDTransformer->isShiftApplied()); mController->setTRDTransformer(mTRDTransformer.get()); } mController->setAllowAfterburnerTracks(ic.options().get("allow-afterburner-tracks")); } + mIniParFile = ic.options().get("initial-params-file"); mUseIniParErrors = !ic.options().get("ignore-initial-params-errors"); if (mPostProcessing && !(mPostProcessing != GenPedeFiles) && (mIniParFile.empty() || mIniParFile == "none")) { @@ -187,10 +212,13 @@ void BarrelAlignmentSpec::init(InitContext& ic) void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) { - static bool initOnceDone = false; - if (!initOnceDone) { - initOnceDone = true; - o2::base::GRPGeomHelper::instance().checkUpdates(pc); + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto tinfo = pc.services().get(); + if (pc.services().get().globalRunNumberChanged) { + if (mController->getDebugOutputLevel()) { + mDBGOut = std::make_unique(fmt::format("mpDebug_{}_{:08d}_{:010d}.root", mLane, tinfo.runNumber, tinfo.tfCounter).c_str(), "recreate"); + mController->setDebugStream(mDBGOut.get()); + } if (!mIgnoreCCDBAlignment) { for (auto id = DetID::First; id <= DetID::Last; id++) { const auto* alg = o2::base::GRPGeomHelper::instance().getAlignment(id); @@ -216,7 +244,7 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) // call this in the very end if (mUsrConfMethod) { - int dummyPar = 0, ret = 0; + int dummyPar = 0, ret = -1; Controller* tmpPtr = mController.get(); const void* args[2] = {&tmpPtr, &dummyPar}; mUsrConfMethod->Execute(nullptr, args, 2, &ret); @@ -230,6 +258,40 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) if (GTrackID::includesDet(DetID::TRD, mMPsrc) && mTRDTransformer) { pc.inputs().get("calvdexb"); // just to trigger the finaliseCCDB } + if (mLoadTPCCalib) { + + static float prevField = 1e-6; + float newField = o2::base::Propagator::Instance()->getNominalBz(); + if (prevField != newField) { + prevField = newField; + if (mDetMask[DetID::TPC]) { + mTPCParam.reset(new o2::gpu::GPUParam); + mTPCParam->SetDefaults(o2::base::Propagator::Instance()->getNominalBz(), false); + mController->setTPCParam(mTPCParam.get()); + } + } + + mTPCVDriftHelper.extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); + bool updateMaps = false; + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } + mController->setTPCCorrMaps(&mTPCCorrMapsLoader); + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mController->setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + } + } } void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -248,13 +310,20 @@ void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& match mTRDTransformer->setCalVdriftExB((const o2::trd::CalVdriftExB*)obj); return; } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + + return; + } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } } void BarrelAlignmentSpec::run(ProcessingContext& pc) { mTimer.Start(false); - updateTimeDependentParams(pc); if (mPostProcessing) { // special mode, no data processing + updateTimeDependentParams(pc); if (mController->getInstanceID() == 0) { if (mPostProcessing & PostProc::CheckConstaints) { mController->addAutoConstraints(); @@ -268,6 +337,7 @@ void BarrelAlignmentSpec::run(ProcessingContext& pc) } else { RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // call after collectData !!! mController->setRecoContainer(&recoData); mController->setTimingInfo(pc.services().get()); if (mCosmic) { @@ -304,20 +374,34 @@ void BarrelAlignmentSpec::endOfStream(EndOfStreamContext& ec) mDBGOut.reset(); } -DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC) +DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; auto dataRequest = std::make_shared(); + bool loadTPCCalib = false; + Options opts{ + ConfigParamSpec{"apply-xor", o2::framework::VariantType::Bool, false, {"flip the 8-th bit of slope and position (for processing TRD CTFs from 2021 pilot beam)"}}, + ConfigParamSpec{"allow-afterburner-tracks", VariantType::Bool, false, {"allow using ITS-TPC afterburner tracks"}}, + ConfigParamSpec{"ignore-ccdb-alignment", VariantType::Bool, false, {"do not aplly CCDB alignment to ideal geometry"}}, + ConfigParamSpec{"initial-params-file", VariantType::String, "", {"initial parameters file"}}, + ConfigParamSpec{"config-macro", VariantType::String, "", {"configuration macro with signature (o2::align::Controller*, int) to execute from init"}}, + ConfigParamSpec{"ignore-initial-params-errors", VariantType::Bool, false, {"ignore initial params (if any) errors for precondition"}}, + ConfigParamSpec{"debug-output", VariantType::Int, 0, {"produce debugging output root files"}}}; if (!postprocess) { dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(src, false, skipDetClusters); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (GTrackID::includesDet(DetID::TRD, srcMP)) { dataRequest->inputs.emplace_back("calvdexb", "TRD", "CALVDRIFTEXB", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CalVdriftExB")); } if (enableCosmic) { dataRequest->requestCoscmicTracks(useMC); } + if (src[DetID::TPC] && !skipDetClusters[DetID::TPC]) { + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + loadTPCCalib = true; + } } auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true @@ -326,21 +410,15 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ true, // askMatLUT o2::base::GRPGeomRequest::Alignments, // geometry dataRequest->inputs, - false, // ask update once (except field) - true); // init PropagatorD + false, // ask update once (except field) + true, // init PropagatorD + "ITS,TPC,TRD,TOF"); // alignment objects to apply return DataProcessorSpec{ "barrel-alignment", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, dets, enableCosmic, postprocess, useMC)}, - Options{ - ConfigParamSpec{"apply-xor", o2::framework::VariantType::Bool, false, {"flip the 8-th bit of slope and position (for processing TRD CTFs from 2021 pilot beam)"}}, - ConfigParamSpec{"allow-afterburner-tracks", VariantType::Bool, false, {"allow using ITS-TPC afterburner tracks"}}, - ConfigParamSpec{"ignore-ccdb-alignment", VariantType::Bool, false, {"do not aplly CCDB alignment to ideal geometry"}}, - ConfigParamSpec{"initial-params-file", VariantType::String, "", {"initial parameters file"}}, - ConfigParamSpec{"config-macro", VariantType::String, "", {"configuration macro with signature (o2::align::Controller*, int) to execute from init"}}, - ConfigParamSpec{"ignore-initial-params-errors", VariantType::Bool, false, {"ignore initial params (if any) errors for precondition"}}, - ConfigParamSpec{"debug-output", VariantType::Int, 0, {"produce debugging output root files"}}}}; + AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, sclOpts, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, + opts}; } } // namespace align diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index 0fdd1d28ee327..8df479ba39260 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -19,6 +19,8 @@ #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" @@ -51,12 +53,13 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"enable-mc", o2::framework::VariantType::Bool, false, {"enable MC-info checks"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, - {"detectors", VariantType::String, std::string{"ITS,TRD,TOF"}, {"comma-separated list of detectors"}}, + {"detectors", VariantType::String, std::string{"ITS,TPC,TRD,TOF"}, {"comma-separated list of detectors"}}, {"enable-tpc-tracks", VariantType::Bool, false, {"allow reading TPC tracks"}}, {"enable-tpc-clusters", VariantType::Bool, false, {"allow reading TPC clusters (will trigger TPC tracks reading)"}}, {"enable-cosmic", VariantType::Bool, false, {"enable cosmic tracks)"}}, {"postprocessing", VariantType::Int, 0, {"postprocessing bits: 1 - extract alignment objects, 2 - check constraints, 4 - print mpParams/Constraints, 8 - relabel pede results"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -79,7 +82,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); - DetID::mask_t allowedDets = DetID::getMask("ITS,TRD,TOF"); + DetID::mask_t allowedDets = DetID::getMask("ITS,TPC,TRD,TOF"); // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); @@ -96,6 +99,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCl{}, srcMP = src; // we may need to load more track types than requested to satisfy dependencies, but only those will be fed to millipede + if (dets[DetID::TPC]) { + loadTPCClusters = loadTPCTracks = true; + } + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); if (!postprocess) { // this part is needed only if the data should be read if (GID::includesDet(DetID::ITS, src)) { src |= GID::getSourceMask(GID::ITS); @@ -134,11 +141,18 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } LOG(info) << "adding TOF request"; } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2_barrel_alignment_configuration.ini"); } - specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC)); + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + + specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC, sclOpt)); // RS FIXME: check which clusters are really needed if (!postprocess) { GID::mask_t dummy; diff --git a/Detectors/Align/include/Align/AlgPntDbg.h b/Detectors/Align/include/Align/AlgPntDbg.h new file mode 100644 index 0000000000000..12e8205f6b614 --- /dev/null +++ b/Detectors/Align/include/Align/AlgPntDbg.h @@ -0,0 +1,91 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlignmentPoint.h +/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch +/// @since 2021-02-01 +/// @brief Meausered point in the sensor. + +/** + * Compact alignment point info for debugging + */ + +#ifndef ALGPNTDBG_H +#define ALGPNTDBG_H + +#include "Align/AlignmentPoint.h" + +namespace o2 +{ +namespace align +{ + +struct AlgPntDbg { + public: + using DetID = o2::detectors::DetID; + // + enum { + UpperLeg = 0 + }; + // + AlgPntDbg() = default; + AlgPntDbg(const AlgPntDbg&) = default; + ~AlgPntDbg() = default; + AlgPntDbg& operator=(const AlgPntDbg& other) = default; + AlgPntDbg(const AlignmentPoint* point) : mDetID(point->getDetID()), mSID(point->getSID()), mAlpha(point->getAlphaSens()), mX(point->getXTracking()), mY(point->getYTracking()), mZ(point->getZTracking()), mErrYY(point->getYZErrTracking()[0]), mErrZZ(point->getYZErrTracking()[2]), mErrYZ(point->getYZErrTracking()[1]) + { + mSinAlp = std::sin(mAlpha); + mCosAlp = std::cos(mAlpha); + mSnp = point->getTrParamWSA()[2]; // track Snp at the sensor + if (point->isInvDir()) { + setUpperLeg(); + } + } + + float getR() const { return std::sqrt(mX * mX + mY * mY); } + float getYTrack() const { return mY + mYRes; } + float getZTrack() const { return mZ + mZRes; } + float getXTrack() const { return mX; } + float getXLab() const { return mX * mCosAlp - mY * mSinAlp; } + float getYLab() const { return mX * mSinAlp + mY * mCosAlp; } + float getZLap() const { return mZ; } + float getXTrackLab() const { return mX * mCosAlp - getYTrack() * mSinAlp; } + float getYTrackLab() const { return mX * mSinAlp + getYTrack() * mCosAlp; } + float getZTrackLab() const { return getZTrack(); } + float getPhi() const { return std::atan2(getYLab(), getXLab()); } + void setFlag(int i) { mFlags |= 0x1 << i; } + bool getFlag(int i) const { return (mFlags & (0x1 << i)) != 0; } + + void setUpperLeg() { setFlag(int(UpperLeg)); } + bool isUpperLeg() const { return getFlag(int(UpperLeg)); } + + int mDetID{}; // DetectorID + int16_t mSID = -1; // sensor ID in the detector + uint16_t mFlags = 0; // flags + float mAlpha = 0.f; // Alpha of tracking frame + float mSinAlp = 0.f; + float mCosAlp = 0.f; + float mX = 0.f; // tracking X + float mY = 0.f; // tracking Y + float mZ = 0.f; // Z + float mYRes = 0.f; // tracking Y residual (track - point) + float mZRes = 0.f; // Z residual + float mErrYY = 0.f; + float mErrZZ = 0.f; + float mErrYZ = 0.f; + float mSnp = 0.f; + + ClassDefNV(AlgPntDbg, 1); +}; + +} // namespace align +} // namespace o2 +#endif diff --git a/Detectors/Align/include/Align/AlgTrcDbg.h b/Detectors/Align/include/Align/AlgTrcDbg.h new file mode 100644 index 0000000000000..cb179dd8b9b2f --- /dev/null +++ b/Detectors/Align/include/Align/AlgTrcDbg.h @@ -0,0 +1,90 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgTrcDbg.h +/// @author ruben.shahoyan@cern.ch + +#ifndef ALGTRCDBG_H +#define ALGTRCDBG_H + +#include "Align/AlignmentTrack.h" +#include "Align/AlgPntDbg.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2 +{ +namespace align +{ + +struct AlgTrcDbg : public o2::track::TrackParCov { + AlgTrcDbg(const AlignmentTrack* trc) { setTrackParam(trc); } + AlgTrcDbg() = default; + ~AlgTrcDbg() = default; + AlgTrcDbg(const AlgTrcDbg&) = default; + AlgTrcDbg& operator=(const AlgTrcDbg&) = default; + + bool setTrackParam(const AlignmentTrack* trc) + { + if (!trc) { + return false; + } + setX(trc->getX()); + setY(trc->getAlpha()); + for (int i = 0; i < 5; i++) { + setParam(trc->getParam(i), i); + } + for (int i = 0; i < 15; i++) { + setCov(trc->getCov()[i], i); + } + mPoints.clear(); + for (int i = 0; i < trc->getNPoints(); i++) { + const auto* tpoint = trc->getPoint(i); + if (tpoint->containsMeasurement()) { + auto& pnt = mPoints.emplace_back(tpoint); + pnt.mYRes = trc->getResidual(0, i); + pnt.mZRes = trc->getResidual(1, i); + } + } + setX(trc->getX()); + setY(trc->getAlpha()); + for (int i = 0; i < 5; i++) { + setParam(trc->getParam(i), i); + } + for (int i = 0; i < 15; i++) { + setCov(trc->getCov()[i], i); + } + for (int i = 0; i < trc->getNPoints(); i++) { + const auto* tpoint = trc->getPoint(i); + if (tpoint->containsMeasurement()) { + auto& pnt = mPoints.emplace_back(tpoint); + pnt.mYRes = trc->getResidual(0, i); + pnt.mZRes = trc->getResidual(1, i); + } + } + mGID.clear(); + mGIDCosmUp.clear(); + return true; + } + + auto getNPoints() const { return mPoints.size(); } + bool isCosmic() const { return mGIDCosmUp.isSourceSet(); } + + std::vector mPoints; + o2::dataformats::GlobalTrackID mGID{}; + o2::dataformats::GlobalTrackID mGIDCosmUp{}; // GID of upper leg in case of cosmic + // + ClassDefNV(AlgTrcDbg, 1); +}; + +} // namespace align +} // namespace o2 +#endif diff --git a/Detectors/Align/include/Align/AlignConfig.h b/Detectors/Align/include/Align/AlignConfig.h index 0400f11190806..e72d436a14e3b 100644 --- a/Detectors/Align/include/Align/AlignConfig.h +++ b/Detectors/Align/include/Align/AlignConfig.h @@ -35,10 +35,12 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { float q2PtMin[NTrackTypes] = {0.01, 0.01}; float q2PtMax[NTrackTypes] = {10., 10.}; float tglMax[NTrackTypes] = {3., 10.}; + float defPTB0Coll = 0.6; + float defPTB0Cosm = 3.0; int minPoints[NTrackTypes] = {4, 10}; int minDetAcc[NTrackTypes] = {1, 1}; - float minScatteringAngleToAccount = 0.0001; + float minScatteringAngleToAccount = 0.0003; int verbose = 0; @@ -50,6 +52,32 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { int minDetectors = 1; // min number of detectors per track int minITSClusters = 4; // min ITS clusters to accept the track int minTRDTracklets = 3; // min TRD tracklets to accept the track + int minTPCClusters = 10; // discard tracks with less clusters + int minTOFClusters = 1; // min TOF clusters to accept track + int maxTPCRowsCombined = 1; // allow combining clusters on so many rows to a single cluster + int discardEdgePadrows = 3; // discard padrow if its distance to stack edge padrow < this + float discardSectorEdgeDepth = 2.5; // discard clusters too close to the sector edge + float ITSOverlapMargin = 0.15; // consider for overlaps only clusters within this marging from the chip edge (in cm) + float ITSOverlapMaxChi2 = 16; // max chi2 between track and overlapping cluster + int ITSOverlapEdgeRows = 1; // require clusters to not have pixels closer than this distance from the edge + float ITSOverlapMaxDZ = 0.3; // max difference in Z for clusters on overlapping ITS chips to consider as candidate for a double hit + + int minPointTotalCosm = 4; // total min number of alignment point to account cosmic track + int minDetectorsCosm = 1; // min number of detectors per cosmic track + int minITSClustersCosm = 0; // min ITS clusters to accept the cosmic track + int minITSClustersCosmLeg = 2; // min ITS clusters per leg to accept the cosmic track + int minTRDTrackletsCosm = 0; // min TRD tracklets to accept the cosmic track + int minTRDTrackletsCosmLeg = 2; // min TRD tracklets per leg to accept the cosmic track + int minTPCClustersCosm = 0; // discard cosmic tracks with less clusters + int minTPCClustersCosmLeg = 10; // discard cosmic tracks with less clusters per leg + int minTOFClustersCosm = 0; // min TOF clusters to accept track + int minTOFClustersCosmLeg = 1; // min TOF clusters per leg to accept track + + int minTPCPadRow = 6; // min TPC pad-row to account + int maxTPCPadRow = 146; // max TPC pad-row to account + + float cosmMaxDSnp = 0.025; // reject cosmic tracks with larger than this snp difference + float cosmMaxDTgl = 0.1; // reject cosmic tracks with larger than this tgl difference float maxDCAforVC[2] = {-1, -1}; // DCA cut in R,Z to allow track be subjected to vertex constraint float maxChi2forVC = -1; // track-vertex chi2 cut to allow the track be subjected to vertex constraint @@ -57,6 +85,7 @@ struct AlignConfig : public o2::conf::ConfigurableParamHelper { float controlFraction = -1.; // fraction for which control output is requested, if negative - only 1st instance of device will write them float MPRecOutFraction = -1.; // compact Millepede2Record fraction, if negative - only 1st instance of device will write them + bool useLinRef = true; // use initial track for lienarization reference point bool MilleOut = true; // Mille output bool KalmanResid = true; // Kalman residuals bool MilleOutBin = true; // text vs binary output for mille data diff --git a/Detectors/Align/include/Align/AlignableDetector.h b/Detectors/Align/include/Align/AlignableDetector.h index 5d671a2b5dbf1..97963c006aa46 100644 --- a/Detectors/Align/include/Align/AlignableDetector.h +++ b/Detectors/Align/include/Align/AlignableDetector.h @@ -115,7 +115,7 @@ class AlignableDetector : public DOFSet void Print(const Option_t* opt = "") const override; virtual void reset(); - virtual int processPoints(GIndex gid, bool inv = false); + virtual int processPoints(GIndex gid, int npntCut = 0, bool inv = false); virtual bool prepareDetectorData() { return true; } virtual void updatePointByTrackInfo(AlignmentPoint* pnt, const trackParam_t* t) const; @@ -209,8 +209,7 @@ class AlignableDetector : public DOFSet TObjArray mVolumes; // all volumes of the detector // // this is transient info - int mNPoints = 0; //! number of points from this detector - int mFirstPoint = 0; //! entry of the 1st point + int mNPoints = 0; //! number of points from this detector // ClassDefOverride(AlignableDetector, 1); // base class for detector global alignment }; diff --git a/Detectors/Align/include/Align/AlignableDetectorITS.h b/Detectors/Align/include/Align/AlignableDetectorITS.h index dcf23f4eca181..5f1816d8368b8 100644 --- a/Detectors/Align/include/Align/AlignableDetectorITS.h +++ b/Detectors/Align/include/Align/AlignableDetectorITS.h @@ -21,6 +21,7 @@ #include "Align/utils.h" #include "ReconstructionDataFormats/TrackParametrizationWithError.h" #include "ReconstructionDataFormats/BaseCluster.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" namespace o2 { @@ -39,6 +40,11 @@ class AlignableDetectorITS : public AlignableDetector public: // using ClusterD = o2::BaseCluster; + using OVL = o2::itsmft::ChipMappingITS::Overlaps; + enum EdgeFlags : int8_t { NONE = -1, + LowRow = OVL::LowRow, + HighRow = OVL::HighRow, + Biased = 2 }; AlignableDetectorITS() = default; // RS FIXME do we need default c-tor? AlignableDetectorITS(Controller* ctr); ~AlignableDetectorITS() override = default; @@ -49,7 +55,7 @@ class AlignableDetectorITS : public AlignableDetector // RSTODO // bool AcceptTrack(const AliESDtrack* trc, int trtype) const; - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; bool prepareDetectorData() final; void SetAddErrorLr(int ilr, double sigY, double sigZ); @@ -71,6 +77,9 @@ class AlignableDetectorITS : public AlignableDetector protected: // std::vector mITSClustersArray; + std::vector mOverlapCandidateID; // pool of indices for potentially overlapping clusters + std::vector mOverlapClusRef; // 1st entry in mOverlapCandidateID for the overlapping cluster indices of each cluster + std::vector mOverlaps; const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; // cluster patterns dictionary // ClassDefOverride(AlignableDetectorITS, 1); diff --git a/Detectors/Align/include/Align/AlignableDetectorTOF.h b/Detectors/Align/include/Align/AlignableDetectorTOF.h index 7c0736c639bdc..b5434bcf6ad3c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTOF.h +++ b/Detectors/Align/include/Align/AlignableDetectorTOF.h @@ -24,7 +24,7 @@ namespace o2 namespace align { -class AlignableDetectorTOF : public AlignableDetector +class AlignableDetectorTOF final : public AlignableDetector { public: AlignableDetectorTOF() = default; @@ -33,7 +33,7 @@ class AlignableDetectorTOF : public AlignableDetector // void defineVolumes() final; // - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; // protected: // diff --git a/Detectors/Align/include/Align/AlignableDetectorTPC.h b/Detectors/Align/include/Align/AlignableDetectorTPC.h index d513f983d44e4..af85e18bf2de1 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTPC.h +++ b/Detectors/Align/include/Align/AlignableDetectorTPC.h @@ -10,8 +10,7 @@ // or submit itself to any jurisdiction. /// @file AlignableDetectorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 +/// @author ruben.shahoyan@cern.ch /// @brief TPC detector wrapper #ifndef ALIGNABLEDETECTORTPC_H @@ -24,23 +23,47 @@ namespace o2 namespace align { -class AlignableDetectorTPC : public AlignableDetector +class AlignableDetectorTPC final : public AlignableDetector { public: - AlignableDetectorTPC(const char* title = ""); - virtual ~AlignableDetectorTPC(); // - virtual void defineVolumes(); - // - bool AcceptTrack(const AliESDtrack* trc, int trtype) const; + AlignableDetectorTPC() = default; + AlignableDetectorTPC(Controller* ctr); + ~AlignableDetectorTPC() final = default; + void defineVolumes() final; + void Print(const Option_t* opt = "") const final; // + int processPoints(GIndex gid, int npntCut, bool inv) final; + + void setTrackTimeStamp(float t) { mTrackTimeStamp = t; } + float getTrackTimeStamp() const { return mTrackTimeStamp; } + + int getStack(int padrow) const + { + for (int i = 0; i < 4; i++) { + if (padrow <= mStackMinMaxRow[i].second) { + return i; + } + } + return -1; + } + + int getDistanceToStackEdge(int padrow) const + { + // distance to the stack min or max padrow + auto st = getStack(padrow); + if (st < 0) { + return -999; + } + return std::min(padrow - mStackMinMaxRow[st].first, mStackMinMaxRow[st].second - padrow); + } + protected: // - // -------- dummies -------- - AlignableDetectorTPC(const AlignableDetectorTPC&); - AlignableDetectorTPC& operator=(const AlignableDetectorTPC&); - // - protected: + float mTrackTimeStamp = 0.f; // use track timestamp in \mus + static constexpr int NSTACKS = 4; + const std::array, NSTACKS> mStackMinMaxRow = {std::pair{0, 62}, std::pair{63, 96}, std::pair{97, 126}, std::pair{127, 151}}; + ClassDef(AlignableDetectorTPC, 1); }; } // namespace align diff --git a/Detectors/Align/include/Align/AlignableDetectorTRD.h b/Detectors/Align/include/Align/AlignableDetectorTRD.h index 7d7a59351c767..4e7577b11055c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTRD.h +++ b/Detectors/Align/include/Align/AlignableDetectorTRD.h @@ -18,14 +18,14 @@ #define ALIGNABLEDETECTORTRD_H #include "Align/AlignableDetector.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" namespace o2 { namespace align { -class AlignableDetectorTRD : public AlignableDetector +class AlignableDetectorTRD final : public AlignableDetector { public: // @@ -61,10 +61,10 @@ class AlignableDetectorTRD : public AlignableDetector mExtraErrRC[1] = z; } - int processPoints(GIndex gid, bool inv) final; + int processPoints(GIndex gid, int npntCut, bool inv) final; protected: - o2::trd::RecoParam mRecoParam; // parameters required for TRD reconstruction + o2::gpu::GPUTRDRecoParam mRecoParam; // parameters required for TRD reconstruction double mNonRCCorrDzDtgl = 0.; // correction in Z for non-crossing tracklets double mCorrDVT = 0.; // correction to Vdrift*t double mExtraErrRC[2] = {0., 0.}; // extra errors for RC tracklets diff --git a/Detectors/Align/include/Align/AlignableSensorHMPID.h b/Detectors/Align/include/Align/AlignableSensorHMPID.h index 6064078d1b923..867b72152c73c 100644 --- a/Detectors/Align/include/Align/AlignableSensorHMPID.h +++ b/Detectors/Align/include/Align/AlignableSensorHMPID.h @@ -29,7 +29,7 @@ namespace o2 namespace align { -class AlignableSensorHMPID : public AlignableSensor +class AlignableSensorHMPID final : public AlignableSensor { public: AlignableSensorHMPID(const char* name = 0, int vid = 0, int iid = 0, int isec = 0); diff --git a/Detectors/Align/include/Align/AlignableSensorITS.h b/Detectors/Align/include/Align/AlignableSensorITS.h index de0e6d35629ec..7769eb6aa22f3 100644 --- a/Detectors/Align/include/Align/AlignableSensorITS.h +++ b/Detectors/Align/include/Align/AlignableSensorITS.h @@ -29,13 +29,15 @@ namespace o2 namespace align { -class AlignableSensorITS : public AlignableSensor +class AlignableSensorITS final : public AlignableSensor { public: AlignableSensorITS() = default; AlignableSensorITS(const char* name, int vid, int iid, Controller* ctr); ~AlignableSensorITS() final = default; void prepareMatrixT2L() final; + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; // protected: // diff --git a/Detectors/Align/include/Align/AlignableSensorTOF.h b/Detectors/Align/include/Align/AlignableSensorTOF.h index 745251d7dc6f2..d9e11dce98a0c 100644 --- a/Detectors/Align/include/Align/AlignableSensorTOF.h +++ b/Detectors/Align/include/Align/AlignableSensorTOF.h @@ -29,7 +29,7 @@ namespace o2 namespace align { -class AlignableSensorTOF : public AlignableSensor +class AlignableSensorTOF final : public AlignableSensor { public: AlignableSensorTOF() = default; diff --git a/Detectors/Align/include/Align/AlignableSensorTPC.h b/Detectors/Align/include/Align/AlignableSensorTPC.h index ee1b0caf5ff30..838e8c8272400 100644 --- a/Detectors/Align/include/Align/AlignableSensorTPC.h +++ b/Detectors/Align/include/Align/AlignableSensorTPC.h @@ -10,39 +10,35 @@ // or submit itself to any jurisdiction. /// @file AlignableSensorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 -/// @brief TPC sensor (chamber) +/// @author ruben.shahoyan@cern.ch +/// @brief TPC fake sensor (sector) #ifndef ALIGNABLESENSORTPC_H #define ALIGNABLESENSORTPC_H #include "Align/AlignableSensor.h" - -class TObjArray; -//class AliTrackPointArray; -//class AliESDtrack; class AlignmentPoint; +class TObjArray; namespace o2 { namespace align { -class AlignableSensorTPC : public AlignableSensor +class AlignableSensorTPC final : public AlignableSensor { public: - AlignableSensorTPC(const char* name = 0, int vid = 0, int iid = 0, int isec = 0); - ~AlignableSensorTPC() = final; - // - int GetSector() const { return fSector; } - void SetSector(uint32_t sc) { fSector = (uint8_t)sc; } - // + AlignableSensorTPC() = default; + AlignableSensorTPC(const char* name, int vid, int iid, int isec, Controller* ctr); + ~AlignableSensorTPC() final = default; + int getSector() const { return mSector; } + void setSector(int sc) { mSector = (uint8_t)sc; } void prepareMatrixT2L() final; - // + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; + protected: - // - uint8_t fSector; // sector ID + uint8_t mSector = 0; // sector ID ClassDef(AlignableSensorTPC, 1) }; diff --git a/Detectors/Align/include/Align/AlignableSensorTRD.h b/Detectors/Align/include/Align/AlignableSensorTRD.h index 65792d597cb3a..eabc3905e9e13 100644 --- a/Detectors/Align/include/Align/AlignableSensorTRD.h +++ b/Detectors/Align/include/Align/AlignableSensorTRD.h @@ -26,7 +26,7 @@ namespace o2 namespace align { -class AlignableSensorTRD : public AlignableSensor +class AlignableSensorTRD final : public AlignableSensor { public: AlignableSensorTRD() = default; @@ -35,7 +35,12 @@ class AlignableSensorTRD : public AlignableSensor int getSector() const { return mSector; } void setSector(int sc) { mSector = (uint8_t)sc; } void dPosTraDParCalib(const AlignmentPoint* pnt, double* deriv, int calibID, const AlignableVolume* parent = nullptr) const final; + + void prepareMatrixL2G(bool reco = false) final; + void prepareMatrixL2GIdeal() final; void prepareMatrixT2L() final; + void prepareMatrixClAlg() final; + void prepareMatrixClAlgReco() final; protected: uint8_t mSector = 0; // sector ID diff --git a/Detectors/Align/include/Align/AlignableVolume.h b/Detectors/Align/include/Align/AlignableVolume.h index 7221f3a3cb587..15b077eb2700f 100644 --- a/Detectors/Align/include/Align/AlignableVolume.h +++ b/Detectors/Align/include/Align/AlignableVolume.h @@ -182,8 +182,8 @@ class AlignableVolume : public DOFSet bool createLocDeltaMatrix(TGeoHMatrix& deltaM) const; // return true if the matrix is not unity void createPreGloDeltaMatrix(TGeoHMatrix& deltaM) const; void createPreLocDeltaMatrix(TGeoHMatrix& deltaM) const; - void createAlignmenMatrix(TGeoHMatrix& alg) const; - void createAlignmentObjects(std::vector& arr) const; + void createAlignmenMatrix(TGeoHMatrix& alg, const TGeoHMatrix* envelopeDelta = nullptr) const; + void createAlignmentObjects(std::vector& arr, const TGeoHMatrix* envelopeDelta = nullptr) const; // void setSkip(bool v = true) { SetBit(kSkipBit, v); } bool getSkip() const { return TestBit(kSkipBit); } @@ -197,6 +197,9 @@ class AlignableVolume : public DOFSet bool ownsDOFID(int id) const; AlignableVolume* getVolOfDOFID(int id) const; // + bool isDummyEnvelope() const { return mIsDummyEnvelope; } + void setDummyEnvelope(bool v = true) { mIsDummyEnvelope = v; } + // bool isDummy() const { return mIsDummy; } void setDummy(bool v) { mIsDummy = v; } // @@ -228,6 +231,8 @@ class AlignableVolume : public DOFSet uint32_t mDOF = 0; // pattern of DOFs uint32_t mDOFAsMeas = 0; // consider DOF as measured with presigma error bool mIsDummy = false; // placeholder (e.g. inactive), used to have the numbering corresponding to position in the container + bool mIsDummyEnvelope = false; // some volumes are dummy envelopes for their children + // char mNDOFGeomFree = 0; // number of free geom degrees of freedom uint8_t mConstrChild = 0; // bitpattern for constraints on children corrections // diff --git a/Detectors/Align/include/Align/AlignmentTrack.h b/Detectors/Align/include/Align/AlignmentTrack.h index d4d5fbde5c7c1..cb69f11cbf85c 100644 --- a/Detectors/Align/include/Align/AlignmentTrack.h +++ b/Detectors/Align/include/Align/AlignmentTrack.h @@ -24,6 +24,7 @@ #define ALIGNMENTTRACK_H #include "Align/AlignmentPoint.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/Track.h" #include #include @@ -38,8 +39,10 @@ class AlignmentTrack : public trackParam_t, public TObject { public: using trackParam_t = o2::track::TrackParametrizationWithError; + using trackPar_t = o2::track::TrackParametrization; using PropagatorD = o2::base::PropagatorD; using MatCorrType = PropagatorD::MatCorrType; + using GTrackID = o2::dataformats::GlobalTrackID; using trackParam_t::setParam; static constexpr double MaxDefStep = 3.0; static constexpr double MaxDefSnp = 0.95; @@ -59,6 +62,8 @@ class AlignmentTrack : public trackParam_t, public TObject }; AlignmentTrack() = default; ~AlignmentTrack() override = default; + void setCurrentTrackID(GTrackID id) { mCurrenTrackID = id; } + GTrackID getCurrentTrackID() const { return mCurrenTrackID; } void defineDOFs(); int getNPoints() const { return mPoints.size(); } int getInnerPointID() const { return mInnerPointID; } @@ -79,9 +84,9 @@ class AlignmentTrack : public trackParam_t, public TObject // template void copyFrom(const o2::track::TrackParametrizationWithError

& trc); - bool propagateToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tLT = nullptr, int signCorr = 0); - bool propagateParamToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // param only - bool propagateParamToPoint(trackParam_t* trSet, int nTr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // params only + bool propagateToPoint(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tLT = nullptr, int signCorr = 0); + bool propagateParamToPoint(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // param only + bool propagateParamToPoint(trackPar_t* trSet, int nTr, const AlignmentPoint* pnt, double maxStep = 3, double maxSnp = 0.95, MatCorrType mt = MatCorrType::USEMatCorrLUT, int signCorr = 0); // params only // bool calcResiduals(const double* params = nullptr); bool calcResidDeriv(double* params = nullptr); @@ -115,23 +120,23 @@ class AlignmentTrack : public trackParam_t, public TObject void imposePtBOff(double pt) { setQ2Pt(1. / pt); } // propagation methods void copyFrom(const trackParam_t* etp); - bool applyMatCorr(trackParam_t& trPar, const double* corrDiag, const AlignmentPoint* pnt); - bool applyMatCorr(trackParam_t* trSet, int ntr, const double* corrDiaf, const AlignmentPoint* pnt); - bool applyMatCorr(trackParam_t& trPar, const double* corrpar); + bool applyMatCorr(trackPar_t& trPar, const double* corrDiag, const AlignmentPoint* pnt); + bool applyMatCorr(trackPar_t* trSet, int ntr, const double* corrDiaf, const AlignmentPoint* pnt); + bool applyMatCorr(trackPar_t& trPar, const double* corrpar); // double getResidual(int dim, int pntID) const { return mResid[dim][pntID]; } const double* getDResDLoc(int dim, int pntID) const { return mDResDLoc[dim].data() + (pntID * mNLocPar); } const double* getDResDGlo(int dim, int id) const { return mDResDGlo[dim].data() + id; } const int* getGloParID() const { return mGloParID.data(); } // - void setParams(trackParam_t& tr, double x, double alp, const double* par, bool add); - void setParams(trackParam_t* trSet, int ntr, double x, double alp, const double* par, bool add); - void setParam(trackParam_t& tr, int par, double val); - void setParam(trackParam_t* trSet, int ntr, int par, double val); - void modParam(trackParam_t& tr, int par, double delta); - void modParam(trackParam_t* trSet, int ntr, int par, double delta); + void setParams(trackPar_t& tr, double x, double alp, const double* par, bool add); + void setParams(trackPar_t* trSet, int ntr, double x, double alp, const double* par, bool add); + void setParam(trackPar_t& tr, int par, double val); + void setParam(trackPar_t* trSet, int ntr, int par, double val); + void modParam(trackPar_t& tr, int par, double delta); + void modParam(trackPar_t* trSet, int ntr, int par, double delta); // - void richardsonDeriv(const trackParam_t* trSet, const double* delta, + void richardsonDeriv(const trackPar_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ); // const double* getLocPars() const { return mLocPar.data(); } @@ -166,6 +171,7 @@ class AlignmentTrack : public trackParam_t, public TObject double mChi2CosmUp = 0; // chi2 for cosmic upper leg double mChi2CosmDn = 0; // chi2 for cosmic down leg double mChi2Ini = 0; // chi2 with current residuals + GTrackID mCurrenTrackID = {}; // currently processed track ID std::vector mPoints{}; // alignment points pointers sorted in X std::vector mDetPoints{}; // alignment points added by detectors std::vector mResid[2]; // residuals array @@ -174,13 +180,14 @@ class AlignmentTrack : public trackParam_t, public TObject std::vector mLocPar; // local parameters array std::vector mGloParID; // IDs of relevant global params private: - bool propagate(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); + bool propagate(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); + bool propagate(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr = 0); // ClassDefOverride(AlignmentTrack, 2) }; //____________________________________________________________________________________________ -inline void AlignmentTrack::setParams(trackParam_t& tr, double x, double alp, const double* par, bool add) +inline void AlignmentTrack::setParams(trackPar_t& tr, double x, double alp, const double* par, bool add) { // set track params const double kDefQ2PtCosm = 1; @@ -200,7 +207,7 @@ inline void AlignmentTrack::setParams(trackParam_t& tr, double x, double alp, co } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParams(trackParam_t* trSet, int ntr, double x, double alp, const double* par, bool add) +inline void AlignmentTrack::setParams(trackPar_t* trSet, int ntr, double x, double alp, const double* par, bool add) { // set parames for multiple tracks (VECTORIZE THIS) if (!add) { // full parameter supplied @@ -219,14 +226,14 @@ inline void AlignmentTrack::setParams(trackParam_t* trSet, int ntr, double x, do } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParam(trackParam_t& tr, int par, double val) +inline void AlignmentTrack::setParam(trackPar_t& tr, int par, double val) { // set track parameter tr.setParam(val, par); } //____________________________________________________________________________________________ -inline void AlignmentTrack::setParam(trackParam_t* trSet, int ntr, int par, double val) +inline void AlignmentTrack::setParam(trackPar_t* trSet, int ntr, int par, double val) { // set parames for multiple tracks (VECTORIZE THIS) for (int i = 0; i < ntr; ++i) { @@ -235,7 +242,7 @@ inline void AlignmentTrack::setParam(trackParam_t* trSet, int ntr, int par, doub } //____________________________________________________________________________________________ -inline void AlignmentTrack::modParam(trackParam_t& tr, int par, double delta) +inline void AlignmentTrack::modParam(trackPar_t& tr, int par, double delta) { // modify track parameter const auto val = tr.getParam(par) + delta; @@ -243,7 +250,7 @@ inline void AlignmentTrack::modParam(trackParam_t& tr, int par, double delta) } //____________________________________________________________________________________________ -inline void AlignmentTrack::modParam(trackParam_t* trSet, int ntr, int par, double delta) +inline void AlignmentTrack::modParam(trackPar_t* trSet, int ntr, int par, double delta) { // modify track parameter (VECTORIZE THOS) for (int i = 0; i < ntr; ++i) { diff --git a/Detectors/Align/include/Align/Controller.h b/Detectors/Align/include/Align/Controller.h index e7fc38329a62c..90abf2025d1c3 100644 --- a/Detectors/Align/include/Align/Controller.h +++ b/Detectors/Align/include/Align/Controller.h @@ -26,8 +26,11 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" #include "Align/AlignmentTrack.h" +#include "Align/AlgTrcDbg.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/TrackCosmics.h" +#include "DataFormatsTPC/VDriftCorrFact.h" +#include "CorrectionMapsHelper.h" #include "Align/Millepede2Record.h" #include "Align/ResidualsController.h" @@ -51,6 +54,8 @@ #include #include #include "Align/Mille.h" +// #include "GPUO2ExternalUser.h" +// #include "DataFormatsTPC/WorkflowHelper.h" namespace o2 { @@ -67,6 +72,11 @@ namespace utils class TreeStreamRedirector; } +namespace gpu +{ +class GPUParam; +} + namespace align { @@ -77,7 +87,7 @@ class AlignableVolume; class AlignmentPoint; class ResidualsControllerFast; -class Controller : public TObject +class Controller final : public TObject { public: struct ProcStat { @@ -214,7 +224,8 @@ class Controller : public TObject void initMPRecOutput(); void initMIlleOutput(); void initResidOutput(); - bool storeProcessedTrack(o2::dataformats::GlobalTrackID tid); + bool storeProcessedTrack(o2::dataformats::GlobalTrackID tid = {}); + void extractDbgTrack(); void printStatistics() const; // void genPedeSteerFile(const Option_t* opt = "") const; @@ -260,12 +271,21 @@ class Controller : public TObject bool getTRDTrigRecFilterActive() const { return mTRDTrigRecFilterActive; } bool getAllowAfterburnerTracks() const { return mAllowAfterburnerTracks; } + void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); + void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph); + o2::gpu::CorrectionMapsHelper* getTPCCorrMaps() { return mTPCCorrMapsHelper; } + const o2::tpc::VDriftCorrFact& getTPCVDrift() const { return mTPCDrift; } + int getInstanceID() const { return mInstanceID; } void setInstanceID(int i) { mInstanceID = i; } int getDebugOutputLevel() const { return mDebugOutputLevel; } void setDebugOutputLevel(int i) { mDebugOutputLevel = i; } void setDebugStream(o2::utils::TreeStreamRedirector* d) { mDBGOut = d; } + o2::utils::TreeStreamRedirector* getDebugStream() { return mDBGOut; } + + void setTPCParam(const o2::gpu::GPUParam* par) { mTPCParam = par; } + const o2::gpu::GPUParam* getTPCParam() const { return mTPCParam; } protected: // @@ -289,10 +309,14 @@ class Controller : public TObject float mMPRecOutFraction = 0.; float mControlFraction = 0.; std::unique_ptr mAlgTrack; // current alignment track + AlgTrcDbg mAlgTrackDbg; // current alignment track debug version const o2::globaltracking::RecoContainer* mRecoData = nullptr; // externally set RecoContainer const o2::trd::TrackletTransformer* mTRDTransformer = nullptr; // TRD tracket transformer bool mTRDTrigRecFilterActive = false; // select TRD triggers processed with ITS bool mAllowAfterburnerTracks = false; // allow using ITS-TPC afterburner tracks + + const o2::gpu::GPUParam* mTPCParam = nullptr; + std::array, DetID::nDetectors> mDetectors{}; // detectors participating in the alignment std::unique_ptr mVtxSens; // fake sensor for the vertex @@ -333,6 +357,10 @@ class Controller : public TObject int mRefRunNumber = 0; // optional run number used for reference int mRefOCDBLoaded = 0; // flag/counter for ref.OCDB loading bool mUseRecoOCDB = true; // flag to preload reco-time calib objects + + o2::tpc::VDriftCorrFact mTPCDrift{}; + o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; + // static const int sSkipLayers[kNLrSkip]; // detector layers for which we don't need module matrices static const Char_t* sDetectorName[kNDetectors]; // names of detectors //RSREM diff --git a/Detectors/Align/include/Align/ResidualsControllerFast.h b/Detectors/Align/include/Align/ResidualsControllerFast.h index 171bfb45757d2..5a2f947db9cca 100644 --- a/Detectors/Align/include/Align/ResidualsControllerFast.h +++ b/Detectors/Align/include/Align/ResidualsControllerFast.h @@ -24,7 +24,7 @@ namespace o2 namespace align { -class ResidualsControllerFast : public TObject +class ResidualsControllerFast final : public TObject { public: enum { kCosmicBit = BIT(14), diff --git a/Detectors/Align/macro/CMakeLists.txt b/Detectors/Align/macro/CMakeLists.txt index 49106e647c412..54c8715b074b2 100644 --- a/Detectors/Align/macro/CMakeLists.txt +++ b/Detectors/Align/macro/CMakeLists.txt @@ -10,6 +10,7 @@ # or submit itself to any jurisdiction. install(FILES algconf.C + algDump.C MPRec2Mille.C DESTINATION share/Detectors/Align/macro) @@ -17,6 +18,10 @@ o2_add_test_root_macro(algconf.C PUBLIC_LINK_LIBRARIES O2::Align LABELS align COMPILE_ONLY) +o2_add_test_root_macro(algDump.C + PUBLIC_LINK_LIBRARIES O2::Align + LABELS align COMPILE_ONLY) + o2_add_test_root_macro(MPRec2Mille.C PUBLIC_LINK_LIBRARIES O2::Align LABELS align COMPILE_ONLY) diff --git a/Detectors/Align/macro/algDump.C b/Detectors/Align/macro/algDump.C new file mode 100644 index 0000000000000..2d1dfcbaaa0de --- /dev/null +++ b/Detectors/Align/macro/algDump.C @@ -0,0 +1,93 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/GeometryManager.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsTRD/Constants.h" +#include "TRDBase/Geometry.h" +#include "TOFBase/Geo.h" +#include +#endif + +using DetID = o2::detectors::DetID; + +void algDump(const std::string& geom = "", const std::string& outname = "algdump.root") +{ + o2::base::GeometryManager::loadGeometry(geom.c_str()); + o2::utils::TreeStreamRedirector outstream(outname.c_str(), "recreate"); + TGeoHMatrix* matAlg = nullptr; + TGeoHMatrix matOrig; + TVector3 pos0, pos; + DetID det; + + auto procSens = [](TGeoHMatrix& mat) { + double loc[3] = {0., 0., 0.}, glo[3]; + mat.LocalToMaster(loc, glo); + return TVector3(glo[0], glo[1], glo[2]); + }; + + auto store = [&outstream, &det, &pos, &pos0](int lr, int sid, int sidLr) { + outstream << "gm" + << "det=" << det.getID() << "lr=" << lr << "sid=" << sid << "sidlr=" << sidLr << "pos0=" << pos0 << "pos=" << pos << "\n"; + printf("xx %d %d %d %f %f %f\n", det.getID(), lr, sid, pos0[0], pos0[1], pos0[1]); + }; + + det = DetID("ITS"); + o2::itsmft::ChipMappingITS mpits; + for (int ic = 0; ic < mpits.getNChips(); ic++) { + int lr = mpits.getLayer(ic); + int ic0 = ic - mpits.getFirstChipsOnLayer(lr); + matAlg = o2::base::GeometryManager::getMatrix(det, ic); + o2::base::GeometryManager::getOriginalMatrix(det, ic, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(lr, ic, ic0); + } + + det = DetID("TRD"); + for (int ilr = 0; ilr < o2::trd::constants::NLAYER; ilr++) { // layer + for (int ich = 0; ich < o2::trd::constants::NSTACK * o2::trd::constants::NSECTOR; ich++) { // chamber + int isector = ich / o2::trd::constants::NSTACK; + int istack = ich % o2::trd::constants::NSTACK; + uint16_t sid = o2::trd::Geometry::getDetector(ilr, istack, isector); + const char* symname = Form("TRD/sm%02d/st%d/pl%d", isector, istack, ilr); + if (!gGeoManager->GetAlignableEntry(symname)) { + continue; + } + matAlg = o2::base::GeometryManager::getMatrix(det, sid); + o2::base::GeometryManager::getOriginalMatrix(det, sid, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(ilr, sid, ich); + } + } + + det = DetID("TOF"); + int cnt = -1; + for (int isc = 0; isc < 18; isc++) { + for (int istr = 1; istr <= o2::tof::Geo::NSTRIPXSECTOR; istr++) { // strip + const char* symname = Form("TOF/sm%02d/strip%02d", isc, istr); + cnt++; + if (!gGeoManager->GetAlignableEntry(symname)) { + continue; + } + matAlg = o2::base::GeometryManager::getMatrix(det, cnt); + o2::base::GeometryManager::getOriginalMatrix(det, cnt, matOrig); + pos0 = procSens(matOrig); + pos = procSens(*matAlg); + store(0, cnt, cnt); + } + } + outstream.Close(); +} diff --git a/Detectors/Align/macro/algconf.C b/Detectors/Align/macro/algconf.C index 2e87a6c07edb3..190c8739eaa34 100644 --- a/Detectors/Align/macro/algconf.C +++ b/Detectors/Align/macro/algconf.C @@ -13,6 +13,7 @@ #include "Align/Controller.h" #include "Align/AlignableVolume.h" #include "Align/AlignableDetectorITS.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/AlignableDetectorTRD.h" #include "Align/AlignableDetectorTOF.h" #endif @@ -21,6 +22,7 @@ using namespace o2::align; void configITS(Controller* c, int par); +void configTPC(Controller* c, int par); void configTRD(Controller* c, int par); void configTOF(Controller* c, int par); @@ -31,6 +33,9 @@ int algconf(Controller* c, int par) if (c->getDetector(o2::detectors::DetID::ITS)) { configITS(c, par); } + if (c->getDetector(o2::detectors::DetID::TPC)) { + configTPC(c, par); + } if (c->getDetector(o2::detectors::DetID::TRD)) { configTRD(c, par); } @@ -102,6 +107,25 @@ void configITS(Controller* c, int par) */ } +void configTPC(Controller* c, int par) +{ + const double kCondSig[AlignableVolume::kNDOFGeom] = {1., 1., 1., 1., 1., 1.}; // precondition sigmas + AlignableDetectorTPC* tpc = (AlignableDetectorTPC*)c->getDetector(o2::detectors::DetID::TPC); + if (!tpc) { + LOG(info) << "No TPC"; + return; + } + + tpc->setFreeDOFPattern(0, -1, "TPC/sec[0-9]+$"); // Fix sectors + auto tpcvol = tpc->getVolume("TPC_envelope"); // envelope + if (!tpcvol || !tpcvol->isDummyEnvelope()) { + LOG(fatal) << "Could not find TPC_envelope volume"; + } + for (int idf = AlignableVolume::kNDOFGeom; idf--;) { + tpcvol->setParErr(idf, kCondSig[idf]); // set condition + } +} + void configTRD(Controller* c, int par) { const double kCondSig[AlignableVolume::kNDOFGeom] = {0.2, 0.2, 0.3, 1., 1., 1.}; // precondition sigmas diff --git a/Detectors/Align/src/AlgPntDbg.cxx b/Detectors/Align/src/AlgPntDbg.cxx new file mode 100644 index 0000000000000..b52492b3a5cc4 --- /dev/null +++ b/Detectors/Align/src/AlgPntDbg.cxx @@ -0,0 +1,22 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgPntDbg.h + +#include "Align/AlgPntDbg.h" + +namespace o2 +{ +namespace align +{ + +} // namespace align +} // namespace o2 diff --git a/Detectors/Align/src/AlgTrcDbg.cxx b/Detectors/Align/src/AlgTrcDbg.cxx new file mode 100644 index 0000000000000..157ca941449b7 --- /dev/null +++ b/Detectors/Align/src/AlgTrcDbg.cxx @@ -0,0 +1,23 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file AlgTrcDbg.h + +#include "Align/AlgTrcDbg.h" +#include "Framework/Logger.h" + +namespace o2 +{ +namespace align +{ + +} // namespace align +} // namespace o2 diff --git a/Detectors/Align/src/AlignLinkDef.h b/Detectors/Align/src/AlignLinkDef.h index 05c73c14520ca..66f3220d720ca 100644 --- a/Detectors/Align/src/AlignLinkDef.h +++ b/Detectors/Align/src/AlignLinkDef.h @@ -8,7 +8,6 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - #ifdef __CLING__ #pragma link off all globals; @@ -19,17 +18,17 @@ #pragma link C++ class o2::align::AlignableDetector + ; #pragma link C++ class o2::align::AlignableDetectorITS + ; #pragma link C++ class o2::align::AlignableDetectorTOF + ; -//#pragma link C++ class o2::align::AlignableDetectorTPC + ; +#pragma link C++ class o2::align::AlignableDetectorTPC + ; #pragma link C++ class o2::align::AlignableDetectorTRD + ; -//#pragma link C++ class o2::align::AlignableDetectorHMPID + ; +// #pragma link C++ class o2::align::AlignableDetectorHMPID + ; #pragma link C++ class o2::align::Millepede2Record + ; #pragma link C++ class o2::align::AlignmentPoint + ; #pragma link C++ class o2::align::AlignableSensor + ; #pragma link C++ class o2::align::AlignableSensorITS + ; #pragma link C++ class o2::align::AlignableSensorTOF + ; -//#pragma link C++ class o2::align::AlignableSensorTPC + ; +#pragma link C++ class o2::align::AlignableSensorTPC + ; #pragma link C++ class o2::align::AlignableSensorTRD + ; -//#pragma link C++ class o2::align::AlignableSensorHMPID + ; +// #pragma link C++ class o2::align::AlignableSensorHMPID + ; #pragma link C++ class o2::align::Controller + ; #pragma link C++ class o2::align::AlignmentTrack + ; #pragma link C++ class o2::align::AlignableVolume + ; @@ -39,9 +38,16 @@ #pragma link C++ class o2::align::GeometricalConstraint + ; #pragma link C++ class o2::align::utils; +#pragma link C++ class o2::align::AlgPntDbg + ; +#pragma link C++ class o2::align::AlgTrcDbg + ; +#pragma link C++ class std::vector < o2::align::AlgPntDbg> + ; + #pragma link C++ class o2::align::AlignConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::align::AlignConfig> + ; #pragma link C++ class std::vector < o2::track::TrackParametrization < double>> + ; +#include "TVector3.h" +#pragma link C++ class std::vector < TVector3> + ; + #endif diff --git a/Detectors/Align/src/AlignableDetector.cxx b/Detectors/Align/src/AlignableDetector.cxx index ae1458db5a521..68e602c85251d 100644 --- a/Detectors/Align/src/AlignableDetector.cxx +++ b/Detectors/Align/src/AlignableDetector.cxx @@ -55,7 +55,7 @@ AlignableDetector::~AlignableDetector() } //____________________________________________ -int AlignableDetector::processPoints(GIndex gid, bool inv) +int AlignableDetector::processPoints(GIndex gid, int npntCut, bool inv) { // Create alignment points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -116,7 +116,7 @@ void AlignableDetector::updateL2GRecoMatrices() void AlignableDetector::reset() { // prepare for the next track processing - mNPoints = mFirstPoint = 0; + mNPoints = 0; } //_________________________________________________________ @@ -177,7 +177,7 @@ void AlignableDetector::defineMatrices() TIter next(&mVolumes); AlignableVolume* vol(nullptr); while ((vol = (AlignableVolume*)next())) { - if (vol->isDummy()) { + if (vol->isDummy() || vol->isDummyEnvelope()) { continue; } vol->prepareMatrixL2G(); // modified global-local matrix diff --git a/Detectors/Align/src/AlignableDetectorITS.cxx b/Detectors/Align/src/AlignableDetectorITS.cxx index 9d56d87e7da1e..7387ae4620bf3 100644 --- a/Detectors/Align/src/AlignableDetectorITS.cxx +++ b/Detectors/Align/src/AlignableDetectorITS.cxx @@ -40,6 +40,8 @@ namespace align AlignableDetectorITS::AlignableDetectorITS(Controller* ctr) : AlignableDetector(DetID::ITS, ctr) { // default c-tor + o2::itsmft::ChipMappingITS mp; + mOverlaps = mp.getOverlapsInfo(); } /* @@ -110,19 +112,18 @@ void AlignableDetectorITS::defineVolumes() } //____________________________________________ -int AlignableDetectorITS::processPoints(GIndex gid, bool inv) +int AlignableDetectorITS::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. // If inv==true, the track propagates in direction of decreasing tracking X // (i.e. upper leg of cosmic track) // - mNPoints = 0; auto algTrack = mController->getAlgTrack(); auto recoData = mController->getRecoContainer(); const auto& algConf = AlignConfig::Instance(); - - auto procClus = [this, &algTrack](const ClusterD& clus) { + int npoints = 0; + auto procClus = [this, inv, &npoints, &algTrack](const ClusterD& clus) { auto* sensor = this->getSensor(clus.getSensorID()); auto& pnt = algTrack->addDetectorPoint(); const auto* sysE = sensor->getAddError(); // additional syst error @@ -137,24 +138,45 @@ int AlignableDetectorITS::processPoints(GIndex gid, bool inv) pnt.setDetID(this->mDetID); pnt.setSID(sensor->getSID()); pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; + npoints++; }; + std::array clusIDs{}; + int nOverlaps = 0; if (gid.getSource() == GIndex::ITS) { const auto tracks = recoData->getITSTracks(); if (tracks.empty()) { return -1; // source not loaded? } const auto& track = tracks[gid.getIndex()]; - if (track.getNClusters() < algConf.minITSClusters) { + if (track.getNClusters() < npntCut) { return -1; } const auto& clusIdx = recoData->getITSTracksClusterRefs(); // do we want to apply some cuts? int clEntry = track.getFirstClusterEntry(); - mFirstPoint = algTrack->getNPoints(); - for (int icl = track.getNumberOfClusters(); icl--;) { - const auto& clus = mITSClustersArray[clusIdx[clEntry++]]; + int preevSensID = -1; + bool errReported = false; + for (int icl = track.getNumberOfClusters(); icl--;) { // clusters refs are stored from outer to inner layers, we loop in inner -> outer direction + const auto& clus = mITSClustersArray[(clusIDs[npoints] = clusIdx[clEntry + icl])]; + if (clus.getSensorID() < preevSensID && !errReported) { // clusters are ordered from outer to inner layer, hence decreasing sensorID + std::string errstr{}; + for (int ie = track.getNumberOfClusters(); ie--;) { + errstr += fmt::format(" {}", mITSClustersArray[clusIdx[clEntry + ie]].getSensorID()); + } + LOGP(error, "wrong ITS clusters order? : chips {}", errstr); + errReported = true; + } + preevSensID = clus.getSensorID(); + if (clus.getBits()) { // overlapping clusters will have bit set + if (clus.isBitSet(EdgeFlags::Biased)) { + continue; + } + if (clus.getCount()) { + nOverlaps++; + } + } procClus(clus); } } else { // ITSAB @@ -163,42 +185,196 @@ int AlignableDetectorITS::processPoints(GIndex gid, bool inv) int nCl = trkITSABref.getNClusters(); int clEntry = trkITSABref.getFirstEntry(); for (int icl = 0; icl < nCl; icl++) { // clusters are stored from inner to outer layers - const auto& clus = mITSClustersArray[ABTrackClusIdx[clEntry + icl]]; + const auto& clus = mITSClustersArray[(clusIDs[npoints] = ABTrackClusIdx[clEntry + icl])]; + if (clus.getBits()) { // overlapping clusters will have bit set + if (clus.isBitSet(EdgeFlags::Biased)) { + continue; + } + if (clus.getCount()) { + nOverlaps++; + } + } procClus(clus); } } - return mNPoints; + if (npoints < npntCut) { // reset points to original start + algTrack->suppressLastPoints(npoints); + npoints = 0; + return 0; + } + + // do we need to process overlaps? + if (nOverlaps) { + auto trcProp = mController->getRecoContainer()->getTrackParam(mController->getAlgTrack()->getCurrentTrackID()); + trcProp.resetCovariance(10); + auto propagator = o2::base::Propagator::Instance(); + for (int icl = 0; icl < npoints; icl++) { + const auto& clus = mITSClustersArray[clusIDs[icl]]; + float alp = getSensor(clus.getSensorID())->getAlpTracking(); + if (!trcProp.rotate(alp) || + !propagator->propagateToX(trcProp, clus.getX(), propagator->getNominalBz(), algConf.maxSnp, algConf.maxStep, base::Propagator::MatCorrType(algConf.matCorType)) || + !trcProp.update(clus)) { + break; + } + + if (clus.getCount()) { // there is an overlap, find best matching cluster + nOverlaps--; + int bestClusID = -1, clusIDtoCheck = mOverlapClusRef[clusIDs[icl]]; + float bestChi2 = algConf.ITSOverlapMaxChi2; + auto trPropOvl = trcProp; + for (int iov = 0; iov < clus.getCount(); iov++) { + int clusOvlID = mOverlapCandidateID[clusIDtoCheck]; + const auto& clusOvl = mITSClustersArray[clusOvlID]; + if (iov == 0) { + if (!trPropOvl.rotate(getSensor(clusOvl.getSensorID())->getAlpTracking()) || + !propagator->propagateToX(trPropOvl, clusOvl.getX(), propagator->getNominalBz(), algConf.maxSnp, algConf.maxStep, base::Propagator::MatCorrType::USEMatCorrNONE)) { + break; + } + } + auto chi2 = trPropOvl.getPredictedChi2(clusOvl); + if (chi2 < bestChi2) { + bestChi2 = chi2; + bestClusID = clusOvlID; + } + } + if (bestClusID != -1) { // account overlapping cluster + procClus(mITSClustersArray[bestClusID]); + } + } + if (!nOverlaps) { + break; + } + } + } + mNPoints += npoints; + return npoints; } //____________________________________________ bool AlignableDetectorITS::prepareDetectorData() { // prepare TF data for processing: convert clusters + const auto& algConf = AlignConfig::Instance(); auto recoData = mController->getRecoContainer(); const auto clusITS = recoData->getITSClusters(); + const auto clusITSROF = recoData->getITSClustersROFRecords(); const auto patterns = recoData->getITSClustersPatterns(); auto pattIt = patterns.begin(); mITSClustersArray.clear(); mITSClustersArray.reserve(clusITS.size()); + if (algConf.ITSOverlapMargin > 0) { + mOverlapClusRef.clear(); + mOverlapClusRef.resize(clusITS.size(), -1); - for (auto& c : clusITS) { - auto* sensor = getSensor(c.getSensorID()); - double sigmaY2, sigmaZ2, sigmaYZ = 0, locXYZC[3], traXYZ[3]; - auto locXYZ = o2::its::ioutils::extractClusterDataA(c, pattIt, mITSDict, sigmaY2, sigmaZ2); // local ideal coordinates - const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix !!! RS FIXME - matAlg.LocalToMaster(locXYZ.data(), locXYZC); // aligned point in the local frame - const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct - mat.MasterToLocal(locXYZC, traXYZ); - /* - if (applyMisalignment) { - auto lrID = chmap.getLayer(c.getSensorID()); - sigmaY2 += conf.sysErrY2[lrID]; - sigmaZ2 += conf.sysErrZ2[lrID]; - } - */ - auto& cl3d = mITSClustersArray.emplace_back(c.getSensorID(), traXYZ[0], traXYZ[1], traXYZ[2], sigmaY2, sigmaZ2, sigmaYZ); // local --> tracking + mOverlapCandidateID.clear(); + mOverlapCandidateID.reserve(clusITS.size()); } + static std::vector edgeClusters; + int ROFCount = 0; + int16_t curSensID = -1; + struct ROFChipEntry { + int16_t rofCount = -1; + int chipFirstEntry = -1; + }; + std::array chipROFStart{}; // fill only for clusters with overlaps + + for (const auto& rof : clusITSROF) { + int maxic = rof.getFirstEntry() + rof.getNEntries(); + edgeClusters.clear(); + for (int ic = rof.getFirstEntry(); ic < maxic; ic++) { + const auto& c = clusITS[ic]; + int16_t sensID = c.getSensorID(); + auto* sensor = getSensor(sensID); + double sigmaY2, sigmaZ2, sigmaYZ = 0, locXYZC[3], traXYZ[3]; + auto pattItCopy = pattIt; + auto locXYZ = o2::its::ioutils::extractClusterDataA(c, pattIt, mITSDict, sigmaY2, sigmaZ2); // local ideal coordinates + const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix !!! RS FIXME + matAlg.LocalToMaster(locXYZ.data(), locXYZC); // aligned point in the local frame + const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct + mat.MasterToLocal(locXYZC, traXYZ); + auto& cl3d = mITSClustersArray.emplace_back(sensID, traXYZ[0], traXYZ[1], traXYZ[2], sigmaY2, sigmaZ2, sigmaYZ); // local --> tracking + + if (algConf.ITSOverlapMargin > 0) { + // fill chips overlaps info for clusters whose center is within of the algConf.ITSOverlapMargin distance from the chip min or max row edge + // but the pixel closest to this edge has distance of at least algConf.ITSOverlapEdgeRows from the edge + int row = 0, col = 0; + o2::itsmft::SegmentationAlpide::localToDetectorUnchecked(locXYZ[0], locXYZ[2], row, col); + int drow = row < o2::itsmft::SegmentationAlpide::NRows / 2 ? row : o2::itsmft::SegmentationAlpide::NRows - row - 1; // distance to the edge + if (drow * o2::itsmft::SegmentationAlpide::PitchRow < algConf.ITSOverlapMargin) { // rough check is passed, check if the edge cluster is indeed good + cl3d.setBit(row < o2::itsmft::SegmentationAlpide::NRows / 2 ? EdgeFlags::LowRow : EdgeFlags::HighRow); // flag that this is an edge cluster and indicate the low/high row side + // check if it is not too close to the edge (to be biased) + if (algConf.ITSOverlapEdgeRows > 0) { // is there a restriction? + auto pattID = c.getPatternID(); + drow = c.getRow(); + if (pattID != itsmft::CompCluster::InvalidPatternID) { + if (!mITSDict->isGroup(pattID)) { + const auto& patt = mITSDict->getPattern(pattID); // reference pixel is min row/col corner + if (row > o2::itsmft::SegmentationAlpide::NRows / 2) { + drow = o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() - 1); + } + } else { // group: reference pixel is the one containing the COG + o2::itsmft::ClusterPattern patt(pattItCopy); + drow = row < o2::itsmft::SegmentationAlpide::NRows / 2 ? drow - patt.getRowSpan() / 2 : o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() / 2 - 1); + } + } else { + o2::itsmft::ClusterPattern patt(pattItCopy); // reference pixel is min row/col corner + if (row > o2::itsmft::SegmentationAlpide::NRows / 2) { + drow = o2::itsmft::SegmentationAlpide::NRows - 1 - (drow + patt.getRowSpan() - 1); + } + } + if (drow < algConf.ITSOverlapEdgeRows) { // too close to the edge, flag this + cl3d.setBit(EdgeFlags::Biased); + } + } + if (!cl3d.isBitSet(EdgeFlags::Biased)) { + if (chipROFStart[sensID].rofCount != ROFCount) { // remember 1st entry + chipROFStart[sensID].rofCount = ROFCount; + chipROFStart[sensID].chipFirstEntry = edgeClusters.size(); // remember 1st entry of edge cluster for this chip + } + edgeClusters.push_back(ic); + } + } + } + } // clusters of ROF + // relate edge clusters of ROF to each other + int prevSensID = -1; + for (auto ic : edgeClusters) { + auto& cl = mITSClustersArray[ic]; + int sensID = cl.getSensorID(); + auto ovl = mOverlaps[sensID]; + int ovlCount = 0; + for (int ir = 0; ir < OVL::NSides; ir++) { + if (ovl.rowSide[ir] == OVL::NONE) { // no overlap from this row side + continue; + } + int chipOvl = ovl.rowSide[ir]; // look for overlaps with this chip + // are there clusters with overlaps on chipOvl? + if (chipROFStart[chipOvl].rofCount == ROFCount) { + auto oClusID = edgeClusters[chipROFStart[chipOvl].chipFirstEntry]; + while (oClusID < int(mITSClustersArray.size())) { + auto oClus = mITSClustersArray[oClusID]; + if (oClus.getSensorID() != sensID) { + break; // no more clusters on the overlapping chip + } + if (oClus.isBitSet(ovl.rowSideOverlap[ir]) && // make sure that the edge cluster is on the right side of the row + !oClus.isBitSet(EdgeFlags::Biased) && // not too close to the edge + std::abs(oClus.getZ() - cl.getZ()) < algConf.ITSOverlapMaxDZ) { // apply fiducial cut on Z distance of 2 clusters + // register overlaping cluster + if (!ovlCount) { // 1st overlap + mOverlapClusRef[ic] = mOverlapCandidateID.size(); + } + mOverlapCandidateID.push_back(oClusID); + ovlCount++; + } + oClusID++; + } + } + } + cl.setCount(std::min(127, ovlCount)); + } + ROFCount++; + } // loop over ROFs return true; } diff --git a/Detectors/Align/src/AlignableDetectorTOF.cxx b/Detectors/Align/src/AlignableDetectorTOF.cxx index a0ca5212594d4..9fa78e133a25d 100644 --- a/Detectors/Align/src/AlignableDetectorTOF.cxx +++ b/Detectors/Align/src/AlignableDetectorTOF.cxx @@ -47,6 +47,9 @@ void AlignableDetectorTOF::defineVolumes() // // AddVolume( volTOF = new AlignableVolume("TOF") ); // no main volume, why? AlignableVolume* sect[NSect] = {}; + AlignableVolume* volTOF = nullptr; // fictious envelope + addVolume(volTOF = new AlignableVolume("TOF_envelope", getDetLabel(), mController)); + volTOF->setDummyEnvelope(); // int cnt = 0; for (int isc = 0; isc < NSect; isc++) { @@ -60,6 +63,7 @@ void AlignableDetectorTOF::defineVolumes() } if (!sect[isc]) { sect[isc] = new AlignableVolume(Form("TOF/sm%02d", isc), getNonSensLabel(isc), mController); + sect[isc]->setParent(volTOF); } strip->setParent(sect[isc]); } // strip @@ -73,7 +77,7 @@ void AlignableDetectorTOF::defineVolumes() } //____________________________________________ -int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) +int AlignableDetectorTOF::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -81,7 +85,7 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) // (i.e. upper leg of cosmic track) // - mNPoints = 0; + int npoints = 0; auto algTrack = mController->getAlgTrack(); auto recoData = mController->getRecoContainer(); auto TOFClusters = recoData->getTOFClusters(); @@ -104,7 +108,6 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) const auto& matT2L = sensor->getMatrixT2L(); matT2L.MasterToLocal(locCorr, traCorr); // - mFirstPoint = algTrack->getNPoints(); auto& pnt = algTrack->addDetectorPoint(); const auto* sysE = sensor->getAddError(); // additional syst error @@ -118,11 +121,12 @@ int AlignableDetectorTOF::processPoints(GIndex gid, bool inv) pnt.setXSens(sensor->getXTracking()); pnt.setDetID(mDetID); pnt.setSID(sensor->getSID()); - // pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; - return mNPoints; + npoints++; + mNPoints += npoints; + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableDetectorTPC.cxx b/Detectors/Align/src/AlignableDetectorTPC.cxx index 63fac7dd9cac6..980ded2d8ff2f 100644 --- a/Detectors/Align/src/AlignableDetectorTPC.cxx +++ b/Detectors/Align/src/AlignableDetectorTPC.cxx @@ -10,84 +10,255 @@ // or submit itself to any jurisdiction. /// @file AlignableDetectorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 +/// @author ruben.shahoyan@cern.ch /// @brief TPC detector wrapper #include "Align/AlignableDetectorTPC.h" #include "Align/AlignableVolume.h" #include "Align/AlignableSensorTPC.h" #include "Align/Controller.h" -//#include "AliGeomManager.h" -//#include "AliESDtrack.h" -#include "Framework/Logger.h" +#include "Align/AlignConfig.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsTPC/Constants.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include #include - -ClassImp(o2::align::AlignableDetectorTPC); +#include "GPUO2ExternalUser.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include "GPUParam.inc" namespace o2 { namespace align { +using namespace TMath; //____________________________________________ -AlignableDetectorTPC::AlignableDetectorTPC(const char* title) +AlignableDetectorTPC::AlignableDetectorTPC(Controller* ctr) : AlignableDetector(DetID::TPC, ctr) { // default c-tor - SetNameTitle(Controller::getDetNameByDetID(Controller::kTPC), title); - setDetID(Controller::kTPC); } //____________________________________________ -AlignableDetectorTPC::~AlignableDetectorTPC() +void AlignableDetectorTPC::defineVolumes() { - // d-tor + // define fictious TPC envelope and sector volumes + AlignableVolume* volTPC = nullptr; + int labDet = getDetLabel(); + const int NSectors = o2::tpc::constants::MAXSECTOR / 2; + + addVolume(volTPC = new AlignableVolume("TPC_envelope", getDetLabel(), mController)); + volTPC->setDummyEnvelope(); + + for (int isec = 0; isec < o2::tpc::constants::MAXSECTOR; isec++) { + int isecSide = isec % NSectors; + const char* symname = Form("TPC/sec%02d", isec); + AlignableSensorTPC* sector = new AlignableSensorTPC(symname, o2::base::GeometryManager::getSensID(mDetID, isec), getSensLabel(isec), isec, mController); + sector->setParent(volTPC); + addVolume(sector); + } } //____________________________________________ -void AlignableDetectorTPC::defineVolumes() +void AlignableDetectorTPC::Print(const Option_t* opt) const { - // define TPC volumes - // - const int kNSect = 18, kAC = 2, kIOROC = 2; - const char* kSide[kAC] = {"A", "C"}; - const char* kROC[kIOROC] = {"Inner", "Outer"}; - // AlignableSensorTPC *chamb=0; - // - int labDet = getDetLabel(); - AlignableVolume* volTPC = new AlignableVolume("ALIC_1/TPC_M_1", labDet); - addVolume(volTPC); + // print info + AlignableDetector::Print(opt); +} + +//____________________________________________ +int AlignableDetectorTPC::processPoints(GIndex gid, int npntCut, bool inv) +{ + // Extract the points corresponding to this detector, recalibrate/realign them to the + // level of the "starting point" for the alignment/calibration session. + // If inv==true, the track propagates in direction of decreasing tracking X + // (i.e. upper leg of cosmic track) // + const auto& algConf = AlignConfig::Instance(); + const auto recoData = mController->getRecoContainer(); + const auto& trk = recoData->getTrack(gid); + gsl::span TPCShMap = recoData->clusterShMapTPC; + + int nClus = trk.getNClusters(); + if (nClus < npntCut) { + return -1; + } + int npointsIni = mNPoints; + auto prop = o2::base::Propagator::Instance(); // float version! + constexpr float TAN10 = 0.17632698; + const auto clusterIdxStruct = recoData->getTPCTracksClusterRefs(); + const auto clusterNativeAccess = recoData->inputsTPCclusters->clusterIndex; + bool fail = false; + auto algTrack = mController->getAlgTrack(); + + o2::track::TrackParCov trkParam = inv ? trk : trk.getOuterParam(); // we refit outer param inward + trkParam.resetCovariance(); + float bzkG = prop->getNominalBz(), qptB5Scale = std::abs(bzkG) > 0.1 ? std::abs(bzkG) / 5.006680f : 1.f; + float q2pt2 = trkParam.getQ2Pt() * trkParam.getQ2Pt(), q2pt2Wgh = q2pt2 * qptB5Scale * qptB5Scale; + float err2 = (100.f + q2pt2Wgh) / (1.f + q2pt2Wgh) * q2pt2; // -> 100 for high pTs, -> 1 for low pTs. + trkParam.setCov(err2, 14); // 100% error + + int direction = inv ? -1 : 1; + int start = inv ? nClus - 1 : 0; + int stop = inv ? -1 : nClus; + const o2::tpc::ClusterNative* cl = nullptr; + uint8_t sector, row = 0, currentSector = 0, currentRow = 0; + short clusterState = 0, nextState; + float tOffset = mTrackTimeStamp / (o2::constants::lhc::LHCBunchSpacingMUS * 8); + bool stopLoop = false; + int npoints = 0; + for (int i = start; i != stop; i += cl ? 0 : direction) { + float x, y, z, xTmp, yTmp, zTmp, charge = 0.f; + int clusters = 0; + double combRow = 0; - for (int roc = 0; roc < kIOROC; roc++) { // inner/outer - for (int side = 0; side < kAC; side++) { // A/C - for (int isc = 0; isc < kNSect; isc++) { // sector ID - const char* symname = Form("TPC/Endcap%s/Sector%d/%sChamber", kSide[side], isc + 1, kROC[roc]); - if (!gGeoManager->GetAlignableEntry(symname)) { - AliErrorF("Did not find alignable %s", symname); + while (true) { + if (!cl) { + auto clTmp = &trk.getCluster(clusterIdxStruct, i, clusterNativeAccess, sector, row); + if (row < algConf.minTPCPadRow) { + if (!inv) { // inward refit: all other clusters padrow will be <= minumum (with outward refit the following clusters have a chance to be accepted) + stopLoop = true; + } + break; + } else if (row > algConf.maxTPCPadRow) { + if (inv) { // outward refit: all other clusters padrow will be >= maximum (with inward refit the following clusters have a chance to be accepted) + stopLoop = true; + } + break; + } + if (algConf.discardEdgePadrows > 0 && getDistanceToStackEdge(row) < algConf.discardEdgePadrows) { + if (i + direction != stop) { + i += direction; + continue; + } else { + stopLoop = true; + break; + } + } + mController->getTPCCorrMaps()->Transform(sector, row, clTmp->getPad(), clTmp->getTime(), xTmp, yTmp, zTmp, tOffset); + if (algConf.discardSectorEdgeDepth > 0) { + if (std::abs(yTmp) + algConf.discardSectorEdgeDepth > xTmp * TAN10) { + if (i + direction != stop) { + i += direction; + continue; + } else { + stopLoop = true; + break; + } + } + } + + cl = clTmp; + nextState = TPCShMap[cl - clusterNativeAccess.clustersLinear]; + } + if (clusters == 0 || (sector == currentSector && std::abs(row - currentRow) < algConf.maxTPCRowsCombined)) { + if (clusters == 1) { + x *= charge; + y *= charge; + z *= charge; + combRow *= charge; + } + if (clusters == 0) { + x = xTmp; + y = yTmp; + z = zTmp; + // mController->getTPCCorrMaps()->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tOffset); + currentRow = row; + currentSector = sector; + charge = cl->qTot; + clusterState = nextState; + combRow = row; + LOGP(debug, "starting a supercluster at row {} of sector {} -> {},{},{}", currentRow, currentSector, x, y, z); + } else { + // float xx, yy, zz; + // mController->getTPCCorrMaps()->Transform(sector, row, cl->getPad(), cl->getTime(), xx, yy, zz, tOffset); + x += xTmp * cl->qTot; + y += yTmp * cl->qTot; + z += zTmp * cl->qTot; + combRow += row * cl->qTot; + charge += cl->qTot; + clusterState |= nextState; + npntCut--; + LOGP(debug, "merging cluster #{} at row {} to a supercluster starting at row {} ", clusters + 1, row, currentRow); + } + cl = nullptr; + clusters++; + if (i + direction != stop) { + i += direction; continue; } - int iid = side * kNSect + isc; - uint16_t vid = AliGeomManager::LayerToVolUID(AliGeomManager::kTPC1 + roc, iid); - iid = labDet + (1 + side) * 10000 + (1 + isc) * 100 + (1 + roc); - AlignableSensorTPC* sens = new AlignableSensorTPC(symname, vid, iid, isc); - sens->setParent(volTPC); - addVolume(sens); - } // sector ID - } // A/C - } // inner/outer - // -} + } + break; + } + if (stopLoop) { + break; + } + if (clusters == 0) { + continue; + } else if (clusters > 1) { + x /= charge; + y /= charge; + z /= charge; + currentRow = combRow / charge; + LOGP(debug, "Combined cluster of {} subclusters: row {} , {},{},{}", clusters, currentRow, x, y, z); + } -//____________________________________________ -bool AlignableDetectorTPC::AcceptTrack(const AliESDtrack* trc, int trtype) const -{ - // test if detector had seed this track - if (!CheckFlags(trc, trtype)) - return false; - if (trc->GetNcls(1) < mNPointsSel[trtype]) - return false; - return true; + if (!trkParam.rotate(math_utils::detail::sector2Angle(currentSector)) || !prop->PropagateToXBxByBz(trkParam, x, algConf.maxSnp)) { + break; + } + if (!npoints) { + trkParam.setZ(z); + } + + auto* sectSensor = (AlignableSensorTPC*)getSensor(currentSector); + const auto* sysE = sectSensor->getAddError(); // additional syst error + + std::array p = {y, z}; + std::array c = {0, 0, 0}; + mController->getTPCParam()->GetClusterErrors2(sector, currentRow, z, trkParam.getSnp(), trkParam.getTgl(), -1.f, 0.f, 0.f, c[0], c[2]); // TODO: Note this disables occupancy / charge components of the error estimation + mController->getTPCParam()->UpdateClusterError2ByState(clusterState, c[0], c[2]); + int nrComb = std::abs(row - currentRow) + 1; + if (nrComb > 1) { + float fact = 1. / std::sqrt(nrComb); + c[0] *= fact; + c[2] *= fact; + } + if (sysE[0] > 0.f) { + c[0] += sysE[0] * sysE[0]; + } + if (sysE[1] > 0.f) { + c[2] += sysE[1] * sysE[1]; + } + + if (!trkParam.update(p, c)) { + break; + } + + auto& pnt = algTrack->addDetectorPoint(); + pnt.setYZErrTracking(c[0], c[1], c[2]); + if (getUseErrorParam()) { // errors will be calculated just before using the point in the fit, using track info + pnt.setNeedUpdateFromTrack(); + } + pnt.setXYZTracking(x, y, z); + pnt.setSensor(sectSensor); + pnt.setAlphaSens(sectSensor->getAlpTracking()); + pnt.setXSens(sectSensor->getXTracking()); + pnt.setDetID(mDetID); + pnt.setSID(sectSensor->getSID()); + pnt.setContainsMeasurement(); + pnt.setInvDir(inv); + pnt.init(); + npoints++; + } + if (npoints < npntCut) { + algTrack->suppressLastPoints(npoints); + mNPoints = npointsIni; + npoints = -1; + } + mNPoints += npoints; + + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index bdf5b8b06a1b5..080d0f72b2516 100644 --- a/Detectors/Align/src/AlignableDetectorTRD.cxx +++ b/Detectors/Align/src/AlignableDetectorTRD.cxx @@ -26,6 +26,7 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsTRD/CalibratedTracklet.h" +#include "GPUO2InterfaceConfiguration.h" #include #include @@ -51,8 +52,11 @@ void AlignableDetectorTRD::defineVolumes() geo->createClusterMatrixArray(); // ideal T2L matrices AlignableSensorTRD* chamb = nullptr; - int labDet = getDetLabel(); AlignableVolume* sect[o2::trd::constants::NSECTOR]{}; + AlignableVolume* volTRD = nullptr; // fictious envelope + + addVolume(volTRD = new AlignableVolume("TRD_envelope", getDetLabel(), mController)); + volTRD->setDummyEnvelope(); for (int ilr = 0; ilr < o2::trd::constants::NLAYER; ilr++) { // layer for (int ich = 0; ich < o2::trd::constants::NSTACK * o2::trd::constants::NSECTOR; ich++) { // chamber @@ -67,6 +71,7 @@ void AlignableDetectorTRD::defineVolumes() } if (!sect[isector]) { sect[isector] = new AlignableVolume(Form("TRD/sm%02d", isector), getNonSensLabel(isector), mController); + sect[isector]->setParent(volTRD); } chamb->setParent(sect[isector]); } // chamber @@ -157,7 +162,7 @@ double AlignableDetectorTRD::getCalibDOFValWithCal(int id) const } //____________________________________________ -int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) +int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) { // Extract the points corresponding to this detector, recalibrate/realign them to the // level of the "starting point" for the alignment/calibration session. @@ -167,22 +172,22 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) const auto& algConf = AlignConfig::Instance(); const auto recoData = mController->getRecoContainer(); const auto& trk = recoData->getTrack(gid); - if (trk.getNtracklets() < algConf.minTRDTracklets) { + if (trk.getNtracklets() < npntCut) { return -1; } auto propagator = o2::base::Propagator::Instance(); // float version! - static float prevBz = -99999.; - if (prevBz != propagator->getNominalBz()) { - prevBz = propagator->getNominalBz(); - mRecoParam.setBfield(prevBz); + static bool firstCall = true; + if (firstCall) { + o2::gpu::GPUO2InterfaceConfiguration config; + config.ReadConfigurableParam(config); + mRecoParam.init(propagator->getNominalBz(), &config.configReconstruction); + firstCall = false; } - mNPoints = 0; const auto* transformer = mController->getTRDTransformer(); auto algTrack = mController->getAlgTrack(); - mFirstPoint = algTrack->getNPoints(); const auto trackletsRaw = recoData->getTRDTracklets(); bool fail = false; - int nPntIni = algTrack->getNPoints(); + int nPntIni = algTrack->getNPoints(), npoints = 0; o2::track::TrackPar trkParam = trk.getOuterParam(); // we refit outer param inward to get tracklet coordinates accounting for tilt for (int il = o2::trd::constants::NLAYER; il--;) { if (trk.getTrackletIndex(il) == -1) { @@ -199,18 +204,10 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) continue; } double locXYZ[3] = {trackletCalibLoc.getX(), trackletCalibLoc.getY(), trackletCalibLoc.getZ()}, locXYZC[3], traXYZ[3]; - ; const auto& matAlg = sensor->getMatrixClAlg(); // local alignment matrix matAlg.LocalToMaster(locXYZ, locXYZC); // aligned point in the local frame const auto& mat = sensor->getMatrixT2L(); // RS FIXME check if correct mat.MasterToLocal(locXYZC, traXYZ); - - // This is a hack until TRD T2L matrix problem will be solved - const auto trackletCalib = recoData->getTRDCalibratedTracklets()[trkltId]; - traXYZ[0] = trackletCalib.getX(); - traXYZ[1] = trackletCalib.getY(); - traXYZ[2] = trackletCalib.getZ(); - int trkltSec = sensor->getSector(); // trkltDet / (o2::trd::constants::NLAYER * o2::trd::constants::NSTACK); float alpSens = sensor->getAlpTracking(); // o2::math_utils::sector2Angle(trkltSec); if (trkltSec != o2::math_utils::angle2Sector(trkParam.getAlpha()) || @@ -252,14 +249,17 @@ int AlignableDetectorTRD::processPoints(GIndex gid, bool inv) pnt.setDetID(mDetID); pnt.setSID(sensor->getSID()); pnt.setContainsMeasurement(); + pnt.setInvDir(inv); pnt.init(); - mNPoints++; + npoints++; } if (fail) { // reset points to original start - algTrack->suppressLastPoints(mNPoints); - mNPoints = 0; + algTrack->suppressLastPoints(npoints); + npoints = 0; } - return mNPoints; + mNPoints += npoints; + + return npoints; } } // namespace align diff --git a/Detectors/Align/src/AlignableSensor.cxx b/Detectors/Align/src/AlignableSensor.cxx index 00b637efdd164..91658eba0a196 100644 --- a/Detectors/Align/src/AlignableSensor.cxx +++ b/Detectors/Align/src/AlignableSensor.cxx @@ -47,7 +47,7 @@ void AlignableSensor::dPosTraDParGeomLOC(const AlignmentPoint* pnt, double* deri // Jacobian of position in sensor tracking frame (tra) vs sensor LOCAL frame // parameters in TGeoHMatrix convention. // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // @@ -94,7 +94,7 @@ void AlignableSensor::dPosTraDParGeomLOC(const AlignmentPoint* pnt, double* deri // Jacobian of position in sensor tracking frame (tra) vs parent volume LOCAL frame parameters. // NO check of parentship is done! // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // this is the matrix for transition from sensor to parent volume local frames: LOC=matRel*loc @@ -193,7 +193,7 @@ void AlignableSensor::dPosTraDParGeomTRA(const AlignmentPoint* pnt, double* deri // tra' = tau*tra // // Result is stored in array deriv as linearized matrix 6x3 - const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5, 0.5, 0.5}; + const double kDelta[kNDOFGeom] = {0.1, 0.1, 0.1, 0.5 * DegToRad(), 0.5 * DegToRad(), 0.5 * DegToRad()}; // changed angles to radians double delta[kNDOFGeom], pos0[3], pos1[3], pos2[3], pos3[3]; TGeoHMatrix matMod; // diff --git a/Detectors/Align/src/AlignableSensorITS.cxx b/Detectors/Align/src/AlignableSensorITS.cxx index d3fe5476ecbbc..b4480a25bc740 100644 --- a/Detectors/Align/src/AlignableSensorITS.cxx +++ b/Detectors/Align/src/AlignableSensorITS.cxx @@ -66,5 +66,40 @@ void AlignableSensorITS::prepareMatrixT2L() setMatrixT2L(t2l); } +void AlignableSensorITS::prepareMatrixL2G(bool reco) +{ + // Note that for ITS2 it is NOT the same as GeometryManager::getMatrix() (i.e. that of the alignable volule) + // since we need the matrix of epitaxial layer and not the whole chip + auto geom = o2::its::GeometryTGeo::Instance(); + const auto* m = geom->extractMatrixSensor(getVolID()); + if (!m) { + LOGP(fatal, "Failed on :GeometryTGeo::extractMatrixSensor({})", getVolID()); + } + reco ? setMatrixL2GReco(*m) : setMatrixL2G(*m); +} + +void AlignableSensorITS::prepareMatrixL2GIdeal() +{ + // Note that for ITS2 it is NOT the same as GeometryManager::getOriginalMatrix (i.e. that of the alignable volule) + // since we need the matrix of epitaxial layer and not the whole chip + auto geom = o2::its::GeometryTGeo::Instance(); + TGeoHMatrix mtmp; + if (!base::GeometryManager::getOriginalMatrix(getSymName(), mtmp)) { // this is chip ideal matrix, not that of the epitaxial layer + LOG(fatal) << "Failed to find ideal L2G matrix for " << getSymName(); + } + // we have to apply to it the difference between the aligner epitaxial layer matrix and that of the chip + const auto* malgSens = geom->extractMatrixSensor(getVolID()); + if (!malgSens) { + LOGP(fatal, "Failed on :GeometryTGeo::extractMatrixSensor({})", getVolID()); + } + const auto* malgChip = geom->getMatrix(getVolID()); + // correct chip original matrix by the difference between aligneg chip and sensor matrices + // Sens_ideal = Chip_ideal * Chip_aligned^-1 * Sens_aligned + auto chAlignInv = malgChip->Inverse(); + chAlignInv.Multiply(*malgSens); + mtmp.Multiply(chAlignInv); + setMatrixL2GIdeal(mtmp); +} + } // namespace align } // namespace o2 diff --git a/Detectors/Align/src/AlignableSensorTPC.cxx b/Detectors/Align/src/AlignableSensorTPC.cxx index 7180a44d2df30..87b3923633f77 100644 --- a/Detectors/Align/src/AlignableSensorTPC.cxx +++ b/Detectors/Align/src/AlignableSensorTPC.cxx @@ -10,62 +10,54 @@ // or submit itself to any jurisdiction. /// @file AlignableSensorTPC.h -/// @author ruben.shahoyan@cern.ch, michael.lettrich@cern.ch -/// @since 2021-02-01 -/// @brief TPC sensor (chamber) +/// @author ruben.shahoyan@cern.ch +/// @brief TPC sensor #include "Align/AlignableSensorTPC.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/utils.h" #include "Framework/Logger.h" -//#include "AliTrackPointArray.h" -//#include "AliESDtrack.h" #include "Align/AlignmentPoint.h" -#include "Align/AlignableDetector.h" - -ClassImp(o2::align::AlignableSensorTPC) - - using namespace o2::align::utils; -using namespace TMath; namespace o2 { namespace align { +using namespace o2::align::utils; +using namespace TMath; //_________________________________________________________ -AlignableSensorTPC::AlignableSensorTPC(const char* name, int vid, int iid, int isec) - : AlignableSensor(name, vid, iid), fSector(isec) +AlignableSensorTPC::AlignableSensorTPC(const char* name, int vid, int iid, int isec, Controller* ctr) : AlignableSensor(name, vid, iid, ctr), mSector(isec) { // def c-tor } -//_________________________________________________________ -AlignableSensorTPC::~AlignableSensorTPC() +//____________________________________________ +void AlignableSensorTPC::prepareMatrixL2G(bool reco) +{ + double alp = math_utils::detail::sector2Angle(mSector % 18); + TGeoHMatrix m; + m.RotateZ(alp * RadToDeg()); + reco ? setMatrixL2GReco(m) : setMatrixL2G(m); +} + +//____________________________________________ +void AlignableSensorTPC::prepareMatrixL2GIdeal() { - // d-tor + double alp = math_utils::detail::sector2Angle(mSector % 18); + TGeoHMatrix m; + m.RotateZ(alp * RadToDeg()); + setMatrixL2GIdeal(m); } //____________________________________________ void AlignableSensorTPC::prepareMatrixT2L() { - // extract from geometry T2L matrix - double alp = math_utils::detail::sector2Angle(fSector); - double loc[3] = {0, 0, 0}, glo[3]; - getMatrixL2GIdeal().LocalToMaster(loc, glo); - double x = Sqrt(glo[0] * glo[0] + glo[1] * glo[1]); - TGeoHMatrix t2l; - t2l.SetDx(x); - t2l.RotateZ(alp * RadToDeg()); - const TGeoHMatrix& l2gi = getMatrixL2GIdeal().Inverse(); - t2l.MultiplyLeft(&l2gi); - /* - const TGeoHMatrix* t2l = AliGeomManager::GetTracking2LocalMatrix(getVolID()); - if (!t2l) { - Print("long"); - AliFatalF("Failed to find T2L matrix for VID:%d %s",getVolID(),getSymName()); - } - */ - setMatrixT2L(t2l); + // local and tracking matrices are the same + double alp = math_utils::detail::sector2Angle(mSector % 18); + mAlp = alp; + TGeoHMatrix m; + setMatrixT2L(m); // } diff --git a/Detectors/Align/src/AlignableSensorTRD.cxx b/Detectors/Align/src/AlignableSensorTRD.cxx index 2dc7d25ca5932..aa52567a36fdf 100644 --- a/Detectors/Align/src/AlignableSensorTRD.cxx +++ b/Detectors/Align/src/AlignableSensorTRD.cxx @@ -19,6 +19,7 @@ #include "Align/utils.h" #include "Framework/Logger.h" #include "Align/AlignmentPoint.h" +#include "DetectorsBase/GeometryManager.h" namespace o2 { @@ -33,22 +34,71 @@ AlignableSensorTRD::AlignableSensorTRD(const char* name, int vid, int iid, int i // def c-tor } +//____________________________________________ +void AlignableSensorTRD::prepareMatrixClAlg() +{ + // prepare alignment matrix in the pseudo-LOCAL frame of TRD (account that the chamber has extra X,Y rotations + TGeoHMatrix ma = getMatrixL2GIdeal().Inverse(); + ma *= getMatrixL2G(); + setMatrixClAlg(ma); + // +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixClAlgReco() +{ + // prepare alignment matrix in the pseudo-LOCAL frame of TRD (account that the chamber has extra X,Y rotations + TGeoHMatrix ma = getMatrixL2GIdeal().Inverse(); + ma *= getMatrixL2G(); + setMatrixClAlgReco(ma); + // +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixL2GIdeal() +{ + TGeoHMatrix Rxy; + Rxy.RotateX(-90); + Rxy.RotateY(-90); + TGeoHMatrix mtmp; + if (!base::GeometryManager::getOriginalMatrix(getSymName(), mtmp)) { + LOG(fatal) << "Failed to find ideal L2G matrix for " << getSymName(); + } + mtmp *= Rxy; + setMatrixL2GIdeal(mtmp); +} + +//____________________________________________ +void AlignableSensorTRD::prepareMatrixL2G(bool reco) +{ + TGeoHMatrix Rxy; + Rxy.RotateX(-90); + Rxy.RotateY(-90); + const char* path = getSymName(); + const TGeoHMatrix* l2g = nullptr; + if (!gGeoManager->GetAlignableEntry(path) || !(l2g = base::GeometryManager::getMatrix(path))) { + LOGP(fatal, "Failed to find L2G matrix for {}alignable {} -> {}", gGeoManager->GetAlignableEntry(path) ? "" : "non-", path, (void*)l2g); + } + TGeoHMatrix mtmp = *l2g; + mtmp *= Rxy; + reco ? setMatrixL2GReco(mtmp) : setMatrixL2G(mtmp); +} + //____________________________________________ void AlignableSensorTRD::prepareMatrixT2L() { // extract from geometry T2L matrix double alp = math_utils::detail::sector2Angle(mSector); mAlp = alp; - double loc[3] = {0, 0, 0}, glo[3]; - getMatrixL2GIdeal().LocalToMaster(loc, glo); - mX = Sqrt(glo[0] * glo[0] + glo[1] * glo[1]); - TGeoHMatrix t2l; - // t2l.SetDx(mX); // to remove when T2L will be clarified - t2l.RotateZ(alp * RadToDeg()); - const TGeoHMatrix l2gi = getMatrixL2GIdeal().Inverse(); - t2l.MultiplyLeft(&l2gi); - + TGeoHMatrix Rs; + Rs.RotateZ(-alp * TMath::RadToDeg()); + TGeoHMatrix m0 = getMatrixL2GIdeal(); + m0.MultiplyLeft(Rs); + TGeoHMatrix t2l = m0.Inverse(); setMatrixT2L(t2l); + double loc[3] = {0, 0, 0}, glo[3]; + t2l.MasterToLocal(loc, glo); + mX = glo[0]; // } diff --git a/Detectors/Align/src/AlignableVolume.cxx b/Detectors/Align/src/AlignableVolume.cxx index 00c3d97ee682a..73d888dc1559b 100644 --- a/Detectors/Align/src/AlignableVolume.cxx +++ b/Detectors/Align/src/AlignableVolume.cxx @@ -42,10 +42,6 @@ G_0*delta_0 ... G^-1_{j-2}*G_{j-1}*delta_{j-1}*G^-1_{j-1}*G_j*delta_j = Delta_0 * Delta_{1} ... Delta_{j-1}*Delta_{j}... * G_j - -> Delta_j = G'_{j-1} * G^-1_{j-1} * G_j * G'^-1_j - where G and G' are modified and original L2G matrices - - From this by induction one gets relation between local and global deltas: Delta_j = Z_j * delta_j * Z^-1_j @@ -295,6 +291,9 @@ void AlignableVolume::prepareMatrixL2G(bool reco) { // extract from geometry L2G matrix, depending on reco flag, set it as a reco-time // or current alignment matrix + if (isDummyEnvelope() || isDummy()) { + return; // unit matrix + } const char* path = getSymName(); if (gGeoManager->GetAlignableEntry(path)) { const TGeoHMatrix* l2g = base::GeometryManager::getMatrix(path); @@ -710,7 +709,7 @@ bool AlignableVolume::createLocDeltaMatrix(TGeoHMatrix& deltaM) const } //_________________________________________________________________ -void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const +void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg, const TGeoHMatrix* envelopeDelta) const { // create final alignment matrix, accounting for eventual prealignment // @@ -724,8 +723,22 @@ void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const // but this creates precision problem. // Therefore we use explicitly cached Deltas from prealignment object. // + // If (parent) envelopeDelta is provided, it is simply added on top of its proper global delta matrix + const AlignableVolume* par = getParent(); - if (createGloDeltaMatrix(alg) && par) { // account parent matrices only if the alg matrix is non-trivial + bool nonTrivial = createGloDeltaMatrix(alg); + if (envelopeDelta) { + if (nonTrivial) { + alg.MultiplyLeft(envelopeDelta); + } else { + alg = *envelopeDelta; + } + nonTrivial = true; + } + // Account parent matrices only if the alg matrix is non-trivial. + // Also, if parent is dummy envelope, it is already accounted via envelopeDelta + if (nonTrivial && (par && !par->isDummyEnvelope())) { + // if envelopeDelta is provided, then there should be TGeoHMatrix dchain; while (par) { dchain.MultiplyLeft(&par->getGlobalDeltaRef()); @@ -777,7 +790,7 @@ void AlignableVolume::createAlignmenMatrix(TGeoHMatrix& alg) const */ //_________________________________________________________________ -void AlignableVolume::createAlignmentObjects(std::vector& arr) const +void AlignableVolume::createAlignmentObjects(std::vector& arr, const TGeoHMatrix* envelopeDelta) const { // add to supplied array alignment object for itself and children if (isDummy()) { @@ -785,11 +798,27 @@ void AlignableVolume::createAlignmentObjects(std::vectorcreateAlignmentObjects(arr); + getChild(ich)->createAlignmentObjects(arr, nonTrivial ? envelopeDelta : nullptr); } } diff --git a/Detectors/Align/src/AlignmentTrack.cxx b/Detectors/Align/src/AlignmentTrack.cxx index b22b23b4881f2..644ee07c64984 100644 --- a/Detectors/Align/src/AlignmentTrack.cxx +++ b/Detectors/Align/src/AlignmentTrack.cxx @@ -168,7 +168,7 @@ bool AlignmentTrack::calcResidDeriv(double* extendedParams, bool invert, int pFr // (like http://root.cern.ch/root/html/ROOT__Math__RichardsonDerivator.html) // const auto& algConf = AlignConfig::Instance(); - trackParam_t probD[kNRDClones]; // use this to vary supplied param for derivative calculation + trackPar_t probD[kNRDClones]; // use this to vary supplied param for derivative calculation double varDelta[kRichardsonN]; const int kInvElem[kNKinParBON] = {-1, 1, 1, -1, -1}; // @@ -511,7 +511,7 @@ bool AlignmentTrack::calcResiduals(const double* extendedParams, bool invert, in } //______________________________________________________ -bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) +bool AlignmentTrack::propagateParamToPoint(trackPar_t* tr, int nTr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) { // Propagate set of tracks to the point (only parameters, no error matrix) // VECTORIZE this @@ -521,7 +521,7 @@ bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const Alig if (!propagateParamToPoint(tr[itr], pnt, maxStep, maxSnp, mt, signCorr)) { if (algConf.verbose > 2) { LOG(error) << "Failed on clone " << itr << " propagation "; - tr[itr].print(); + tr[itr].printParam(); pnt->print(AlignmentPoint::kMeasurementBit | AlignmentPoint::kMaterialBit); } return false; @@ -531,21 +531,33 @@ bool AlignmentTrack::propagateParamToPoint(trackParam_t* tr, int nTr, const Alig } //______________________________________________________ -bool AlignmentTrack::propagateParamToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) +bool AlignmentTrack::propagateParamToPoint(trackPar_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, int signCorr) { // propagate tracks to the point (only parameters, no error matrix) return propagate(tr, pnt, maxStep, maxSnp, mt, nullptr, signCorr); } //______________________________________________________ -bool AlignmentTrack::propagateToPoint(trackParam_t& tr, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +bool AlignmentTrack::propagateToPoint(trackParam_t& tr, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) { // propagate tracks to the point. If matCor is true, then material corrections will be applied. // if matPar pointer is provided, it will be filled by total x2x0 and signed xrho - return propagate(tr, pnt, maxStep, maxSnp, mt, tLT, signCorr); + return propagate(tr, linRef, pnt, maxStep, maxSnp, mt, tLT, signCorr); } -bool AlignmentTrack::propagate(trackParam_t& track, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +bool AlignmentTrack::propagate(trackParam_t& track, trackPar_t* linRef, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) +{ + if (signCorr == 0) { // auto + // calculate the sign of the energy loss correction and ensure the upper leg of cosmics is calculated correctly. + double dx = pnt->getXTracking() - track.getX(); + int dir = dx > 0.f ? 1 : -1; + signCorr = pnt->isInvDir() ? dir : -dir; // propagation along the track direction should have signCorr=-1 + } + // do propagation in at least 2 step to reveal eventual effect of MS on the position + return PropagatorD::Instance()->propagateToAlphaX(track, linRef, pnt->getAlphaSens(), pnt->getXTracking(), pnt->getUseBzOnly(), maxSnp, maxStep, 2, mt, tLT, signCorr); +} + +bool AlignmentTrack::propagate(trackPar_t& track, const AlignmentPoint* pnt, double maxStep, double maxSnp, MatCorrType mt, track::TrackLTIntegral* tLT, int signCorr) { if (signCorr == 0) { // auto // calculate the sign of the energy loss correction and ensure the upper leg of cosmics is calculated correctly. @@ -603,7 +615,7 @@ bool AlignmentTrack::ApplyMS(trackParam_t& trPar, double tms,double pms) */ //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corrPar, const AlignmentPoint* pnt) +bool AlignmentTrack::applyMatCorr(trackPar_t& trPar, const double* corrPar, const AlignmentPoint* pnt) { // Modify track param (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -630,7 +642,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corrPar, co } //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) +bool AlignmentTrack::applyMatCorr(trackPar_t& trPar, const double* corr) { // Modify track param (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -645,7 +657,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) printf("%+.3e ", corr[i]); } printf("\n"); - trPar.print(); + trPar.printParam(); } return false; } @@ -656,7 +668,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t& trPar, const double* corr) } //______________________________________________________ -bool AlignmentTrack::applyMatCorr(trackParam_t* trSet, int ntr, const double* corrDiag, const AlignmentPoint* pnt) +bool AlignmentTrack::applyMatCorr(trackPar_t* trSet, int ntr, const double* corrDiag, const AlignmentPoint* pnt) { // Modify set of track params (e.g. trackParam_t) in the tracking frame // by delta accounting for material effects @@ -683,7 +695,7 @@ bool AlignmentTrack::applyMatCorr(trackParam_t* trSet, int ntr, const double* co if (!applyMatCorr(trSet[itr], corr)) { if (algConf.verbose > 2) { LOGP(error, "Failed on clone {} materials", itr); - trSet[itr].print(); + trSet[itr].printParam(); } return false; } @@ -732,7 +744,7 @@ double AlignmentTrack::richardsonExtrap(const double* val, int ord) } //______________________________________________ -void AlignmentTrack::richardsonDeriv(const trackParam_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ) +void AlignmentTrack::richardsonDeriv(const trackPar_t* trSet, const double* delta, const AlignmentPoint* pnt, double& derY, double& derZ) { // Calculate Richardson derivatives for diagonalized Y and Z from a set of kRichardsonN pairs // of tracks with same parameter of i-th pair varied by +-delta[i] @@ -787,7 +799,7 @@ void AlignmentTrack::Print(Option_t* opt) const for (int ip = 0; ip < getNPoints(); ip++) { printf("#%3d ", ip); auto* pnt = getPoint(ip); - pnt->print(0); // RS FIXME OPT + pnt->print(AlignmentPoint::kMeasurementBit); // RS FIXME OPT // if (res && pnt->containsMeasurement()) { printf(" Residuals : %+.3e %+.3e -> Pulls: %+7.2f %+7.2f\n", @@ -848,17 +860,17 @@ bool AlignmentTrack::iniFit() { // perform initial fit of the track // - trackParam_t trc(*(trackParam_t*)this); - // + const auto& algConf = AlignConfig::Instance(); if (!getFieldON()) { // for field-off data impose nominal momentum + setQ2Pt(isCosmic() ? 1. / algConf.defPTB0Cosm : 1. / algConf.defPTB0Coll); } + trackParam_t trc(*(trackParam_t*)this); mChi2 = mChi2CosmUp = mChi2CosmDn = 0; // // the points are ranged from outer to inner for collision tracks, // and from outer point of lower leg to outer point of upper leg for the cosmic track // // the fit will always start from the outgoing track in inward direction - const auto& algConf = AlignConfig::Instance(); if (!fitLeg(trc, 0, getInnerPointID(), mNeedInv[0])) { if (algConf.verbose > 2) { LOG(warn) << "Failed fitLeg 0"; @@ -874,7 +886,7 @@ bool AlignmentTrack::iniFit() trackParam_t trcU = trc; if (!fitLeg(trcU, getNPoints() - 1, getInnerPointID() + 1, mNeedInv[1])) { //fit upper leg of cosmic track if (algConf.verbose > 2) { - LOG(warn) << "Failed fitLeg 0"; + LOG(warn) << "Failed fitLeg 1"; trc.print(); } return false; // collision track or cosmic lower leg @@ -882,7 +894,7 @@ bool AlignmentTrack::iniFit() // // propagate to reference point, which is the inner point of lower leg const AlignmentPoint* refP = getPoint(getInnerPointID()); - if (!propagateToPoint(trcU, refP, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, -1)) { // moving along the track: energy is lost + if (!propagateToPoint(trcU, nullptr, refP, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, -1)) { // moving along the track: energy is lost return false; } // @@ -922,6 +934,17 @@ bool AlignmentTrack::combineTracks(trackParam_t& trcL, const trackParam_t& trcU) return false; } // + LOGP(debug, "CosmDn: {}", trcL.asString()); + LOGP(debug, "CosmUp: {}", trcU.asString()); + float dsnp = trcL.getSnp() - trcU.getSnp(), dTgl = trcL.getTgl() - trcU.getTgl(); + if (std::abs(dsnp) > algConf.cosmMaxDSnp || std::abs(dTgl) > algConf.cosmMaxDTgl) { + if (algConf.verbose > 2) { + LOGP(error, "Rejecting track with dSnp={} dTgl={}", dsnp, dTgl); + LOGP(error, "CosmDn: {}", trcL.asString()); + LOGP(error, "CosmUp: {}", trcU.asString()); + } + return false; + } // const covMat_t& covU = trcU.getCov(); // const covMat_t& covL = trcL.getCov(); // @@ -968,7 +991,8 @@ bool AlignmentTrack::combineTracks(trackParam_t& trcL, const trackParam_t& trcU) } mChi2 += chi2; // - // printf("Combined: Chi2Tot:%.2f ChiUp:%.2f ChiDn:%.2f ChiCmb:%.2f\n",mChi2,mChi2CosmUp,mChi2CosmDn, chi2); + LOGP(debug, "CosmCB: {}", trcL.asString()); + LOGP(debug, "Combined: Chi2Tot:{} ChiUp:{} ChiDn:{} ChiCmb:{} DSnp:{} DTgl:{} Alp:{}", mChi2, mChi2CosmUp, mChi2CosmDn, chi2, dsnp, dTgl, trcL.getAlpha()); return true; } @@ -1004,7 +1028,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) inv = true; trc.invert(); } - // Fit is done from outward to inward: normally against the track direction (hence e.loss is compensated) inless inversion is requested (cosmic upper leg) + // Fit is done from outward to inward: normally against the track direction (hence e.loss is compensated) unless inversion is requested (cosmic upper leg) if (!propagateParamToPoint(trc, p0, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), -1)) { if (algConf.verbose > 2) { LOGF(warn, "Failed on PropagateParamOnlyTo to %f", p0->getXTracking()); @@ -1012,6 +1036,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) } return false; } + trackPar_t linRef(trc), *linRefP = algConf.useLinRef ? &linRef : nullptr; trc.setCov(kIniErr); trc.setCov(16 * trc.getQ2Pt() * trc.getQ2Pt(), 4, 4); // lowest diagonal element (Q2Pt2) // @@ -1030,7 +1055,7 @@ bool AlignmentTrack::fitLeg(trackParam_t& trc, int pFrom, int pTo, bool& inv) int pntCnt = 0; for (int ip = pFrom; ip != pTo; ip += pinc) { // inward fit from outer point AlignmentPoint* pnt = getPoint(ip); - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // against track direction : e.loss is compensated + if (!propagateToPoint(trc, linRefP, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // against track direction : e.loss is compensated if (algConf.verbose > 2) { LOGF(warn, "Failed on propagateToPoint %d (%d : %d) %f", ip, pFrom, pTo, pnt->getXTracking()); trc.print(); @@ -1127,7 +1152,7 @@ bool AlignmentTrack::residKalman() trc.invert(); inv = !inv; } - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { return false; } if (!pnt->containsMeasurement()) { @@ -1166,7 +1191,7 @@ bool AlignmentTrack::residKalman() trc.invert(); inv = !inv; } - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // we are going along track direction, e.loss is applied + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), nullptr, signELoss)) { // we are going along track direction, e.loss is applied return false; } if (!pnt->containsMeasurement()) { @@ -1323,7 +1348,7 @@ bool AlignmentTrack::processMaterials(trackParam_t& trc, int pFrom, int pTo) // matTL.clearFast(); // printf("-> ProcMat %d (%d->%d)\n",ip,pFrom,pTo); - if (!propagateToPoint(trc, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), &matTL, signELoss)) { // with material corrections + if (!propagateToPoint(trc, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType(algConf.matCorType), &matTL, signELoss)) { // with material corrections if (algConf.verbose > 2) { LOG(error) << "Failed to take track to point" << ip << " (dir: " << pFrom << "->" << pTo << ") with mat.corr."; trc.print(); @@ -1334,7 +1359,7 @@ bool AlignmentTrack::processMaterials(trackParam_t& trc, int pFrom, int pTo) // // is there enough material to consider the point as a scatterer? bool hasMaterial = matTL.getX2X0() > minX2X0; - if (!propagateToPoint(tr0, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType::USEMatCorrNONE, nullptr, signELoss)) { // no material corrections + if (!propagateToPoint(tr0, nullptr, pnt, algConf.maxStep, algConf.maxSnp, MatCorrType::USEMatCorrNONE, nullptr, signELoss)) { // no material corrections if (algConf.verbose > 2) { LOG(error) << "Failed to take track to point" << ip << " (dir: " << pFrom << "->" << pTo << ") with mat.corr."; tr0.print(); @@ -1428,7 +1453,7 @@ void AlignmentTrack::sortPoints() // sort points in order against track direction: innermost point is last // for collision tracks. // For 2-leg cosmic tracks: 1st points of outgoing (lower) leg are added from large to - // small radii, then the points of incomint (upper) leg are added in increasing R direction + // small radii, then the points of incoming (upper) leg are added in increasing R direction // // The mInnerPointID will mark the id of the innermost point, i.e. the last one for collision-like // tracks and in case of cosmics - the point of lower leg with smallest R diff --git a/Detectors/Align/src/Controller.cxx b/Detectors/Align/src/Controller.cxx index 387af1460a0a0..5cfbbf9f3a4ae 100644 --- a/Detectors/Align/src/Controller.cxx +++ b/Detectors/Align/src/Controller.cxx @@ -24,6 +24,7 @@ #include "Align/AlignableDetectorITS.h" #include "Align/AlignableDetectorTRD.h" #include "Align/AlignableDetectorTOF.h" +#include "Align/AlignableDetectorTPC.h" #include "Align/EventVertex.h" #include "Align/ResidualsControllerFast.h" #include "Align/GeometricalConstraint.h" @@ -43,10 +44,13 @@ #include #include #include +#include "GPUO2ExternalUser.h" +#include "DataFormatsTPC/WorkflowHelper.h" #include #include "CommonUtils/NameConf.h" #include "MathUtils/SymMatrixSolver.h" #include "DataFormatsParameters/GRPObject.h" +#include "GPUParam.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" @@ -110,6 +114,9 @@ void Controller::init() if (mDetMask[DetID::TRD]) { addDetector(new AlignableDetectorTRD(this)); } + if (mDetMask[DetID::TPC]) { + addDetector(new AlignableDetectorTPC(this)); + } if (mDetMask[DetID::TOF]) { addDetector(new AlignableDetectorTOF(this)); } @@ -160,6 +167,10 @@ void Controller::process() bool useVertexConstrain = false; if (vtx) { auto nContrib = vtx->getNContributors(); + // check cov matrix since data reconstructed with < 6797a257f5ab8ffaec32d56dddb0a321939bdf1c may have negative errors + if (vtx->getSigmaX2() < 0. || vtx->getSigmaY2() < 0. || vtx->getSigmaZ2() < 0.) { + continue; + } useVertexConstrain = nContrib >= algConf.vtxMinCont && nContrib <= algConf.vtxMaxCont; mStat.data[ProcStat::kInput][ProcStat::kVertices]++; } @@ -176,6 +187,8 @@ void Controller::process() int start = trackRef.getFirstEntryOfSource(src), end = start + trackRef.getEntriesOfSource(src); for (int ti = start; ti < end; ti++) { auto trackIndex = primVerGIs[ti]; + mAlgTrack->setCurrentTrackID(trackIndex); + bool tpcIn = false; if (trackIndex.isAmbiguous()) { auto& ambSeen = ambigTable[trackIndex]; if (ambSeen) { // processed @@ -209,21 +222,33 @@ void Controller::process() int ndet = 0, npntDet = 0; if ((det = getDetector(DetID::ITS))) { - if (contributorsGID[GIndex::ITS].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITS], false)) >= algConf.minITSClusters) { + if (contributorsGID[GIndex::ITS].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITS], algConf.minITSClusters, false)) > 0) { npnt += npntDet; ndet++; - } else if (mAllowAfterburnerTracks && contributorsGID[GIndex::ITSAB].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITSAB], false)) > 0) { + } else if (mAllowAfterburnerTracks && contributorsGID[GIndex::ITSAB].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::ITSAB], 2, false)) > 0) { npnt += npntDet; ndet++; } else { continue; } } - if ((det = getDetector(DetID::TRD)) && contributorsGID[GIndex::TRD].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TRD], false)) >= algConf.minTRDTracklets) { + if ((det = getDetector(DetID::TPC)) && contributorsGID[GIndex::TPC].isIndexSet()) { + float t0 = 0, t0err = 0; + mRecoData->getTrackTime(trackIndex, t0, t0err); + ((AlignableDetectorTPC*)det)->setTrackTimeStamp(t0); + npntDet = det->processPoints(contributorsGID[GIndex::TPC], algConf.minTPCClusters, false); + if (npntDet > 0) { + npnt += npntDet; + ndet++; + tpcIn = true; + } + } + + if ((det = getDetector(DetID::TRD)) && contributorsGID[GIndex::TRD].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TRD], algConf.minTRDTracklets, false)) > 0) { npnt += npntDet; ndet++; } - if ((det = getDetector(DetID::TOF)) && contributorsGID[GIndex::TOF].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TOF], false)) > 0) { + if ((det = getDetector(DetID::TOF)) && contributorsGID[GIndex::TOF].isIndexSet() && (npntDet = det->processPoints(contributorsGID[GIndex::TOF], algConf.minTOFClusters, false)) > 0) { npnt += npntDet; ndet++; } @@ -231,7 +256,7 @@ void Controller::process() if (algConf.verbose > 1) { LOGP(info, "processing track {} {} of vtref {}, Ndets:{}, Npoints: {}, use vertex: {} | Kin: {} Kout: {}", ti, trackIndex.asString(), ivref, ndet, npnt, useVertexConstrain && trackIndex.isPVContributor(), trcIn.asString(), trcOut.asString()); } - if (ndet < algConf.minDetectors) { + if (ndet < algConf.minDetectors || (tpcIn && ndet == 1)) { // we don't want TPC only track continue; } if (npnt < algConf.minPointTotal) { @@ -307,8 +332,16 @@ void Controller::process() } continue; } - - if (mUseMC && mDebugOutputLevel) { + if (mDebugOutputLevel && mAlgTrackDbg.setTrackParam(mAlgTrack.get())) { + mAlgTrackDbg.mGID = trackIndex; + (*mDBGOut) << "algtrack" + << "runNumber=" << mTimingInfo.runNumber + << "tfID=" << mTimingInfo.tfCounter + << "orbit=" << mTimingInfo.firstTForbit + << "bz=" << PropagatorD::Instance()->getNominalBz() + << "t=" << mAlgTrackDbg << "\n"; + } + if (mUseMC && mDebugOutputLevel > 1) { auto lbl = mRecoData->getTrackMCLabel(trackIndex); if (lbl.isValid()) { std::vector pntX, pntY, pntZ, trcX, trcY, trcZ, prpX, prpY, prpZ, alpha, xsens, pntXTF, pntYTF, pntZTF, resY, resZ; @@ -379,6 +412,12 @@ void Controller::process() void Controller::processCosmic() { auto timerStart = std::chrono::system_clock::now(); + const auto tracks = mRecoData->getCosmicTracks(); + if (!tracks.size()) { + LOGP(info, "Skipping TF {}: No cosmic tracks", mNTF); + mNTF++; + return; + } int nTrc = 0, nTrcAcc = 0; for (auto id = DetID::First; id <= DetID::Last; id++) { auto* det = getDetector(id); @@ -386,18 +425,164 @@ void Controller::processCosmic() det->prepareDetectorData(); // in case the detector needs to preprocess the RecoContainer data } } - const auto& algConf = AlignConfig::Instance(); - // process vertices with contributor tracks bool fieldON = std::abs(PropagatorD::Instance()->getNominalBz()) > 0.1; - const auto tracks = mRecoData->getCosmicTracks(); for (const auto& track : tracks) { + resetForNextTrack(); nTrc++; mStat.data[ProcStat::kInput][ProcStat::kCosmic]++; + std::array contributorsGID[2] = {mRecoData->getSingleDetectorRefs(track.getRefBottom()), mRecoData->getSingleDetectorRefs(track.getRefTop())}; + bool hasTRD = false, hasITS = false, hasTPC = false, hasTOF = false; + if (contributorsGID[0][GTrackID::TRD].isIndexSet() || contributorsGID[1][GTrackID::TRD].isIndexSet()) { + hasTRD = true; + } + if (contributorsGID[0][GTrackID::TOF].isIndexSet() || contributorsGID[1][GTrackID::TOF].isIndexSet()) { + hasTOF = true; + } + if (contributorsGID[0][GTrackID::TPC].isIndexSet() || contributorsGID[1][GTrackID::TPC].isIndexSet()) { + hasTPC = true; + } + if (contributorsGID[0][GTrackID::ITS].isIndexSet() || contributorsGID[1][GTrackID::ITS].isIndexSet()) { + hasITS = true; + } + // check detectors contributions + AlignableDetector* det = nullptr; + int ndet = 0, npnt = 0; + bool accTrack = true; + bool tpcIn = false; + if ((det = getDetector(DetID::ITS))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::ITS].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::ITS], algConf.minITSClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minITSClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + + if ((det = getDetector(DetID::TPC))) { + int npntDet = 0; + ((AlignableDetectorTPC*)det)->setTrackTimeStamp(track.getTimeMUS().getTimeStamp()); + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TPC].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TPC], algConf.minTPCClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTPCClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + tpcIn = true; + } + } + + if ((det = getDetector(DetID::TRD))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TRD].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TRD], algConf.minTRDTrackletsCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTRDTrackletsCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + + if ((det = getDetector(DetID::TOF))) { + int npntDet = 0; + for (int ibt = 0; ibt < 2; ibt++) { + int npntDetBT = 0; + mAlgTrack->setCurrentTrackID(ibt ? track.getRefBottom() : track.getRefTop()); + if (contributorsGID[ibt][GIndex::TOF].isIndexSet() && (npntDetBT = det->processPoints(contributorsGID[ibt][GIndex::TOF], algConf.minTOFClustersCosmLeg, ibt)) < 0) { + accTrack = false; + break; + } + npntDet += npntDetBT; + } + if (!accTrack || npntDet < algConf.minTOFClustersCosm) { + continue; + } + if (npntDet) { + ndet++; + npnt += npntDet; + } + } + if (algConf.verbose > 1) { + LOGP(info, "processing cosmic track B-Leg:{} T-Leg:{}, Ndets:{}, Npoints: {}", track.getRefBottom().asString(), track.getRefTop().asString(), ndet, npnt); + } + if (ndet < algConf.minDetectorsCosm /* || (tpcIn && ndet == 1)*/) { + continue; + } + if (npnt < algConf.minPointTotalCosm) { + if (algConf.verbose > 0) { + LOGP(info, "too few points {} < {}", npnt, algConf.minPointTotalCosm); + } + continue; + } + mAlgTrack->setCosmic(true); + mAlgTrack->copyFrom(mRecoData->getTrackParamOut(track.getRefBottom())); // copy kinematices of outer track as the refit will be done inward + mAlgTrack->setFieldON(fieldON); + mAlgTrack->sortPoints(); + if (!mAlgTrack->iniFit()) { + if (algConf.verbose > 0) { + LOGP(warn, "iniFit failed"); + } + continue; + } + if (!mAlgTrack->processMaterials()) { + if (algConf.verbose > 0) { + LOGP(warn, "processMaterials failed"); + } + continue; + } + mAlgTrack->defineDOFs(); + if (!mAlgTrack->calcResidDeriv()) { + if (algConf.verbose > 0) { + LOGP(warn, "calcResidDeriv failed"); + } + continue; + } + if (mDebugOutputLevel && mAlgTrackDbg.setTrackParam(mAlgTrack.get())) { + mAlgTrackDbg.mGID = track.getRefBottom(); + mAlgTrackDbg.mGIDCosmUp = track.getRefTop(); + (*mDBGOut) << "algtrack" + << "runNumber=" << mTimingInfo.runNumber + << "tfID=" << mTimingInfo.tfCounter + << "orbit=" << mTimingInfo.firstTForbit + << "bz=" << PropagatorD::Instance()->getNominalBz() + << "t=" << mAlgTrackDbg << "\n"; + } + storeProcessedTrack(); + mStat.data[ProcStat::kAccepted][ProcStat::kCosmic]++; + nTrcAcc++; } auto timerEnd = std::chrono::system_clock::now(); std::chrono::duration duration = timerEnd - timerStart; - LOGP(info, "Processed TF {}: {} vertices ({} used), {} tracks ({} used) in {} ms", mNTF, nTrc, nTrcAcc, duration.count()); + LOGP(info, "Processed cosmic TF {}: {} tracks ({} used) in {} ms", mNTF, nTrc, nTrcAcc, duration.count()); mNTF++; } @@ -1690,5 +1875,17 @@ void Controller::expandGlobalsBy(int n) mNDOFs += n; } +//______________________________________________ +void Controller::setTPCVDrift(const o2::tpc::VDriftCorrFact& v) +{ + mTPCDrift = v; +} + +//______________________________________________ +void Controller::setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph) +{ + mTPCCorrMapsHelper = maph; +} + } // namespace align } // namespace o2 diff --git a/Detectors/Align/src/ResidualsController.cxx b/Detectors/Align/src/ResidualsController.cxx index 94092597e5e40..b8d64ecea202f 100644 --- a/Detectors/Align/src/ResidualsController.cxx +++ b/Detectors/Align/src/ResidualsController.cxx @@ -206,7 +206,7 @@ bool ResidualsController::fillTrack(AlignmentTrack& trc, bool doKalman) int nfill = 0; for (int i = 0; i < np; i++) { auto pnt = trc.getPoint(i); - int inv = pnt->isInvDir() ? -1 : 1; // Flag invertion for cosmic upper leg + int inv = pnt->isInvDir() ? -1 : 1; // Flag inversion for cosmic upper leg if (!pnt->containsMeasurement()) { continue; } @@ -216,7 +216,7 @@ bool ResidualsController::fillTrack(AlignmentTrack& trc, bool doKalman) mVolID[nfill] = pnt->getVolID(); mLabel[nfill] = pnt->getSensor()->getInternalID(); mAlpha[nfill] = pnt->getAlphaSens(); - mX[nfill] = pnt->getXTracking() * inv; + mX[nfill] = pnt->getXTracking(); mY[nfill] = pnt->getYTracking(); mZ[nfill] = pnt->getZTracking(); mDY[nfill] = pnt->getResidY(); diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 1bde116e23c23..83a9193274e4f 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -8,12 +8,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. +#add_compile_options(-O0 -g -fPIC) o2_add_library(DetectorsBase SOURCES src/Detector.cxx src/GeometryManager.cxx src/MaterialManager.cxx src/MaterialManagerParam.cxx + src/GeometryManagerParam.cxx src/Propagator.cxx src/MatLayerCyl.cxx src/MatLayerCylSet.cxx @@ -25,6 +27,10 @@ o2_add_library(DetectorsBase src/TFIDInfoHelper.cxx src/GRPGeomHelper.cxx src/Stack.cxx + src/VMCSeederService.cxx + src/GlobalParams.cxx + src/O2Tessellated.cxx + src/TGeoGeometryUtils.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -39,9 +45,10 @@ o2_add_library(DetectorsBase O2::SimulationDataFormat O2::SimConfig O2::CCDB + O2::GPUDataTypes MC::VMC TBB::tbb - PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Merger # Must not link to avoid cyclic dependency + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -49,6 +56,7 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/GeometryManager.h include/DetectorsBase/MaterialManager.h include/DetectorsBase/MaterialManagerParam.h + include/DetectorsBase/GeometryManagerParam.h include/DetectorsBase/Propagator.h include/DetectorsBase/Ray.h include/DetectorsBase/MatCell.h @@ -56,17 +64,22 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/MatLayerCylSet.h include/DetectorsBase/Aligner.h include/DetectorsBase/Stack.h - include/DetectorsBase/SimFieldUtils.h) + include/DetectorsBase/SimFieldUtils.h + include/DetectorsBase/GlobalParams.h + include/DetectorsBase/O2Tessellated.h + ) if(BUILD_SIMULATION) - o2_add_test( - MatBudLUT - SOURCES test/testMatBudLUT.cxx - COMPONENT_NAME DetectorsBase - PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::ITSMFTReconstruction - LABELS detectorsbase - ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + if (NOT APPLE) + o2_add_test( + MatBudLUT + SOURCES test/testMatBudLUT.cxx + COMPONENT_NAME DetectorsBase + PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::ITSMFTReconstruction + LABELS detectorsbase + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) + endif() o2_add_test( MCStack @@ -78,6 +91,19 @@ if(BUILD_SIMULATION) VMCWORKDIR=${CMAKE_BINARY_DIR}/stage/${CMAKE_INSTALL_DATADIR}) endif() +install(FILES test/buildMatBudLUT.C + test/extractLUTLayers.C + test/rescaleLUT.C + DESTINATION share/macro/) + o2_add_test_root_macro(test/buildMatBudLUT.C PUBLIC_LINK_LIBRARIES O2::DetectorsBase LABELS detectorsbase) + +o2_add_test_root_macro(test/extractLUTLayers.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) + +o2_add_test_root_macro(test/rescaleLUT.C + PUBLIC_LINK_LIBRARIES O2::DetectorsBase + LABELS detectorsbase) diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h index 8ccdae6dfbfd3..e94123bb2b7ff 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -26,11 +26,15 @@ #include "DetectorsCommonDataFormats/CTFHeader.h" #include "DetectorsCommonDataFormats/CTFIOSize.h" #include "DataFormatsCTP/TriggerOffsetsParam.h" -#include "rANS/rans.h" +#include "DetectorsCommonDataFormats/ANSHeader.h" +#include "rANS/factory.h" +#include "rANS/compat.h" +#include "rANS/histogram.h" #include #include "Framework/InitContext.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/ConfigParamRegistry.h" +#include namespace o2 { @@ -54,8 +58,8 @@ class CTFCoderBase Decoder }; CTFCoderBase() = delete; - CTFCoderBase(int n, DetID det, float memFactor = 1.f) : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} - CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f) : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f) {} + CTFCoderBase(int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} + CTFCoderBase(OpType op, int n, DetID det, float memFactor = 1.f, const std::string& ctfdictOpt = "none") : mOpType(op), mCoders(n), mDet(det), mMemMarginFactor(memFactor > 1.f ? memFactor : 1.f), mDictOpt{ctfdictOpt} {} virtual ~CTFCoderBase() = default; virtual void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) = 0; @@ -77,19 +81,30 @@ class CTFCoderBase void createCodersFromFile(const std::string& dictPath, o2::ctf::CTFCoderBase::OpType op, bool mayFail = false); template - void createCoder(OpType op, const o2::rans::RenormedFrequencyTable& renormedFrequencyTable, int slot) + void createCoder(OpType op, const o2::rans::RenormedDenseHistogram& renormedHistogram, int slot) { - if (renormedFrequencyTable.empty()) { - LOG(warning) << "Empty dictionary provided for slot " << slot << ", " << (op == OpType::Encoder ? "encoding" : "decoding") << " will assume literal symbols only"; - } - - switch (op) { - case OpType::Encoder: - mCoders[slot].reset(new o2::rans::LiteralEncoder64(renormedFrequencyTable)); - break; - case OpType::Decoder: - mCoders[slot].reset(new o2::rans::LiteralDecoder64(renormedFrequencyTable)); - break; + LOG_IF(warning, renormedHistogram.empty()) << fmt::format("Empty dictionary provided for slot {}, {} will assume literal symbols only", slot, (op == OpType::Encoder ? "encoding" : "decoding")); + + if (mANSVersion == ANSVersionCompat) { + switch (op) { + case OpType::Encoder: + mCoders[slot] = std::make_any>(rans::compat::makeEncoder::fromRenormed(renormedHistogram)); + break; + case OpType::Decoder: + mCoders[slot] = std::make_any>(rans::compat::makeDecoder::fromRenormed(renormedHistogram)); + break; + } + } else if (mANSVersion == ANSVersion1) { + switch (op) { + case OpType::Encoder: + mCoders[slot] = std::make_any>(rans::makeDenseEncoder<>::fromRenormed(renormedHistogram)); + break; + case OpType::Decoder: + mCoders[slot] = std::make_any>(rans::makeDecoder<>::fromRenormed(renormedHistogram)); + break; + } + } else { + throw std::runtime_error("unsupported ANS version"); } } @@ -128,6 +143,9 @@ class CTFCoderBase size_t getIRFrameSelMarginFwd() const { return mIRFrameSelMarginFwd; } long getIRFrameSelShift() const { return mIRFrameSelShift; } + inline const ctf::ANSHeader& getANSVersion() const noexcept { return mANSVersion; }; + inline ctf::ANSHeader& getANSVersion() { return const_cast(const_cast(*this).getANSVersion()); }; + inline void setANSVersion(const ctf::ANSHeader& ansVersion) noexcept { mANSVersion = ansVersion; }; void setBCShift(int64_t n) { mBCShift = n; } void setFirstTFOrbit(uint32_t n) { mFirstTFOrbit = n; } auto getBCShift() const { return mBCShift; } @@ -135,6 +153,14 @@ class CTFCoderBase void setSupportBCShifts(bool v = true) { mSupportBCShifts = v; } bool getSupportBCShifts() const { return mSupportBCShifts; } + void setDictBinding(const std::string& s) { mDictBinding = s; } + const std::string& getDictBinding() const { return mDictBinding; } + + void setTrigOffsBinding(const std::string& s) { mTrigOffsBinding = s; } + const std::string& getTrigOffsBinding() const { return mTrigOffsBinding; } + + const DetID getDet() const { return mDet; } + protected: void reportIRFrames(); std::string getPrefix() const { return o2::utils::Str::concat_string(mDet.getName(), "_CTF: "); } @@ -147,17 +173,33 @@ class CTFCoderBase } bool canApplyBCShift(const o2::InteractionRecord& ir) const { return canApplyBCShift(ir, mBCShift); } + template + [[nodiscard]] size_t estimateBufferSize(size_t slot, source_IT samplesBegin, source_IT samplesEnd); + + template + size_t estimateBufferSize(size_t slot, size_t nSamples); + + template + [[nodiscard]] size_t estimateBufferSize(size_t slot, const std::vector& samples) + { + return estimateBufferSize(slot, samples.begin(), samples.end()); + } + template std::vector loadDictionaryFromTree(TTree* tree); - std::vector> mCoders; // encoders/decoders + std::vector mCoders; // encoders/decoders DetID mDet; - CTFDictHeader mExtHeader; // external dictionary header + std::string mDictOpt{}; + std::string mDictBinding{"ctfdict"}; + std::string mTrigOffsBinding{"trigoffset"}; + CTFDictHeader mExtHeader; // external dictionary header o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector - float mMemMarginFactor = 1.0f; // factor for memory allocation in EncodedBlocks + float mMemMarginFactor = 1.0f; // factor for memory allocation in EncodedBlocks bool mLoadDictFromCCDB{true}; bool mSupportBCShifts{false}; - OpType mOpType; // Encoder or Decoder - int64_t mBCShift = 0; // shift to apply to decoded IR (i.e. CTP offset if was not corrected on raw data decoding level) + OpType mOpType; // Encoder or Decoder + ctf::ANSHeader mANSVersion{ctf::ANSVersionCompat}; // Version of the ANSEncoder/Decoder + int64_t mBCShift = 0; // shift to apply to decoded IR (i.e. CTP offset if was not corrected on raw data decoding level) uint32_t mFirstTFOrbit = 0; size_t mIRFrameSelMarginBwd = 0; // margin in BC to add to the IRFrame lower boundary when selection is requested size_t mIRFrameSelMarginFwd = 0; // margin in BC to add to the IRFrame upper boundary when selection is requested @@ -272,15 +314,31 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (ic.options().hasOption("irframe-shift")) { mIRFrameSelShift = (long)ic.options().get("irframe-shift"); } - auto dict = ic.options().get("ctf-dict"); - if (dict.empty() || dict == "ccdb") { // load from CCDB + bool ansVersionProvided = false; + if (ic.options().hasOption("ans-version")) { + if (ic.options().isSet("ans-version")) { + const std::string ansVersionString = ic.options().get("ans-version"); + if (!ansVersionString.empty()) { + mANSVersion = ansVersionFromString(ansVersionString); + LOGP(info, "parsing ansVersionString {} into {}", ansVersionString, static_cast(mANSVersion)); + if (mANSVersion == ANSVersionUnspecified) { + throw std::invalid_argument(fmt::format("Invalid ANS Version {}", ansVersionString)); + } + ansVersionProvided = true; + } + } + } + if (mDictOpt.empty() || mDictOpt == "ccdb") { // load from CCDB mLoadDictFromCCDB = true; } else { - if (dict != "none") { // none means per-CTF dictionary will created on the fly - createCodersFromFile(dict, mOpType); - LOGP(info, "Loaded {} from {}", mExtHeader.asString(), dict); + if (mDictOpt != "none") { // none means per-CTF dictionary will created on the fly + createCodersFromFile(mDictOpt, mOpType); + LOGP(info, "Loaded {} from {}, ANS Version {}", mExtHeader.asString(), mDictOpt, std::string(mANSVersion)); } else { - LOGP(info, "Internal per-TF CTF Dict will be created"); + if (!ansVersionProvided) { + mANSVersion = ANSVersion1; + } + LOGP(info, "Internal per-TF CTF Dict will be created, ANS Version {}", std::string(mANSVersion)); } mLoadDictFromCCDB = false; // don't try to load from CCDB } @@ -315,7 +373,7 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi } createCoders(*dict, mOpType); mExtHeader = static_cast(CTF::get(dict->data())->getHeader()); - LOGP(info, "Loaded {} from CCDB", mExtHeader.asString()); + LOGP(info, "Loaded {} from CCDB, ANS Version {}", mExtHeader.asString(), std::string(mANSVersion)); } mLoadDictFromCCDB = false; // we read the dictionary at most once! } else if ((match = (matcher == o2::framework::ConcreteDataMatcher("CTP", "Trig_Offset", 0)))) { @@ -333,6 +391,39 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi return match; } +template +[[nodiscard]] inline size_t CTFCoderBase::estimateBufferSize(size_t slot, IT samplesBegin, IT samplesEnd) +{ + using source_type = typename std::iterator_traits::value_type; + const size_t nSamples = std::distance(samplesBegin, samplesEnd); + return estimateBufferSize(slot, nSamples); +}; + +template +[[nodiscard]] inline size_t CTFCoderBase::estimateBufferSize(size_t slot, size_t nSamples) +{ + + std::any& coder = mCoders[slot]; + if (coder.has_value()) { + const size_t alphabetRangeBits = [this, &coder]() { + if (mANSVersion == ANSVersionCompat) { + const auto& encoder = std::any_cast&>(coder); + auto view = rans::trim(rans::makeHistogramView(encoder.getSymbolTable())); + return rans::utils::getRangeBits(view.getMin(), view.getMax()); + } else if (mANSVersion == ANSVersion1) { + const auto& encoder = std::any_cast&>(coder); + auto view = rans::trim(rans::makeHistogramView(encoder.getSymbolTable())); + return rans::utils::getRangeBits(view.getMin(), view.getMax()); + } else { + throw std::runtime_error("unsupported ANS version"); + } + }(); + return rans::compat::calculateMaxBufferSizeB(nSamples, alphabetRangeBits); + } else { + return nSamples * sizeof(source_T); + } +}; + } // namespace ctf } // namespace o2 diff --git a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h index ba87b6ecd7ddb..ba3dd821fa118 100644 --- a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h +++ b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h @@ -25,9 +25,15 @@ #include "Framework/CompletionPolicy.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DeviceSpec.h" +#include "Framework/Task.h" #include "Framework/DataSpecUtils.h" #include #include +#include +#ifdef WITH_OPENMP +#include +#include "TROOT.h" +#endif namespace o2 { @@ -160,7 +166,12 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect } for (auto& is : spec.inputs) { if (inputBindings.find(is.binding) != inputBindings.end()) { - LOG(error) << "Found duplicate input binding " << is.binding; + // we can accept duplicate binding if it is bound to the same spec (e.g. ccdbParamSpec) + if (std::find(combinedInputSpec.begin(), combinedInputSpec.end(), is) == combinedInputSpec.end()) { + LOG(error) << "Found duplicate input binding with different spec.:" << is; + } else { + continue; // consider as already accounted + } } combinedInputSpec.push_back(is); inputBindings[is.binding] = 1; @@ -212,6 +223,16 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect void init(o2::framework::InitContext& ic) { std::cerr << "Init Combined\n"; + mNThreads = std::max(1, ic.options().get("combine-nthreads")); + if (mNThreads > 1) { +#ifdef WITH_OPENMP + LOGP(info, "Combined tasks will be run with {} threads", mNThreads); + ROOT::EnableThreadSafety(); +#else + LOGP(warn, "{} threads requested for combined tasks but OpenMP is not detected, link it from the workflow CMakeList", mNThreads); + mNThreads = 1; +#endif + } for (auto& t : tasks) { // the init function actually creates the onProcess function // which we have to do here (maybe some more stuff needed) @@ -236,18 +257,33 @@ o2::framework::DataProcessorSpec specCombiner(std::string const& name, std::vect void run(o2::framework::ProcessingContext& pc) { - std::cerr << "Processing Combined\n"; - for (auto& t : tasks) { - std::cerr << " Executing sub-device " << t.name << "\n"; - t.algorithm.onProcess(pc); + std::cerr << "Processing Combined with " << mNThreads << " threads\n"; + if (mNThreads > 1) { + size_t nt = tasks.size(); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (size_t i = 0; i < nt; i++) { + auto& t = tasks[i]; + std::cerr << " Executing sub-device " << t.name << "\n"; + t.algorithm.onProcess(pc); + } + } else { + for (auto& t : tasks) { + std::cerr << " Executing sub-device " << t.name << "\n"; + t.algorithm.onProcess(pc); + } } } private: std::vector tasks; std::unordered_map> optionsRemap; + int mNThreads = 1; }; + combinedOptions.emplace_back(ConfigParamSpec{"combine-nthreads", VariantType::Int, 1, {"Number of threads for combined tasks"}}); + return DataProcessorSpec{ name, combinedInputSpec, diff --git a/Detectors/Base/include/DetectorsBase/Detector.h b/Detectors/Base/include/DetectorsBase/Detector.h index 73764e8b7021b..f1744086d6a05 100644 --- a/Detectors/Base/include/DetectorsBase/Detector.h +++ b/Detectors/Base/include/DetectorsBase/Detector.h @@ -29,7 +29,6 @@ #include #include #include -#include #include "CommonUtils/ShmManager.h" #include "CommonUtils/ShmAllocator.h" #include @@ -42,9 +41,7 @@ #include -namespace o2 -{ -namespace base +namespace o2::base { /// This is the basic class for any AliceO2 detector module, whether it is @@ -109,6 +106,9 @@ class Detector : public FairDetector /// declare alignable volumes of detector virtual void addAlignableVolumes() const; + /// fill parallel geometry with sensitive volumes of detector + virtual void fillParallelWorld() const; + /// Sets per wrapper volume parameters virtual void defineWrapperVolume(Int_t id, Double_t rmin, Double_t rmax, Double_t zspan); @@ -216,6 +216,10 @@ class Detector : public FairDetector // which is required for media creation static void initFieldTrackingParams(int& mode, float& maxfield); + /// set the DetID to HitBitIndex mapping. Succeeds if not already set. + static void setDetId2HitBitIndex(std::vector const& v) { Detector::sDetId2HitBitIndex = v; } + static std::vector const& getDetId2HitBitIndex() { return Detector::sDetId2HitBitIndex; } + protected: Detector(const Detector& origin); @@ -233,6 +237,7 @@ class Detector : public FairDetector static Float_t mDensityFactor; //! factor that is multiplied to all material densities (ONLY for // systematic studies) + static std::vector sDetId2HitBitIndex; //! global lookup table keeping mapping of DetID to index in hit bit field (used in MCTrack) ClassDefOverride(Detector, 1); // Base class for ALICE Modules }; @@ -255,17 +260,12 @@ T decodeShmMessage(fair::mq::Parts& dataparts, int index, bool*& busy) } // this goes into the source -void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, - void* data, size_t size, void (*func_ptr)(void* data, void* hint), void* hint); +void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, TClass* cl); template void attachTMessage(Container const& hits, fair::mq::Channel& channel, fair::mq::Parts& parts) { - TMessage* tmsg = new TMessage(); - tmsg->WriteObjectAny((void*)&hits, TClass::GetClass(typeid(hits))); - attachMessageBufferToParts( - parts, channel, tmsg->Buffer(), tmsg->BufferSize(), - [](void* data, void* hint) { delete static_cast(hint); }, tmsg); + attachMessageBufferToParts(parts, channel, (void*)&hits, TClass::GetClass(typeid(hits))); } void* decodeTMessageCore(fair::mq::Parts& dataparts, int index); @@ -738,9 +738,9 @@ class DetImpl : public o2::base::Detector int mInitialized = false; char* mHitCollectorBufferPtr = nullptr; //! pointer to hit (collector) buffer location (strictly internal) + ClassDefOverride(DetImpl, 0); }; -} // namespace base -} // namespace o2 +} // namespace o2::base #endif diff --git a/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h b/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h index e3ec5cf02b2b5..ec1f9a91c3f20 100644 --- a/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h +++ b/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h @@ -22,6 +22,7 @@ #include "DataFormatsParameters/GRPLHCIFData.h" #include "DataFormatsParameters/GRPECSObject.h" #include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" namespace o2::framework { @@ -92,6 +93,7 @@ struct GRPGeomRequest { Ideal, Alignments }; + bool askAggregateRunInfo = false; bool askGRPECS = false; bool askGRPLHCIF = false; bool askGRPMagField = false; @@ -104,7 +106,8 @@ struct GRPGeomRequest { bool needPropagatorD = false; // init also PropagatorD GRPGeomRequest() = delete; - GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce = false, bool needPropD = false); + GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce = false, bool needPropD = false, std::string detMaskString = "all"); + void requireAggregateRunInfo(std::vector& inputs); void addInput(const o2::framework::InputSpec&& isp, std::vector& inputs); }; @@ -121,14 +124,16 @@ class GRPGeomHelper } void setRequest(std::shared_ptr req); bool finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); - void checkUpdates(o2::framework::ProcessingContext& pc) const; + void checkUpdates(o2::framework::ProcessingContext& pc); auto getAlignment(o2::detectors::DetID det) const { return mAlignments[det]; } auto getMatLUT() const { return mMatLUT; } auto getGRPECS() const { return mGRPECS; } auto getGRPLHCIF() const { return mGRPLHCIF; } auto getGRPMagField() const { return mGRPMagField; } - auto getOrbitResetTimeMS() const { return mOrbitResetTimeMS; } + auto getOrbitResetTimeMS() const { return mOrbitResetTimeMUS / 1000; } + auto getOrbitResetTimeMUS() const { return mOrbitResetTimeMUS; } + const o2::parameters::AggregatedRunInfo& getAggregatedRunInfo() const { return mAggregatedRunInfo; } static int getNHBFPerTF(); private: @@ -141,7 +146,8 @@ class GRPGeomHelper const o2::parameters::GRPECSObject* mGRPECS = nullptr; const o2::parameters::GRPLHCIFData* mGRPLHCIF = nullptr; const o2::parameters::GRPMagField* mGRPMagField = nullptr; - long mOrbitResetTimeMS = 0; // orbit reset time in milliseconds + o2::parameters::AggregatedRunInfo mAggregatedRunInfo{}; + long mOrbitResetTimeMUS = 0; // orbit reset time in microseconds }; } // namespace base diff --git a/Detectors/Base/include/DetectorsBase/GeometryManager.h b/Detectors/Base/include/DetectorsBase/GeometryManager.h index 96998187cca63..ad1d77b10f49a 100644 --- a/Detectors/Base/include/DetectorsBase/GeometryManager.h +++ b/Detectors/Base/include/DetectorsBase/GeometryManager.h @@ -86,8 +86,7 @@ class GeometryManager : public TObject double meanZ = 0.; // mean Z: sum(x_i*Z_i)/sum(x_i) [adimensional] double meanZ2A = 0.; // Z/A mean: sum(x_i*Z_i/A_i)/sum(x_i) [adimensional] double length = -1.; // length: sum(x_i) [cm] - int nCross = 0; - ; // number of boundary crosses + int nCross = 0; // number of boundary crosses MatBudgetExt() = default; ~MatBudgetExt() = default; @@ -132,6 +131,7 @@ class GeometryManager : public TObject /// detector geometry. The output global matrix is stored in 'm'. /// Returns kFALSE in case TGeo has not been initialized or the volume path is not valid. static Bool_t getOriginalMatrixFromPath(const char* path, TGeoHMatrix& m); + private: /// sensitive volume identifier composed from (det_ID< { + bool useParallelWorld = false; + bool usePwGeoBVH = false; + bool usePwCaching = false; + + int printLevel = -1; // geometry level to print out (determined by the volume path) + + O2ParamDef(GeometryManagerParam, "GeometryManagerParam"); +}; + +} // namespace o2 + +#endif /* DETECTORS_BASE_INCLUDE_GEOMETRYMANAGERPARAM_H_ */ diff --git a/Detectors/Base/include/DetectorsBase/GlobalParams.h b/Detectors/Base/include/DetectorsBase/GlobalParams.h new file mode 100644 index 0000000000000..25bee925196d3 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/GlobalParams.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef DETECTORS_BASE_GLOBALPARAMS_H_ +#define DETECTORS_BASE_GLOBALPARAMS_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ + +struct GlobalParams : public o2::conf::ConfigurableParamHelper { + bool withITS3 = false; // detector geometry includes ITS3 + + O2ParamDef(GlobalParams, "GlobalParams"); +}; + +} // namespace o2 + +#endif // DETECTORS_BASE_GLOBALPARAMS_H_ diff --git a/Detectors/Base/include/DetectorsBase/MatCell.h b/Detectors/Base/include/DetectorsBase/MatCell.h index b5af66439955d..40c5fd3db1f69 100644 --- a/Detectors/Base/include/DetectorsBase/MatCell.h +++ b/Detectors/Base/include/DetectorsBase/MatCell.h @@ -31,7 +31,13 @@ struct MatCell { float meanX2X0; ///< fraction of radiaton lenght GPUd() MatCell() : meanRho(0.f), meanX2X0(0.f) {} - GPUdDefault() MatCell(const MatCell& src) CON_DEFAULT; + GPUdDefault() MatCell(const MatCell& src) = default; + + GPUd() void set(const MatCell& c) + { + meanRho = c.meanRho; + meanX2X0 = c.meanX2X0; + } GPUd() void scale(float scale) { @@ -49,7 +55,7 @@ struct MatBudget : MatCell { float length; ///< length in material GPUd() MatBudget() : length(0.f) {} - GPUdDefault() MatBudget(const MatBudget& src) CON_DEFAULT; + GPUdDefault() MatBudget(const MatBudget& src) = default; GPUd() void scale(float scale) { diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h index 4d509fdf9e3c1..e63de51e0a6ca 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCyl.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCyl.h @@ -54,9 +54,11 @@ class MatLayerCyl : public o2::gpu::FlatObject Within = 0, Above = 1 }; +#ifndef GPUCA_GPUCODE MatLayerCyl(); - MatLayerCyl(const MatLayerCyl& src) CON_DELETE; - ~MatLayerCyl() CON_DEFAULT; + MatLayerCyl(const MatLayerCyl& src) = delete; + ~MatLayerCyl() = default; +#endif #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version MatLayerCyl(float rMin, float rMax, float zHalfSpan, float dzMin, float drphiMin); @@ -90,6 +92,15 @@ class MatLayerCyl : public o2::gpu::FlatObject GPUd() const MatCell& getCellPhiBin(int iphi, int iz) const { return mCells[getCellIDPhiBin(iphi, iz)]; } GPUd() const MatCell& getCell(int iphiSlice, int iz) const { return mCells[getCellID(iphiSlice, iz)]; } +#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version + MatCell& getCellPhiBin(int iphi, int iz) + { + return mCells[getCellIDPhiBin(iphi, iz)]; + } + + void scale(float factor, bool _x2x0 = true, bool _rho = true); +#endif + // ---------------------- Z slice manipulation // convert Z to Zslice GPUd() RangeStatus isZOutside(float z) const { return z < getZMin() ? Below : (z > getZMax() ? Above : Within); } @@ -156,6 +167,8 @@ class MatLayerCyl : public o2::gpu::FlatObject /// Gives minimal alignment in bytes required for the flat buffer static constexpr size_t getBufferAlignmentBytes() { return 8; } #endif + // linearized cell ID from phi bin and z bin + GPUd() int getCellIDPhiBin(int iphi, int iz) const { return getCellID(phiBin2Slice(iphi), iz); } protected: GPUd() int getNCells() const { return getNZBins() * getNPhiSlices(); } @@ -165,9 +178,6 @@ class MatLayerCyl : public o2::gpu::FlatObject // linearized cell ID from phi slice and z bin GPUd() int getCellID(int iphi, int iz) const { return iphi * getNZBins() + iz; } - // linearized cell ID from phi bin and z bin - GPUd() int getCellIDPhiBin(int iphi, int iz) const { return getCellID(phiBin2Slice(iphi), iz); } - // convert Phi (in 0:2pi convention) to PhiBinID GPUd() int getPhiBinID(float phi) const { diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 8c25c07c0ccc1..cba6e5cebcfc8 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -51,9 +51,11 @@ class MatLayerCylSet : public o2::gpu::FlatObject { public: - MatLayerCylSet() CON_DEFAULT; - ~MatLayerCylSet() CON_DEFAULT; - MatLayerCylSet(const MatLayerCylSet& src) CON_DELETE; +#ifndef GPUCA_GPUCODE + MatLayerCylSet() = default; + ~MatLayerCylSet() = default; + MatLayerCylSet(const MatLayerCylSet& src) = delete; +#endif GPUd() const MatLayerCylSetLayout* get() const { return reinterpret_cast(mFlatBufferPtr); } GPUd() MatLayerCylSetLayout* get() { return reinterpret_cast(mFlatBufferPtr); } @@ -79,8 +81,15 @@ class MatLayerCylSet : public o2::gpu::FlatObject static MatLayerCylSet* loadFromFile(const std::string& inpFName = "matbud.root"); static MatLayerCylSet* rectifyPtrFromFile(MatLayerCylSet* ptr); + // initializes internal voxel lookup + void initLayerVoxelLU(); + void flatten(); + MatLayerCyl& getLayer(int i) { return get()->mLayers[i]; } + MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3, const MatLayerCylSet* toAdd = nullptr) const; + void finalizeStructures(); + #endif // !GPUCA_ALIGPUCODE #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version @@ -89,11 +98,18 @@ class MatLayerCylSet : public o2::gpu::FlatObject // get material budget traversed on the line between point0 and point1 return getMatBudget(point0.X(), point0.Y(), point0.Z(), point1.X(), point1.Y(), point1.Z()); } + + void scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0 = true, bool _rho = true); + void scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0 = true, bool _rho = true); + #endif // !GPUCA_ALIGPUCODE GPUd() MatBudget getMatBudget(float x0, float y0, float z0, float x1, float y1, float z1) const; GPUd() int searchSegment(float val, int low = -1, int high = -1) const; + /// searches a layer based on r2 input, using a lookup table + GPUd() int searchLayerFast(float r2, int low = -1, int high = -1) const; + #ifndef GPUCA_GPUCODE //----------------------------------------------------------- std::size_t estimateFlatBufferSize() const; @@ -114,6 +130,14 @@ class MatLayerCylSet : public o2::gpu::FlatObject static constexpr size_t getBufferAlignmentBytes() { return 8; } #endif // !GPUCA_GPUCODE + static constexpr float LayerRMax = 500; // maximum value of R lookup (corresponds to last layer of MatLUT) + static constexpr float VoxelRDelta = 0.05; // voxel spacing for layer lookup; seems a natural choice - corresponding ~ to smallest spacing + static constexpr float InvVoxelRDelta = 1.f / VoxelRDelta; + static constexpr int NumVoxels = int(LayerRMax / VoxelRDelta); + + uint16_t mLayerVoxelLU[2 * NumVoxels]; //! helper structure to lookup a layer based on known radius (static dimension for easy copy to GPU) + bool mInitializedLayerVoxelLU = false; //! if the voxels have been initialized + ClassDefNV(MatLayerCylSet, 1); }; diff --git a/Detectors/Base/include/DetectorsBase/MaterialManager.h b/Detectors/Base/include/DetectorsBase/MaterialManager.h index 4448998ee3d33..b0de75c2d6c84 100644 --- a/Detectors/Base/include/DetectorsBase/MaterialManager.h +++ b/Detectors/Base/include/DetectorsBase/MaterialManager.h @@ -218,7 +218,7 @@ class MaterialManager std::unordered_map mDensityMap; void initDensityMap(); - float getDensity(std::string const& modname); + float getDensity(std::string const& modname, std::string const& matname); // Hide details by providing these private methods so it cannot happen that special settings // are applied as default settings by accident using a boolean flag diff --git a/Detectors/Base/include/DetectorsBase/O2Tessellated.h b/Detectors/Base/include/DetectorsBase/O2Tessellated.h new file mode 100644 index 0000000000000..0a1cee8b3e01f --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/O2Tessellated.h @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_BASE_O2TESSELLATED_ +#define ALICEO2_BASE_O2TESSELLATED_ + +#include "TGeoShape.h" +#include "TGeoBBox.h" +#include "TGeoVector3.h" +#include "TGeoTypedefs.h" +#include "TGeoTessellated.h" + +namespace o2 +{ +namespace base +{ + +class O2Tessellated : public TGeoBBox +{ + + public: + using Vertex_t = Tessellated::Vertex_t; + + private: + int fNfacets = 0; // Number of facets + int fNvert = 0; // Number of vertices + int fNseg = 0; // Number of segments + bool fDefined = false; //! Shape fully defined + bool fClosedBody = false; // The faces are making a closed body + + // for now separate vectors but might be better to group per face + std::vector fVertices; // List of vertices + std::vector fFacets; // List of facets + std::vector fOutwardNormals; // Vector of outward-facing normals (to be streamed !) + + std::multimap fVerticesMap; //! Temporary map used to deduplicate vertices + bool fIsClosed = false; //! to know if shape still needs closure/initialization + void* fBVH = nullptr; //! BVH acceleration structure for safety and navigation + + O2Tessellated(const O2Tessellated&) = delete; + O2Tessellated& operator=(const O2Tessellated&) = delete; + + // bvh helper functions + void BuildBVH(); + void CalculateNormals(); + + public: + // constructors + O2Tessellated() {} + O2Tessellated(const char* name, int nfacets = 0); + O2Tessellated(const char* name, const std::vector& vertices); + // from a TGeoTessellated + O2Tessellated(TGeoTessellated const&, bool check = false); + + // destructor + ~O2Tessellated() override {} + + void ComputeBBox() override; + void CloseShape(bool check = true, bool fixFlipped = true, bool verbose = true); + + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2); + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3); + bool AddFacet(int i1, int i2, int i3); + bool AddFacet(int i1, int i2, int i3, int i4); + int AddVertex(const Vertex_t& vert); + + bool FacetCheck(int ifacet) const; + Vertex_t FacetComputeNormal(int ifacet, bool& degenerated) const; + + int GetNfacets() const { return fFacets.size(); } + int GetNsegments() const { return fNseg; } + int GetNvertices() const { return fNvert; } + bool IsClosedBody() const { return fClosedBody; } + bool IsDefined() const { return fDefined; } + + const TGeoFacet& GetFacet(int i) const { return fFacets[i]; } + const Vertex_t& GetVertex(int i) const { return fVertices[i]; } + + int DistancetoPrimitive(int, int) override { return 99999; } + const TBuffer3D& GetBuffer3D(int reqSections, Bool_t localFrame) const override; + void GetMeshNumbers(int& nvert, int& nsegs, int& npols) const override; + int GetNmeshVertices() const override { return fNvert; } + void InspectShape() const override {} + TBuffer3D* MakeBuffer3D() const override; + void Print(Option_t* option = "") const override; + void SavePrimitive(std::ostream&, Option_t*) override {} + void SetPoints(double* points) const override; + void SetPoints(Float_t* points) const override; + void SetSegsAndPols(TBuffer3D& buff) const override; + void Sizeof3D() const override {} + + /// Resize and center the shape in a box of size maxsize + void ResizeCenter(double maxsize); + + /// Flip all facets + void FlipFacets() + { + for (auto facet : fFacets) + facet.Flip(); + } + + bool CheckClosure(bool fixFlipped = true, bool verbose = true); + + /// Reader from .obj format + static O2Tessellated* ImportFromObjFormat(const char* objfile, bool check = false, bool verbose = false); + + // navigation functions used by TGeoNavigator (attention: only the iact == 3 cases implemented for now) + Double_t DistFromOutside(const Double_t* point, const Double_t* dir, Int_t iact = 1, + Double_t step = TGeoShape::Big(), Double_t* safe = nullptr) const override; + Double_t DistFromInside(const Double_t* point, const Double_t* dir, Int_t iact = 1, Double_t step = TGeoShape::Big(), + Double_t* safe = nullptr) const override; + bool Contains(const Double_t* point) const override; + Double_t Safety(const Double_t* point, Bool_t in = kTRUE) const override; + void ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const override; + + // these are trivial implementations, just for debugging + Double_t DistFromInside_Loop(const Double_t* point, const Double_t* dir) const; + Double_t DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const; + bool Contains_Loop(const Double_t* point) const; + + Double_t Capacity() const override; + + private: + // a safety kernel used in multiple implementations + template + Double_t SafetyKernel(const Double_t* point, bool in, int* closest_facet_id = nullptr) const; + + ClassDefOverride(O2Tessellated, 1) // tessellated shape class +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index 84cc4b5f30732..75b9446aebade 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -17,7 +17,6 @@ #define ALICEO2_BASE_PROPAGATOR_ #include "GPUCommonRtypes.h" -#include "GPUCommonArray.h" #include "CommonConstants/PhysicsConstants.h" #include "ReconstructionDataFormats/Track.h" #include "ReconstructionDataFormats/DCA.h" @@ -25,6 +24,7 @@ #include "DetectorsBase/MatLayerCylSet.h" #ifndef GPUCA_GPUCODE +#include #include #endif @@ -34,7 +34,7 @@ namespace parameters { class GRPObject; class GRPMagField; -} +} // namespace parameters namespace dataformats { @@ -45,7 +45,7 @@ namespace field { class MagFieldFast; class MagneticField; -} +} // namespace field namespace gpu { @@ -76,6 +76,10 @@ class PropagatorImpl value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t& linRef, value_type x, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool PropagateToXBxByBz(TrackPar_t& track, value_type x, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; @@ -84,6 +88,10 @@ class PropagatorImpl value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(TrackParCov_t& track, TrackPar_t& linRef, value_type x, value_type bZ, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToX(TrackPar_t& track, value_type x, value_type bZ, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; @@ -92,13 +100,40 @@ class PropagatorImpl GPUd() bool propagateTo(track_T& track, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const { - return bzOnly ? propagateToX(track, x, getNominalBz(), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + return bzOnly ? propagateToX(track, x, getBz(track.getXYZGlo()), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool propagateToX(TrackParCov_t& track, TrackPar_t* linRef, value_type x, value_type bZ, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return linRef ? propagateToX(track, *linRef, x, bZ, maxSnp, maxStep, matCorr, tofInfo, signCorr) : propagateToX(track, x, bZ, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t* linRef, value_type x, + value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, + track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return linRef ? PropagateToXBxByBz(track, *linRef, x, maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); + } + + GPUd() bool propagateTo(TrackParCov_t& track, TrackPar_t* linRef, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const + { + return bzOnly ? propagateToX(track, linRef, x, getBz(track.getXYZGlo()), maxSnp, maxStep, matCorr, tofInfo, signCorr) : PropagateToXBxByBz(track, linRef, x, maxSnp, maxStep, matCorr, tofInfo, signCorr); } template GPUd() bool propagateToAlphaX(track_T& track, value_type alpha, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, int minSteps = 1, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToAlphaX(TrackParCov_t& track, TrackPar_t* linRef, value_type alpha, value_type x, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, int minSteps = 1, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + + template + GPUd() bool propagateToR(track_T& track, value_type r, bool bzOnly = false, value_type maxSnp = MAX_SIN_PHI, value_type maxStep = MAX_STEP, + MatCorrType matCorr = MatCorrType::USEMatCorrLUT, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0) const; + GPUd() bool propagateToDCA(const o2::dataformats::VertexBase& vtx, o2::track::TrackParametrizationWithError& track, value_type bZ, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, o2::dataformats::DCA* dcaInfo = nullptr, track::TrackLTIntegral* tofInfo = nullptr, @@ -111,12 +146,12 @@ class PropagatorImpl GPUd() bool propagateToDCA(const o2::math_utils::Point3D& vtx, o2::track::TrackParametrization& track, value_type bZ, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, - gpu::gpustd::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, + std::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0, value_type maxD = 999.f) const; GPUd() bool propagateToDCABxByBz(const o2::math_utils::Point3D& vtx, o2::track::TrackParametrization& track, value_type maxStep = MAX_STEP, MatCorrType matCorr = MatCorrType::USEMatCorrLUT, - gpu::gpustd::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, + std::array* dca = nullptr, track::TrackLTIntegral* tofInfo = nullptr, int signCorr = 0, value_type maxD = 999.f) const; PropagatorImpl(PropagatorImpl const&) = delete; @@ -126,16 +161,18 @@ class PropagatorImpl // Bz at the origin GPUd() void updateField(); - GPUd() value_type getNominalBz() const { return mBz; } + GPUd() value_type getNominalBz() const { return mNominalBz; } GPUd() void setTGeoFallBackAllowed(bool v) { mTGeoFallBackAllowed = v; } GPUd() bool isTGeoFallBackAllowed() const { return mTGeoFallBackAllowed; } GPUd() void setMatLUT(const o2::base::MatLayerCylSet* lut) { mMatLUT = lut; } GPUd() const o2::base::MatLayerCylSet* getMatLUT() const { return mMatLUT; } GPUd() void setGPUField(const o2::gpu::GPUTPCGMPolynomialField* field) { mGPUField = field; } GPUd() const o2::gpu::GPUTPCGMPolynomialField* getGPUField() const { return mGPUField; } - GPUd() void setBz(value_type bz) { mBz = bz; } + GPUd() void setNominalBz(value_type bz) { mNominalBz = bz; } + GPUd() bool hasMagFieldSet() const { return mField != nullptr; } - GPUd() void estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const; + GPUd() value_type estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const; + GPUd() float estimateLTIncrement(const o2::track::TrackParametrization& trc, const o2::math_utils::Point3D& postStart, const o2::math_utils::Point3D& posEnd) const; #ifndef GPUCA_GPUCODE static PropagatorImpl* Instance(bool uninitialized = false) @@ -155,6 +192,10 @@ class PropagatorImpl GPUd() void getFieldXYZ(const math_utils::Point3D xyz, double* bxyz) const; + GPUd() float getBz(const math_utils::Point3D xyz) const; + + GPUd() double getBz(const math_utils::Point3D xyz) const; + private: #ifndef GPUCA_GPUCODE PropagatorImpl(bool uninitialized = false); @@ -163,10 +204,12 @@ class PropagatorImpl static constexpr value_type Epsilon = 0.00001; // precision of propagation to X template GPUd() void getFieldXYZImpl(const math_utils::Point3D xyz, T* bxyz) const; + template + GPUd() T getBzImpl(const math_utils::Point3D xyz) const; const o2::field::MagFieldFast* mFieldFast = nullptr; ///< External fast field map (barrel only for the moment) o2::field::MagneticField* mField = nullptr; ///< External nominal field map - value_type mBz = 0; ///< nominal field + value_type mNominalBz = 0; ///< nominal field bool mTGeoFallBackAllowed = true; ///< allow fall back to TGeo if requested MatLUT is not available const o2::base::MatLayerCylSet* mMatLUT = nullptr; // externally set LUT diff --git a/Detectors/Base/include/DetectorsBase/Ray.h b/Detectors/Base/include/DetectorsBase/Ray.h index 1701e65c2d638..a72208c41af0d 100644 --- a/Detectors/Base/include/DetectorsBase/Ray.h +++ b/Detectors/Base/include/DetectorsBase/Ray.h @@ -49,7 +49,7 @@ class Ray GPUd() Ray() : mP{0.f}, mD{0.f}, mDistXY2(0.f), mDistXY2i(0.f), mDistXYZ(0.f), mXDxPlusYDy(0.f), mXDxPlusYDyRed(0.f), mXDxPlusYDy2(0.f), mR02(0.f), mR12(0.f) { } - GPUdDefault() ~Ray() CON_DEFAULT; + GPUdDefault() ~Ray() = default; #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version Ray(const math_utils::Point3D point0, const math_utils::Point3D point1); @@ -74,8 +74,8 @@ class Ray GPUd() float getDist() const { return mDistXYZ; } GPUd() float getDist(float deltaT) const { return mDistXYZ * (deltaT > 0 ? deltaT : -deltaT); } - // for debud only - float getPos(float t, int i) const { return mP[i] + t * mD[i]; } + // for debug only + GPUd() float getPos(float t, int i) const { return mP[i] + t * mD[i]; } GPUd() float getPhi(float t) const { diff --git a/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h new file mode 100644 index 0000000000000..5ec85f1c14702 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ + +class TGeoShape; +class TGeoTessellated; + +namespace o2 +{ +namespace base +{ + +/// A few utility functions to operate on TGeo geometries (transformations, printing, ...) +class TGeoGeometryUtils +{ + public: + ///< Transform any (primitive) TGeoShape to a tessellated representation + static TGeoTessellated* TGeoShapeToTGeoTessellated(TGeoShape const*); +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/include/DetectorsBase/VMCSeederService.h b/Detectors/Base/include/DetectorsBase/VMCSeederService.h new file mode 100644 index 0000000000000..1669c73b39620 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/VMCSeederService.h @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_VMC_SEEDERSERVICE_H +#define ALICEO2_VMC_SEEDERSERVICE_H + +class TVirtualMC; + +#include + +namespace o2 +{ +namespace base +{ + +// A simple (singleton) service to propagate random number seeding +// to VMC engines in a loosely-coupled, just-in-time way (without needing to compile against these libraries). +class VMCSeederService +{ + + public: + static VMCSeederService const& instance() + { + static VMCSeederService inst; + return inst; + } + + void setSeed() const; // will propagate seed to the VMC engines + + typedef std::function SeederFcn; + + private: + VMCSeederService(); + void initSeederFunction(TVirtualMC const*); + + SeederFcn mSeederFcn; // the just-in-time compiled function talking to the VMC engines +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/src/BaseDPLDigitizer.cxx b/Detectors/Base/src/BaseDPLDigitizer.cxx index 173f3490ef560..3b3c5cfd477cb 100644 --- a/Detectors/Base/src/BaseDPLDigitizer.cxx +++ b/Detectors/Base/src/BaseDPLDigitizer.cxx @@ -16,6 +16,7 @@ #include #include #include +#include using namespace o2::base; @@ -54,6 +55,10 @@ void BaseDPLDigitizer::init(o2::framework::InitContext& ic) } } + // initialize the global ROOT random number generator (needed or not) + LOG(info) << "Initializing ROOT digitizer random with seed " << o2::conf::DigiParams::Instance().seed; + gRandom->SetSeed(o2::conf::DigiParams::Instance().seed); + // finally call specific init this->initDigitizerTask(ic); } diff --git a/Detectors/Base/src/CTFCoderBase.cxx b/Detectors/Base/src/CTFCoderBase.cxx index 49885bcc3dcdb..e29ca9e6d1afe 100644 --- a/Detectors/Base/src/CTFCoderBase.cxx +++ b/Detectors/Base/src/CTFCoderBase.cxx @@ -51,13 +51,13 @@ void CTFCoderBase::updateTimeDependentParams(ProcessingContext& pc, bool askTree setFirstTFOrbit(pc.services().get().firstTForbit); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mOpType == OpType::Decoder) { - pc.inputs().get("trigoffset"); // this is a configurable param + pc.inputs().get(mTrigOffsBinding); // this is a configurable param } if (mLoadDictFromCCDB) { if (askTree) { - pc.inputs().get("ctfdict"); // just to trigger the finaliseCCDB + pc.inputs().get(mDictBinding); // just to trigger the finaliseCCDB } else { - pc.inputs().get*>("ctfdict"); // just to trigger the finaliseCCDB + pc.inputs().get*>(mDictBinding); // just to trigger the finaliseCCDB } } } diff --git a/Detectors/Base/src/Detector.cxx b/Detectors/Base/src/Detector.cxx index e93295f1fc3f6..d2be9237f6f13 100644 --- a/Detectors/Base/src/Detector.cxx +++ b/Detectors/Base/src/Detector.cxx @@ -17,6 +17,7 @@ #include "DetectorsBase/MaterialManager.h" #include "DetectorsCommonDataFormats/DetID.h" #include "Field/MagneticField.h" +#include "Framework/TMessageSerializer.h" #include "TString.h" // for TString #include "TGeoManager.h" @@ -30,6 +31,7 @@ using namespace o2::base; using namespace o2::detectors; Float_t Detector::mDensityFactor = 1.0; +std::vector o2::base::Detector::sDetId2HitBitIndex{}; // initialize empty vector Detector::Detector() : FairDetector(), mMapMaterial(), mMapMedium() {} Detector::Detector(const char* name, Bool_t Active) @@ -169,6 +171,10 @@ void Detector::addAlignableVolumes() const LOG(warning) << "Alignable volumes are not yet defined for " << GetName(); } +void Detector::fillParallelWorld() const +{ +} + int Detector::registerSensitiveVolumeAndGetVolID(TGeoVolume const* vol) { // register this volume with FairRoot @@ -195,16 +201,19 @@ int Detector::registerSensitiveVolumeAndGetVolID(std::string const& name) #include #include #include -namespace o2 -{ -namespace base +namespace o2::base { // this goes into the source -void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, size_t size, - void (*free_func)(void* data, void* hint), void* hint) +void attachMessageBufferToParts(fair::mq::Parts& parts, fair::mq::Channel& channel, void* data, TClass* cl) { - std::unique_ptr message(channel.NewMessage(data, size, free_func, hint)); - parts.AddPart(std::move(message)); + auto msg = channel.Transport()->CreateMessage(4096, fair::mq::Alignment{64}); + // This will serialize the data directly into the message buffer, without any further + // buffer or copying. Notice how the message will have 8 bytes of header and then + // the serialized data as TBufferFile. In principle one could construct a serialized TMessage payload + // however I did not manage to get it to work for every case. + o2::framework::FairOutputTBuffer buffer(*msg); + o2::framework::TMessageSerializer::serialize(buffer, data, cl); + parts.AddPart(std::move(msg)); } void attachDetIDHeaderMessage(int id, fair::mq::Channel& channel, fair::mq::Parts& parts) { @@ -245,17 +254,14 @@ void* decodeShmCore(fair::mq::Parts& dataparts, int index, bool*& busy) void* decodeTMessageCore(fair::mq::Parts& dataparts, int index) { - class TMessageWrapper : public TMessage - { - public: - TMessageWrapper(void* buf, Int_t len) : TMessage(buf, len) { ResetBit(kIsOwner); } - ~TMessageWrapper() override = default; - }; auto rawmessage = std::move(dataparts.At(index)); - auto message = std::make_unique(rawmessage->GetData(), rawmessage->GetSize()); - return message.get()->ReadObjectAny(message.get()->GetClass()); + o2::framework::FairInputTBuffer buffer((char*)rawmessage->GetData(), rawmessage->GetSize()); + buffer.InitMap(); + auto* cl = buffer.ReadClass(); + buffer.SetBufferOffset(0); + buffer.ResetMap(); + return buffer.ReadObjectAny(cl); } -} // namespace base -} // namespace o2 +} // namespace o2::base ClassImp(o2::base::Detector); diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index f12f07321dcab..8255c143ebb4a 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -26,6 +26,7 @@ #pragma link C++ class o2::base::GeometryManager::MatBudgetExt + ; #pragma link C++ class o2::base::MaterialManager + ; #pragma link C++ class o2::MaterialManagerParam + ; +#pragma link C++ class o2::GeometryManagerParam + ; #pragma link C++ class o2::base::SimFieldUtils + ; #pragma link C++ class o2::base::Ray + ; @@ -36,6 +37,11 @@ #pragma link C++ class o2::base::Aligner + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::base::Aligner> + ; +#pragma link C++ class o2::GlobalParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::GlobalParams> + ; + #pragma link C++ class o2::data::Stack + ; +#pragma link C++ class o2::base::O2Tessellated - ; + #endif diff --git a/Detectors/Base/src/GRPGeomHelper.cxx b/Detectors/Base/src/GRPGeomHelper.cxx index a171e42a37298..e7e5248493548 100644 --- a/Detectors/Base/src/GRPGeomHelper.cxx +++ b/Detectors/Base/src/GRPGeomHelper.cxx @@ -36,7 +36,7 @@ using namespace o2::base; using namespace o2::framework; namespace o2d = o2::dataformats; -GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce, bool needPropD) +GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, bool GRPMagField, bool askMatLUT, GeomRequest geom, std::vector& inputs, bool askOnce, bool needPropD, std::string detMaskString) : askGRPECS(GRPECS), askGRPLHCIF(GRPLHCIF), askGRPMagField(GRPMagField), askMatLUT(askMatLUT), askTime(orbitResetTime), askOnceAllButField(askOnce), needPropagatorD(needPropD) { if (geom == Aligned) { @@ -48,9 +48,12 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, } if (geom == Alignments) { askAlignments = true; + o2::detectors::DetID::mask_t algDetMask = DetID::getMask(detMaskString); for (auto id = DetID::First; id <= DetID::Last; id++) { - std::string binding = fmt::format("align{}", DetID::getName(id)); - addInput({binding, DetID::getDataOrigin(id), "ALIGNMENT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/Align", DetID::getName(id)))}, inputs); + if (algDetMask[id]) { + std::string binding = fmt::format("align{}", DetID::getName(id)); + addInput({binding, DetID::getDataOrigin(id), "ALIGNMENT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/Align", DetID::getName(id)))}, inputs); + } } } if (askMatLUT) { @@ -60,7 +63,7 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, addInput({"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}, inputs); } if (askGRPECS) { - addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", true)}, inputs); // Run dependent !!! + addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", 1)}, inputs); // Run dependent !!! } if (askGRPLHCIF) { addInput({"grplhcif", "GLO", "GRPLHCIF", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPLHCIF")}, inputs); @@ -70,6 +73,22 @@ GRPGeomRequest::GRPGeomRequest(bool orbitResetTime, bool GRPECS, bool GRPLHCIF, } } +void GRPGeomRequest::requireAggregateRunInfo(std::vector& inputs) +{ + askAggregateRunInfo = true; + // require dependencies + if (!askGRPECS) { + askGRPECS = true; + addInput({"grpecs", "GLO", "GRPECS", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/GRPECS", 1)}, inputs); + } + if (!askTime) { + askTime = true; + addInput({"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}, inputs); + } + addInput({"RCTRunInfo", "RCT", "RunInfo", 0, Lifetime::Condition, ccdbParamSpec("RCT/Info/RunInformation", 2)}, inputs); + addInput({"CTPRunOrbit", "CTP", "RunOrbit", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/FirstRunOrbit")}, inputs); // TODO: should become run-depenendent (1) object +} + void GRPGeomRequest::addInput(const o2::framework::InputSpec&& isp, std::vector& inputs) { if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { @@ -121,8 +140,8 @@ bool GRPGeomHelper::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return true; } if (mRequest->askTime && matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { - mOrbitResetTimeMS = (*(std::vector*)obj)[0] / 1000; - LOG(info) << "orbit reset time updated to " << mOrbitResetTimeMS; + mOrbitResetTimeMUS = (*(std::vector*)obj)[0]; + LOG(info) << "orbit reset time updated to " << mOrbitResetTimeMUS; return true; } if (mRequest->askMatLUT && matcher == ConcreteDataMatcher("GLO", "MATLUT", 0)) { @@ -156,7 +175,7 @@ bool GRPGeomHelper::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return false; } -void GRPGeomHelper::checkUpdates(ProcessingContext& pc) const +void GRPGeomHelper::checkUpdates(ProcessingContext& pc) { // request input just to trigger finaliseCCDB if there was an update @@ -215,13 +234,21 @@ void GRPGeomHelper::checkUpdates(ProcessingContext& pc) const if (mRequest->askAlignments) { for (auto id = DetID::First; id <= DetID::Last; id++) { std::string binding = fmt::format("align{}", DetID::getName(id)); - if (pc.inputs().isValid(binding.c_str())) { - pc.inputs().get*>(binding); + if (pc.inputs().getPos(binding.c_str()) < 0) { + continue; } else { - return; + pc.inputs().get*>(binding); } } } + if (mRequest->askAggregateRunInfo) { + const auto hmap = pc.inputs().get("RCTRunInfo"); // metadata only! + auto rl = o2::ccdb::BasicCCDBManager::getRunDuration(hmap); + auto ctfFirstRunOrbitVec = pc.inputs().get*>("CTPRunOrbit"); + mAggregatedRunInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(pc.services().get().runNumber, rl.first, rl.second, mOrbitResetTimeMUS, mGRPECS, ctfFirstRunOrbitVec.get()); + LOGP(debug, "Extracted AggregateRunInfo: runNumber:{}, sor:{}, eor:{}, orbitsPerTF:{}, orbitReset:{}, orbitSOR:{}, orbitEOR:{}", + mAggregatedRunInfo.runNumber, mAggregatedRunInfo.sor, mAggregatedRunInfo.eor, mAggregatedRunInfo.orbitsPerTF, mAggregatedRunInfo.orbitReset, mAggregatedRunInfo.orbitSOR, mAggregatedRunInfo.orbitEOR); + } } } diff --git a/Detectors/Base/src/GeometryManager.cxx b/Detectors/Base/src/GeometryManager.cxx index 885cff7a73588..a067767752a69 100644 --- a/Detectors/Base/src/GeometryManager.cxx +++ b/Detectors/Base/src/GeometryManager.cxx @@ -12,8 +12,8 @@ /// \file GeometryManager.cxx /// \brief Implementation of the GeometryManager class -#include // for LOG -#include // for TIter +#include // for LOG +#include // for TIter #include #include // for TGeoHMatrix #include // for TGeoNode @@ -24,6 +24,7 @@ #include #include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/GeometryManagerParam.h" #include "DetectorsCommonDataFormats/AlignParam.h" #include "CommonUtils/NameConf.h" #include "DetectorsBase/Aligner.h" @@ -256,7 +257,7 @@ bool GeometryManager::applyAlignment(const std::vector(mFlatBufferPtr + offs)); @@ -193,20 +195,25 @@ void MatLayerCyl::optimizePhiSlices(float maxRelDiff) return; } int newSl = 0; + std::vector phi2SlNew(getNPhiBins()); + for (int i = 0; i < getNPhiBins(); i++) { + phi2SlNew[i] = mPhiBin2Slice[i]; + } for (int is = 1; is < getNPhiSlices(); is++) { if (!canMergePhiSlices(is - 1, is, maxRelDiff)) { newSl++; + } else { + mPhiBin2Slice[is] = mPhiBin2Slice[is - 1]; } - mPhiBin2Slice[is] = newSl; + phi2SlNew[is] = newSl; // new numbering } - LOG(info) << newSl + 1 << " slices out of " << getNPhiBins(); if (newSl + 1 == getNPhiSlices()) { return; } newSl = 0; int slMin = 0, slMax = 0, is = 0; while (is++ < getNPhiSlices()) { - while (is < getNPhiSlices() && mPhiBin2Slice[is] == newSl) { // select similar slices + while (is < getNPhiSlices() && phi2SlNew[is] == newSl) { // select similar slices slMax++; is++; } @@ -229,6 +236,9 @@ void MatLayerCyl::optimizePhiSlices(float maxRelDiff) newSl++; slMin = slMax = is; } + for (int i = 0; i < getNPhiBins(); i++) { + mPhiBin2Slice[i] = phi2SlNew[i]; + } mNPhiSlices = newSl; // relocate arrays to avoid spaces after optimization @@ -265,8 +275,8 @@ void MatLayerCyl::getMeanRMS(MatCell& mean, MatCell& rms) const rms.meanX2X0 /= nc; rms.meanRho -= mean.meanRho * mean.meanRho; rms.meanX2X0 -= mean.meanX2X0 * mean.meanX2X0; - rms.meanRho = rms.meanRho > 0.f ? std::sqrt(rms.meanRho) : 0.f; - rms.meanX2X0 = rms.meanX2X0 > 0.f ? std::sqrt(rms.meanX2X0) : 0.f; + rms.meanRho = rms.meanRho > 0.f ? o2::math_utils::sqrt(rms.meanRho) : 0.f; + rms.meanX2X0 = rms.meanX2X0 > 0.f ? o2::math_utils::sqrt(rms.meanX2X0) : 0.f; } //________________________________________________________________________________ @@ -309,9 +319,24 @@ void MatLayerCyl::flatten(char* newPtr) mConstructionMask = Constructed; } +//________________________________________________________________________________ +void MatLayerCyl::scale(float factor, bool _x2x0, bool _rho) +{ + LOGP(info, "Scaling layer {:.3f} #include "CommonUtils/TreeStreamRedirector.h" //#define _DBG_LOC_ // for local debugging only #endif // !GPUCA_ALIGPUCODE - +#undef NDEBUG using namespace o2::base; using flatObject = o2::gpu::FlatObject; @@ -90,7 +88,15 @@ void MatLayerCylSet::populateFromTGeo(int ntrPerCell) get()->mLayers[i].print(); get()->mLayers[i].populateFromTGeo(ntrPerCell); } + finalizeStructures(); +} + +//________________________________________________________________________________ +void MatLayerCylSet::finalizeStructures() +{ // build layer search structures + assert(mConstructionMask == InProgress); + int nlr = getNLayers(); int nR2Int = 2 * (nlr + 1); o2::gpu::resizeArray(get()->mR2Intervals, 0, nR2Int); o2::gpu::resizeArray(get()->mInterval2LrID, 0, nR2Int); @@ -102,7 +108,7 @@ void MatLayerCylSet::populateFromTGeo(int ntrPerCell) for (int i = 1; i < nlr; i++) { const auto& lr = getLayer(i); - if (std::sqrt(lr.getRMin2()) > std::sqrt(get()->mR2Intervals[nRIntervals] + Ray::Tiny)) { + if (o2::math_utils::sqrt(lr.getRMin2()) > o2::math_utils::sqrt(get()->mR2Intervals[nRIntervals] + Ray::Tiny)) { // register gap get()->mInterval2LrID[nRIntervals] = -1; get()->mR2Intervals[++nRIntervals] = lr.getRMin2(); @@ -169,6 +175,29 @@ void MatLayerCylSet::writeToFile(const std::string& outFName) outf.Close(); } +void MatLayerCylSet::initLayerVoxelLU() +{ + if (mInitializedLayerVoxelLU) { + LOG(info) << "Layer voxel already initialized; Aborting"; + return; + } + LOG(info) << "Initializing voxel layer lookup"; + // do some check if voxels are dimensioned correctly + if (LayerRMax < get()->mRMax) { + LOG(fatal) << "Cannot initialized layer voxel lookup due to dimension problem (fix constants in MatLayerCylSet.h)"; + } + for (int voxel = 0; voxel < NumVoxels; ++voxel) { + // check the 2 extremes of this voxel "covering" + const auto lowerR = voxel * VoxelRDelta; + const auto upperR = lowerR + VoxelRDelta; + const auto lowerSegment = searchSegment(lowerR * lowerR); + const auto upperSegment = searchSegment(upperR * upperR); + mLayerVoxelLU[2 * voxel] = lowerSegment; + mLayerVoxelLU[2 * voxel + 1] = upperSegment; + } + mInitializedLayerVoxelLU = true; +} + //________________________________________________________________________________ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) { @@ -182,7 +211,8 @@ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) LOG(error) << "Failed to load mat.LUT from " << inpFName; return nullptr; } - return rectifyPtrFromFile(mb); + auto rptr = rectifyPtrFromFile(mb); + return rptr; } //________________________________________________________________________________ @@ -192,6 +222,7 @@ MatLayerCylSet* MatLayerCylSet::rectifyPtrFromFile(MatLayerCylSet* ptr) if (ptr && !ptr->get()) { ptr->fixPointers(); } + ptr->initLayerVoxelLU(); return ptr; } @@ -225,6 +256,33 @@ void MatLayerCylSet::print(bool data) const float(getFlatBufferSize()) / 1024 / 1024); } +//________________________________________________________________________________ +void MatLayerCylSet::scaleLayersByID(int lrFrom, int lrTo, float factor, bool _x2x0, bool _rho) +{ + lrFrom = std::max(0, std::min(lrFrom, get()->mNLayers - 1)); + lrTo = std::max(0, std::min(lrTo, get()->mNLayers - 1)); + int dir = lrFrom >= lrTo ? -1 : 1; + lrTo += dir; + for (int i = lrFrom; i != lrTo; i += dir) { + get()->mLayers[i].scale(factor, _x2x0, _rho); + } +} + +//________________________________________________________________________________ +void MatLayerCylSet::scaleLayersByR(float rFrom, float rTo, float factor, bool _x2x0, bool _rho) +{ + if (rFrom > rTo) { + std::swap(rFrom, rTo); + } + Ray ray(std::max(getRMin(), rFrom), 0., 0., std::min(getRMax(), rTo), 0., 0.); + short lmin, lmax; + if (!getLayersRange(ray, lmin, lmax)) { + LOGP(warn, "No layers found for {} < r < {}", rFrom, rTo); + return; + } + scaleLayersByID(lmin, lmax, factor, _x2x0, _rho); +} + #endif //!GPUCA_ALIGPUCODE #ifndef GPUCA_GPUCODE @@ -259,10 +317,11 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa while (lrID >= lmin) { // go from outside to inside const auto& lr = getLayer(lrID); int nphiSlices = lr.getNPhiSlices(); - int nc = ray.crossLayer(lr); + int nc = ray.crossLayer(lr); // determines how many crossings this ray has with this tubular layer for (int ic = nc; ic--;) { float cross1, cross2; ray.getCrossParams(ic, cross1, cross2); // tmax,tmin of crossing the layer + auto phi0 = ray.getPhi(cross1), phi1 = ray.getPhi(cross2), dPhi = phi0 - phi1; auto phiID = lr.getPhiSliceID(phi0), phiIDLast = lr.getPhiSliceID(phi1); // account for eventual wrapping around 0 @@ -275,6 +334,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa phiID += nphiSlices; } } + int stepPhiID = phiID > phiIDLast ? -1 : 1; bool checkMorePhi = true; auto tStartPhi = cross1, tEndPhi = 0.f; @@ -382,21 +442,40 @@ GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& l return false; } int lmxInt, lmnInt; - lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; - lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + if (!mInitializedLayerVoxelLU) { + lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + } else { + lmxInt = rmax2 < getRMax2() ? searchLayerFast(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchLayerFast(rmin2, 0, lmxInt + 1) : 0; + } + const auto* interval2LrID = get()->mInterval2LrID; lmax = interval2LrID[lmxInt]; lmin = interval2LrID[lmnInt]; // make sure lmnInt and/or lmxInt are not in the gap if (lmax < 0) { - lmax = interval2LrID[--lmxInt]; // rmax2 is in the gap, take highest layer below rmax2 + lmax = interval2LrID[lmxInt - 1]; // rmax2 is in the gap, take highest layer below rmax2 } if (lmin < 0) { - lmin = interval2LrID[++lmnInt]; // rmin2 is in the gap, take lowest layer above rmin2 + lmin = interval2LrID[lmnInt + 1]; // rmin2 is in the gap, take lowest layer above rmin2 } return lmin <= lmax; // valid if both are not in the same gap } +GPUd() int MatLayerCylSet::searchLayerFast(float r2, int low, int high) const +{ + // we can avoid the sqrt .. at the cost of more memory in the lookup + const auto index = 2 * int(o2::gpu::CAMath::Sqrt(r2) * InvVoxelRDelta); + const auto layersfirst = mLayerVoxelLU[index]; + const auto layerslast = mLayerVoxelLU[index + 1]; + if (layersfirst != layerslast) { + // this means the voxel is undecided and we revert to search + return searchSegment(r2, layersfirst, layerslast + 1); + } + return layersfirst; +} + GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const { ///< search segment val belongs to. The val MUST be within the boundaries @@ -416,6 +495,7 @@ GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const } mid = (low + high) >> 1; } + return mid; } @@ -525,3 +605,55 @@ void MatLayerCylSet::fixPointers(char* oldPtr, char* newPtr, bool newPtrValid) } } #endif // !GPUCA_GPUCODE + +#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version + +MatLayerCylSet* MatLayerCylSet::extractCopy(float rmin, float rmax, float tolerance, const MatLayerCylSet* addTo) const +{ + // extract layers in the covering rmin-rmax range. If addTo is provided, simply substitute its layers by those from this + if (addTo && addTo->getNLayers() != getNLayers()) { + LOGP(fatal, "addTo has {} layers, this has {}", addTo->getNLayers(), getNLayers()); + } + Ray ray(std::max(getRMin(), rmin), 0., 0., std::min(getRMax(), rmax), 0., 0.); + short lmin, lmax; + if (!getLayersRange(ray, lmin, lmax)) { + LOGP(warn, "No layers found for {} < r < {}", rmin, rmax); + return nullptr; + } + LOGP(info, "Will extract layers {}:{} (out of {} layers) for {} < r < {}", lmin, lmax, getNLayers(), rmin, rmax); + MatLayerCylSet* copy = new MatLayerCylSet(); + int lrCount = 0, lrCounOld = 0, lrCountTot = 0; + auto addLr = [copy, &lrCountTot](const MatLayerCyl& lr) { + float drphi = lr.getDPhi() * (lr.getRMin() + lr.getRMax()) / 2. * 0.999; + copy->addLayer(lr.getRMin(), lr.getRMax(), lr.getZMax(), lr.getDZ(), drphi); + auto& lrNew = copy->getLayer(lrCountTot++); + for (int iz = 0; iz < lrNew.getNZBins(); iz++) { + for (int ip = 0; ip < lrNew.getNPhiBins(); ip++) { + lrNew.getCellPhiBin(ip, iz).set(lr.getCellPhiBin(ip, iz)); + } + } + }; + if (addTo) { + for (int il = 0; il < lmin; il++) { + addLr(addTo->getLayer(il)); + lrCounOld++; + } + } + for (int il = lmin; il <= lmax; il++) { + addLr(getLayer(il)); + lrCount++; + } + if (addTo) { + for (int il = lmax + 1; il < getNLayers(); il++) { + addLr(addTo->getLayer(il)); + lrCounOld++; + } + } + copy->finalizeStructures(); + copy->optimizePhiSlices(tolerance); + copy->flatten(); + LOGP(info, "Added layers {}:{} for {}second << " from material match"; + } + return iter->second; } - return o2::conf::SimMaterialParams::Instance().globalDensityFactor; + // density on module level + iter = mDensityMap.find(modname); + if (iter != mDensityMap.end()) { + if (debug) { + LOG(info) << "MatManager - " << modname << "/" << matname << " : applying density " << iter->second << " from module match"; + } + return iter->second; + } + // global factor + const auto global = o2::conf::SimMaterialParams::Instance().globalDensityFactor; + if (debug && global != 1.0) { + LOG(info) << "MatManager - " << modname << "/" << matname << " : applying global density " << iter->second; + } + return global; } void MaterialManager::Material(const char* modname, Int_t imat, const char* name, Float_t a, Float_t z, Float_t dens, Float_t radl, Float_t absl, Float_t* buf, Int_t nwbuf) { TString uniquename = modname; - auto densityFactor = getDensity(modname); + auto densityFactor = getDensity(modname, name); + uniquename.Append("_"); uniquename.Append(name); if (TVirtualMC::GetMC()) { @@ -173,7 +202,7 @@ void MaterialManager::Mixture(const char* modname, Int_t imat, const char* name, Int_t nlmat, Float_t* wmat) { TString uniquename = modname; - auto densityFactor = getDensity(modname); + auto densityFactor = getDensity(modname, name); uniquename.Append("_"); uniquename.Append(name); diff --git a/Detectors/Base/src/O2Tessellated.cxx b/Detectors/Base/src/O2Tessellated.cxx new file mode 100644 index 0000000000000..256a70e5a697a --- /dev/null +++ b/Detectors/Base/src/O2Tessellated.cxx @@ -0,0 +1,1509 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +// An implementation of TGeoTessellated augmented with efficient navigation functions. +// Asked for integration into ROOT here https://github.com/root-project/root/pull/21045 +// Will be deleted once we get this from ROOT. + +#include +#include + +#include "TGeoManager.h" +#include "TGeoMatrix.h" +#include "TGeoVolume.h" +#include "TVirtualGeoPainter.h" +#include "DetectorsBase/O2Tessellated.h" +#include "TBuffer3D.h" +#include "TBuffer3DTypes.h" +#include "TMath.h" +#include "TBuffer.h" + +#include +#include + +// THIS IS THIRD PARTY CODE (TO BE PUT IN ROOT) WHICH DOES NOT NEED TO ADHERE TO OUR LINTING +// NOLINTBEGIN + +// include the Third-party BVH headers +#include "bvh2_third_party.h" +// some kernels on top of BVH +#include "bvh2_extra_kernels.h" + +#include +#include + +using namespace o2::base; +ClassImp(O2Tessellated); + +using Vertex_t = Tessellated::Vertex_t; + +//////////////////////////////////////////////////////////////////////////////// +/// Compact consecutive equal vertices + +int TGeoFacet::CompactFacet(Vertex_t* vert, int nvertices) +{ + // Compact the common vertices and return new facet + if (nvertices < 2) + return nvertices; + int nvert = nvertices; + int i = 0; + while (i < nvert) { + if (vert[(i + 1) % nvert] == vert[i]) { + // shift last vertices left by one element + for (int j = i + 2; j < nvert; ++j) + vert[j - 1] = vert[j]; + nvert--; + } + i++; + } + return nvert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if a connected neighbour facet has compatible normal + +bool TGeoFacet::IsNeighbour(const TGeoFacet& other, bool& flip) const +{ + + // Find a connecting segment + bool neighbour = false; + int line1[2], line2[2]; + int npoints = 0; + for (int i = 0; i < fNvert; ++i) { + auto ivert = fIvert[i]; + // Check if the other facet has the same vertex + for (int j = 0; j < other.GetNvert(); ++j) { + if (ivert == other[j]) { + line1[npoints] = i; + line2[npoints] = j; + if (++npoints == 2) { + neighbour = true; + bool order1 = line1[1] == line1[0] + 1; + bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert(); + flip = (order1 == order2); + return neighbour; + } + } + } + } + return neighbour; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. In case nfacets is zero, it is user's responsibility to +/// call CloseShape once all faces are defined. + +O2Tessellated::O2Tessellated(const char* name, int nfacets) : TGeoBBox(name, 0, 0, 0) +{ + fNfacets = nfacets; + if (nfacets) + fFacets.reserve(nfacets); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor providing directly the array of vertices. Facets have to be added +/// providing vertex indices rather than coordinates. + +O2Tessellated::O2Tessellated(const char* name, const std::vector& vertices) : TGeoBBox(name, 0, 0, 0) +{ + fVertices = vertices; + fNvert = fVertices.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct from TGeoTessellated + +O2Tessellated::O2Tessellated(TGeoTessellated const& tsl, bool check) : TGeoBBox(tsl.GetName(), 0, 0, 0) +{ + fNfacets = tsl.GetNfacets(); + fNvert = tsl.GetNvertices(); + fNseg = tsl.GetNsegments(); + + // copy facet and vertex done + fVertices.reserve(fNvert); + fFacets.reserve(fNfacets); + for (int i = 0; i < fNfacets; ++i) { + fFacets.push_back(tsl.GetFacet(i)); + } + for (int i = 0; i < fNvert; ++i) { + fVertices.push_back(tsl.GetVertex(i)); + } + // finish remaining structures + CloseShape(check); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a vertex checking for duplicates, returning the vertex index + +int O2Tessellated::AddVertex(Vertex_t const& vert) +{ + constexpr double tolerance = 1.e-10; + auto vertexHash = [&](Vertex_t const& vertex) { + // Compute hash for the vertex + long hash = 0; + // helper function to generate hash from integer numbers + auto hash_combine = [](long seed, const long value) { + return seed ^ (std::hash{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + }; + for (int i = 0; i < 3; i++) { + // use tolerance to generate int with the desired precision from a real number for hashing + hash = hash_combine(hash, std::roundl(vertex[i] / tolerance)); + } + return hash; + }; + + auto hash = vertexHash(vert); + bool isAdded = false; + int ivert = -1; + // Get the compatible vertices + auto range = fVerticesMap.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + ivert = it->second; + if (fVertices[ivert] == vert) { + isAdded = true; + break; + } + } + if (!isAdded) { + ivert = fVertices.size(); + fVertices.push_back(vert); + fVerticesMap.insert(std::make_pair(hash, ivert)); + } + return ivert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + + Vertex_t vert[3]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + int nvert = TGeoFacet::CompactFacet(vert, 3); + if (nvert < 3) { + Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + int ind[3]; + for (auto i = 0; i < 3; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += 3; + fFacets.emplace_back(ind[0], ind[1], ind[2]); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 3; + fFacets.emplace_back(i0, i1, i2); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + Vertex_t vert[4]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + vert[3] = pt3; + int nvert = TGeoFacet::CompactFacet(vert, 4); + if (nvert < 3) { + Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + + int ind[4]; + for (auto i = 0; i < nvert; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += nvert; + if (nvert == 3) + fFacets.emplace_back(ind[0], ind[1], ind[2]); + else + fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + + if (fNfacets > 0 && GetNfacets() == fNfacets) + CloseShape(false); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2, int i3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 4; + fFacets.emplace_back(i0, i1, i2, i3); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute normal for a given facet + +Vertex_t O2Tessellated::FacetComputeNormal(int ifacet, bool& degenerated) const +{ + // Compute normal using non-zero segments + constexpr double kTolerance = 1.e-20; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + degenerated = true; + Vertex_t normal; + for (int i = 0; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]]; + if (e1.Mag2() < kTolerance) + continue; + for (int j = i + 1; j < nvert; ++j) { + Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]]; + if (e2.Mag2() < kTolerance) + continue; + normal = Vertex_t::Cross(e1, e2); + // e1 and e2 may be colinear + if (normal.Mag2() < kTolerance) + continue; + normal.Normalize(); + degenerated = false; + break; + } + if (!degenerated) + break; + } + return normal; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check validity of facet + +bool O2Tessellated::FacetCheck(int ifacet) const +{ + constexpr double kTolerance = 1.e-10; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + bool degenerated = true; + FacetComputeNormal(ifacet, degenerated); + if (degenerated) { + std::cout << "Facet: " << ifacet << " is degenerated\n"; + return false; + } + + // Compute surface area + double surfaceArea = 0.; + for (int i = 1; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i]] - fVertices[facet[0]]; + Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]]; + surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag(); + } + if (surfaceArea < kTolerance) { + std::cout << "Facet: " << ifacet << " has zero surface area\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Close the shape: calculate bounding box and compact vertices + +void O2Tessellated::CloseShape(bool check, bool fixFlipped, bool verbose) +{ + if (fIsClosed && fBVH) { + return; + } + // Compute bounding box + fDefined = true; + fNvert = fVertices.size(); + fNfacets = fFacets.size(); + ComputeBBox(); + + BuildBVH(); + if (fOutwardNormals.size() == 0) { + CalculateNormals(); + } else { + // short check if the normal container is of correct size + if (fOutwardNormals.size() != fFacets.size()) { + std::cerr << "Inconsistency in normal container"; + } + } + fIsClosed = true; + + // Cleanup the vertex map + std::multimap().swap(fVerticesMap); + + if (fVertices.size() > 0) { + if (!check) + return; + + // Check facets + for (auto i = 0; i < fNfacets; ++i) + FacetCheck(i); + + fClosedBody = CheckClosure(fixFlipped, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check closure of the solid and check/fix flipped normals + +bool O2Tessellated::CheckClosure(bool fixFlipped, bool verbose) +{ + int* nn = new int[fNfacets]; + bool* flipped = new bool[fNfacets]; + bool hasorphans = false; + bool hasflipped = false; + for (int i = 0; i < fNfacets; ++i) { + nn[i] = 0; + flipped[i] = false; + } + + for (int icrt = 0; icrt < fNfacets; ++icrt) { + // all neighbours checked? + if (nn[icrt] >= fFacets[icrt].GetNvert()) + continue; + for (int i = icrt + 1; i < fNfacets; ++i) { + bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]); + if (isneighbour) { + if (flipped[icrt]) + flipped[i] = !flipped[i]; + if (flipped[i]) + hasflipped = true; + nn[icrt]++; + nn[i]++; + if (nn[icrt] == fFacets[icrt].GetNvert()) + break; + } + } + if (nn[icrt] < fFacets[icrt].GetNvert()) + hasorphans = true; + } + + if (hasorphans && verbose) { + Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (nn[icrt] < fFacets[icrt].GetNvert()) + std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n"; + } + } + fClosedBody = !hasorphans; + int nfixed = 0; + if (hasflipped) { + if (verbose) + Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (flipped[icrt]) { + if (verbose) + std::cout << icrt << "\n"; + if (fixFlipped) { + fFacets[icrt].Flip(); + nfixed++; + } + } + } + if (nfixed && verbose) + Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed); + } + delete[] nn; + delete[] flipped; + + return !hasorphans; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute bounding box + +void O2Tessellated::ComputeBBox() +{ + const double kBig = TGeoShape::Big(); + double vmin[3] = {kBig, kBig, kBig}; + double vmax[3] = {-kBig, -kBig, -kBig}; + for (const auto& facet : fFacets) { + for (int i = 0; i < facet.GetNvert(); ++i) { + for (int j = 0; j < 3; ++j) { + vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j)); + vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j)); + } + } + } + fDX = 0.5 * (vmax[0] - vmin[0]); + fDY = 0.5 * (vmax[1] - vmin[1]); + fDZ = 0.5 * (vmax[2] - vmin[2]); + for (int i = 0; i < 3; ++i) + fOrigin[i] = 0.5 * (vmax[i] + vmin[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns numbers of vertices, segments and polygons composing the shape mesh. + +void O2Tessellated::GetMeshNumbers(int& nvert, int& nsegs, int& npols) const +{ + nvert = fNvert; + nsegs = fNseg; + npols = GetNfacets(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a TBuffer3D describing *this* shape. +/// Coordinates are in local reference frame. + +TBuffer3D* O2Tessellated::MakeBuffer3D() const +{ + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols); + if (buff) { + SetPoints(buff->fPnts); + SetSegsAndPols(*buff); + } + return buff; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Prints basic info + +void O2Tessellated::Print(Option_t*) const +{ + std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and " + << GetNfacets() << " facets\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills TBuffer3D structure for segments and polygons. + +void O2Tessellated::SetSegsAndPols(TBuffer3D& buff) const +{ + const int c = GetBasicColor(); + int* segs = buff.fSegs; + int* pols = buff.fPols; + + int indseg = 0; // segment internal data index + int indpol = 0; // polygon internal data index + int sind = 0; // segment index + for (const auto& facet : fFacets) { + auto nvert = facet.GetNvert(); + pols[indpol++] = c; + pols[indpol++] = nvert; + for (auto j = 0; j < nvert; ++j) { + int k = (j + 1) % nvert; + // segment made by next consecutive points + segs[indseg++] = c; + segs[indseg++] = facet[j]; + segs[indseg++] = facet[k]; + // add segment to current polygon and increment segment index + pols[indpol + nvert - j - 1] = sind++; + } + indpol += nvert; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points to an array. + +void O2Tessellated::SetPoints(double* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + vertex.CopyTo(&points[ind]); + ind += 3; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points in float. + +void O2Tessellated::SetPoints(Float_t* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + points[ind++] = vertex.x(); + points[ind++] = vertex.y(); + points[ind++] = vertex.z(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize the shape by scaling vertices within maxsize and center to origin + +void O2Tessellated::ResizeCenter(double maxsize) +{ + using Vector3_t = Vertex_t; + + if (!fDefined) { + Error("ResizeCenter", "Not all faces are defined"); + return; + } + Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]); + double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ); + double scale = maxsize / maxedge; + for (size_t i = 0; i < fVertices.size(); ++i) { + fVertices[i] = scale * (fVertices[i] - origin); + } + fOrigin[0] = fOrigin[1] = fOrigin[2] = 0; + fDX *= scale; + fDY *= scale; + fDZ *= scale; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills a static 3D buffer and returns a reference. + +const TBuffer3D& O2Tessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const +{ + static TBuffer3D buffer(TBuffer3DTypes::kGeneric); + + FillBuffer3D(buffer, reqSections, localFrame); + + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + + if (reqSections & TBuffer3D::kRawSizes) { + if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) { + buffer.SetSectionsValid(TBuffer3D::kRawSizes); + } + } + if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) { + SetPoints(buffer.fPnts); + if (!buffer.fLocalFrame) { + TransformPoints(buffer.fPnts, buffer.NbPnts()); + } + + SetSegsAndPols(buffer); + buffer.SetSectionsValid(TBuffer3D::kRaw); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reads a single tessellated solid from an .obj file. + +O2Tessellated* O2Tessellated::ImportFromObjFormat(const char* objfile, bool check, bool verbose) +{ + using std::vector, std::string, std::ifstream, std::stringstream, std::endl; + + vector vertices; + vector sfacets; + + struct FacetInd_t { + int i0 = -1; + int i1 = -1; + int i2 = -1; + int i3 = -1; + int nvert = 0; + FacetInd_t(int a, int b, int c) + { + i0 = a; + i1 = b; + i2 = c; + nvert = 3; + }; + FacetInd_t(int a, int b, int c, int d) + { + i0 = a; + i1 = b; + i2 = c; + i3 = d; + nvert = 4; + }; + }; + + vector facets; + // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. + // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; }; + + // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to + // 0. + // struct tex_t { double u; double v; double w; }; + + // List of vertex normals in (x,y,z) form; normals might not be unit vectors. + // struct vn_t { double x; double y; double z; }; + + // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement + // struct vp_t { double u; double v; double w; }; + + // Faces are defined using lists of vertex, texture and normal indices which start at 1. + // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices. + // f v1//vn1 v2//vn2 v3//vn3 ... + + // Records starting with the letter "l" specify the order of the vertices which build a polyline. + // l v1 v2 v3 v4 v5 v6 ... + + string line; + int ind[4] = {0}; + ifstream file(objfile); + if (!file.is_open()) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unable to open %s", objfile); + return nullptr; + } + + while (getline(file, line)) { + stringstream ss(line); + string tag; + + // We ignore everything which is not a vertex or a face + if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) { + // Decode the vertex + double pos[4] = {0, 0, 0, 1}; + ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3]; + vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]); + } + + else if (line.rfind('f', 0) == 0) { + // Decode the face + ss >> tag; + string word; + sfacets.clear(); + while (ss >> word) + sfacets.push_back(word); + if (sfacets.size() > 4 || sfacets.size() < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices", + sfacets.size()); + return nullptr; + } + int nvert = 0; + for (auto& sword : sfacets) { + stringstream ssword(sword); + string token; + getline(ssword, token, '/'); // just need the vertex index, which is the first token + // Convert string token to integer + + ind[nvert++] = stoi(token) - 1; + if (ind[nvert - 1] < 0) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s", + objfile); + return nullptr; + } + } + if (nvert == 3) + facets.emplace_back(ind[0], ind[1], ind[2]); + else + facets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + } + } + + int nvertices = (int)vertices.size(); + int nfacets = (int)facets.size(); + if (nfacets < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile); + return nullptr; + } + + string sobjfile(objfile); + if (verbose) + std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl; + + auto tsl = new O2Tessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices); + + for (int i = 0; i < nfacets; ++i) { + auto facet = facets[i]; + if (facet.nvert == 3) + tsl->AddFacet(facet.i0, facet.i1, facet.i2); + else + tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3); + } + tsl->CloseShape(check, true, verbose); + tsl->Print(); + return tsl; +} + +// implementation of some geometry helper functions in anonymous namespace +namespace +{ + +using Vertex_t = Tessellated::Vertex_t; +// The classic Moeller-Trumbore ray triangle-intersection kernel: +// - Compute triangle edges e1, e2 +// - Compute determinant det +// - Reject parallel rays +// - Compute barycentric coordinates u, v +// - Compute ray parameter t +double rayTriangle(const Vertex_t& orig, const Vertex_t& dir, const Vertex_t& v0, const Vertex_t& v1, + const Vertex_t& v2, double rayEPS = 1e-8) +{ + constexpr double EPS = 1e-8; + const double INF = std::numeric_limits::infinity(); + Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; + Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; + auto p = Vertex_t::Cross(dir, e2); + auto det = e1.Dot(p); + if (std::abs(det) <= EPS) { + return INF; + } + + Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]}; + auto invDet = 1.0 / det; + auto u = tvec.Dot(p) * invDet; + if (u < 0.0 || u > 1.0) { + return INF; + } + auto q = Vertex_t::Cross(tvec, e1); + auto v = dir.Dot(q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return INF; + } + auto t = e2.Dot(q) * invDet; + return (t > rayEPS) ? t : INF; +} + +template +struct Vec3f { + T x, y, z; +}; + +template +inline Vec3f operator-(const Vec3f& a, const Vec3f& b) +{ + return {a.x - b.x, a.y - b.y, a.z - b.z}; +} + +template +inline Vec3f cross(const Vec3f& a, const Vec3f& b) +{ + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; +} + +template +inline T dot(const Vec3f& a, const Vec3f& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +// Kernel to get closest/shortest distance between a point and a triangl (a,b,c). +// Performed by default in float since Safety can be approximate. +// Project point onto triangle plane +// If projection lies inside → distance to plane +// Otherwise compute min distance to the three edges +// Return squared distance +template +T pointTriangleDistSq(const Vec3f& p, const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + // Edges + Vec3f ab = b - a; + Vec3f ac = c - a; + Vec3f ap = p - a; + + auto d1 = dot(ab, ap); + auto d2 = dot(ac, ap); + if (d1 <= T(0.0) && d2 <= T(0.0)) { + return dot(ap, ap); // barycentric (1,0,0) + } + + Vec3f bp = p - b; + auto d3 = dot(ab, bp); + auto d4 = dot(ac, bp); + if (d3 >= T(0.0) && d4 <= d3) { + return dot(bp, bp); // (0,1,0) + } + + T vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + T v = d1 / (d1 - d3); + Vec3f proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AB + } + + Vec3f cp = p - c; + T d5 = dot(ab, cp); + T d6 = dot(ac, cp); + if (d6 >= T(0.0f) && d5 <= d6) { + return dot(cp, cp); // (0,0,1) + } + + T vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + T w = d2 / (d2 - d6); + Vec3f proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AC + } + + T va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + T w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + Vec3f proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)}; + Vec3f d = p - proj; + return dot(d, d); // edge BC + } + + // Inside face region + T denom = T(1.0f) / (va + vb + vc); + T v = vb * denom; + T w = vc * denom; + + Vec3f proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w}; + + Vec3f d = p - proj; + return dot(d, d); +} + +template +inline Vec3f normalize(const Vec3f& v) +{ + T len2 = dot(v, v); + if (len2 == T(0.0f)) { + std::cerr << "Degnerate triangle. Cannot determine normal"; + return {0, 0, 0}; + } + T invLen = T(1.0f) / std::sqrt(len2); + return {v.x * invLen, v.y * invLen, v.z * invLen}; +} + +template +inline Vec3f triangleNormal(const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + const Vec3f e1 = b - a; + const Vec3f e2 = c - a; + return normalize(cross(e1, e2)); +} + +} // end anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromOutside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t stepmax, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + const auto topnode_bbox = mybvh->get_root().get_bbox(); + if ((-point[0] + topnode_bbox.min[0]) > stepmax) { + return Big(); + } + if ((-point[1] + topnode_bbox.min[1]) > stepmax) { + return Big(); + } + if ((-point[2] + topnode_bbox.min[2]) > stepmax) { + return Big(); + } + if ((point[0] - topnode_bbox.max[0]) > stepmax) { + return Big(); + } + if ((point[1] - topnode_bbox.max[1]) > stepmax) { + return Big(); + } + if ((point[2] - topnode_bbox.max[2]) > stepmax) { + return Big(); + } + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + const auto& facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // quick normal test. Coming from outside, the dot product must be negative + if (n.Dot(dir_v) > 0.) { + continue; + } + + auto thisdist = rayTriangle(Vertex_t(point[0], point[1], point[2]), dir_v, + fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.); + + if (thisdist < local_step) { + local_step = thisdist; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromInside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t /*stepmax*/, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0., // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(dir_v) <= 0.) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = + rayTriangle(Vertex_t{point[0], point[1], point[2]}, dir_v, v0, v1, v2, 0.); + if (t < local_step) { + local_step = t; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Capacity + +Double_t O2Tessellated::Capacity() const +{ + // For explanation of the following algorithm see: + // https://en.wikipedia.org/wiki/Polyhedron#Volume + // http://wwwf.imperial.ac.uk/~rn/centroid.pdf + + double vol = 0.0; + for (size_t i = 0; i < fFacets.size(); ++i) { + auto& facet = fFacets[i]; + auto a = fVertices[facet[0]]; + auto b = fVertices[facet[1]]; + auto c = fVertices[facet[2]]; + vol += + a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]); + } + return vol / 6.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// BuildBVH + +void O2Tessellated::BuildBVH() +{ + using Scalar = float; + using BBox = bvh::v2::BBox; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // helper determining axis aligned bounding box from a facet; + auto GetBoundingBox = [this](TGeoFacet const& facet) { +#ifndef NDEBUG + const auto nvertices = facet.GetNvert(); + assert(nvertices == 3); // for now only triangles +#endif + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + BBox bbox; + bbox.min[0] = std::min(std::min(v1[0], v2[0]), v3[0]) - 0.001f; + bbox.min[1] = std::min(std::min(v1[1], v2[1]), v3[1]) - 0.001f; + bbox.min[2] = std::min(std::min(v1[2], v2[2]), v3[2]) - 0.001f; + bbox.max[0] = std::max(std::max(v1[0], v2[0]), v3[0]) + 0.001f; + bbox.max[1] = std::max(std::max(v1[1], v2[1]), v3[1]) + 0.001f; + bbox.max[2] = std::max(std::max(v1[2], v2[2]), v3[2]) + 0.001f; + return bbox; + }; + + // we need bounding boxes enclosing the primitives and centers of primitives + // (replaced here by centers of bounding boxes) to build the bvh + std::vector bboxes; + std::vector centers; + + // loop over all the triangles/Facets; + int nd = fFacets.size(); + for (int i = 0; i < nd; ++i) { + auto& facet = fFacets[i]; + + // fetch the bounding box of this node and add to the vector of bounding boxes + (bboxes).push_back(GetBoundingBox(facet)); + centers.emplace_back((bboxes).back().get_center()); + } + + // check if some previous object is registered and delete if necessary + if (fBVH) { + delete (Bvh*)fBVH; + fBVH = nullptr; + } + + // create the bvh + typename bvh::v2::DefaultBuilder::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + auto bvh = bvh::v2::DefaultBuilder::build(bboxes, centers, config); + auto bvhptr = new Bvh; + *bvhptr = std::move(bvh); // copy structure + fBVH = (void*)(bvhptr); + + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Contains + +bool O2Tessellated::Contains(Double_t const* point) const +{ + // we do the parity test + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return false; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + if (!TGeoBBox::Contains(point)) { + return false; + } + + // An arbitrary test direction. + // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat + // without evident symmetries. + Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + double local_step = Big(); + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + size_t crossings = 0; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto& facet = fFacets[objectid]; + + // for the parity test, we probe all crossing surfaces + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(Vertex_t(point[0], point[1], point[2]), + test_dir, v0, v1, v2, 0.); + + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return false; + }); + + return crossings & 1; +} + +namespace +{ + +// Helper classes/structs used for priority queue - BVH traversal +// structure keeping cost (value) for a BVH index +struct BVHPrioElement { + size_t bvh_node_id; + float value; +}; + +// A priority queue for BVHPrioElement with an additional clear method +// for quick reset. We intentionally derive from std::priority_queue here to expose a +// clear() convenience method via access to the protected container `c`. +// This is internal, non-polymorphic code and relies on standard-library +// implementation details that are stable across supported platforms. +template +class BVHPrioQueue : public std::priority_queue, Comparator> +{ + public: + using std::priority_queue, + Comparator>::priority_queue; // constructor inclusion + + // convenience method to quickly clear/reset the queue (instead of having to pop one by one) + void clear() { this->c.clear(); } +}; + +} // namespace + +/// a reusable safety kernel, which optionally returns the closest face +template +inline Double_t O2Tessellated::SafetyKernel(const Double_t* point, bool in, int* closest_facet_id) const +{ + // This is the classic traversal/pruning of a BVH based on priority queue search + + float smallest_safety_sq = TGeoShape::Big(); + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + + // testpoint object in float for quick BVH interaction + Vec3 testpoint(point[0], point[1], point[2]); + + auto currnode = mybvh->nodes[0]; // we start from the top BVH node + // we do a quick check on the top node (in case we are outside shape) + bool outside_top = false; + if (!in) { + outside_top = !bvh::v2::extra::contains(currnode.get_bbox(), testpoint); + if (outside_top) { + const auto safety_sq_to_top = bvh::v2::extra::SafetySqToNode(currnode.get_bbox(), testpoint); + // we simply return safety to the outer bounding box as an estimate + return std::sqrt(safety_sq_to_top); + } + } + + // comparator bringing out "smallest" value on top + auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; }; + static thread_local BVHPrioQueue queue(cmp); + queue.clear(); + + // algorithm is based on standard iterative tree traversal with priority queues + float current_safety_to_node_sq = 0.f; + + if (returnFace) { + *closest_facet_id = -1; + } + + do { + if (currnode.is_leaf()) { + // we are in a leaf node and actually talk to a face/triangular primitive + const auto begin_prim_id = currnode.index.first_id(); + const auto end_prim_id = begin_prim_id + currnode.index.prim_count(); + + for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) { + const auto object_id = mybvh->prim_ids[p_id]; + + const auto& facet = fFacets[object_id]; + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + + auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{v1[0], v1[1], v1[2]}, + Vec3f{v2[0], v2[1], v2[2]}, Vec3f{v3[0], v3[1], v3[2]}); + + if (thissafetySQ < smallest_safety_sq) { + smallest_safety_sq = thissafetySQ; + if (returnFace) { + *closest_facet_id = object_id; + } + } + } + } else { + // not a leave node ... for further traversal, + // we inject the children into priority queue based on distance to it's bounding box + const auto leftchild_id = currnode.index.first_id(); + const auto rightchild_id = leftchild_id + 1; + + for (size_t childid : {leftchild_id, rightchild_id}) { + if (childid >= mybvh->nodes.size()) { + continue; + } + + const auto& node = mybvh->nodes[childid]; + const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint); + + if (inside) { + // this must be further considered because we are inside the bounding box + queue.push(BVHPrioElement{childid, -1.}); + } else { + auto safety_to_node_square = bvh::v2::extra::SafetySqToNode(node.get_bbox(), testpoint); + if (safety_to_node_square <= smallest_safety_sq) { + // this should be further considered + queue.push(BVHPrioElement{childid, safety_to_node_square}); + } + } + } + } + + if (queue.size() > 0) { + auto currElement = queue.top(); + currnode = mybvh->nodes[currElement.bvh_node_id]; + current_safety_to_node_sq = currElement.value; + queue.pop(); + } else { + break; + } + } while (current_safety_to_node_sq <= smallest_safety_sq); + + return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Safety + +Double_t O2Tessellated::Safety(const Double_t* point, Bool_t in) const +{ + // we could use some caching here (in future) since queries to the solid will likely + // be made with some locality + + // fall-back to precise safety kernel + return SafetyKernel(point, in); +} + +//////////////////////////////////////////////////////////////////////////////// +/// ComputeNormal interface + +void O2Tessellated::ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const +{ + // We take the approach to identify closest facet to the point via safety + // and returning the normal from this face. + + // TODO: Before doing that we could check for cached points from other queries + + // use safety kernel + int closest_face_id = -1; + SafetyKernel(point, true, &closest_face_id); + + if (closest_face_id < 0) { + norm[0] = 1.; + norm[1] = 0.; + norm[2] = 0.; + return; + } + + const auto& n = fOutwardNormals[closest_face_id]; + norm[0] = n[0]; + norm[1] = n[1]; + norm[2] = n[2]; + + // change sign depending on dir + if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) { + norm[0] = -norm[0]; + norm[1] = -norm[1]; + norm[2] = -norm[2]; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromInside function + +Double_t O2Tessellated::DistFromInside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(d) <= 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromOutside function + +Double_t O2Tessellated::DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from outside, the dot product must be negative) + if (n.Dot(d) > 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) Contains + +bool O2Tessellated::Contains_Loop(const Double_t* point) const +{ + // Fixed ray direction + const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + Vertex_t p(point[0], point[1], point[2]); + + int crossings = 0; + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, test_dir, v0, v1, v2, 0.); + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return (crossings & 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Custom streamer which performs Closing on read. +/// Recalculation of BVH and normals is fast + +void O2Tessellated::Streamer(TBuffer& b) +{ + if (b.IsReading()) { + b.ReadClassBuffer(O2Tessellated::Class(), this); + CloseShape(false); // close shape but do not re-perform checks + } else { + b.WriteClassBuffer(O2Tessellated::Class(), this); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the normals + +void O2Tessellated::CalculateNormals() +{ + fOutwardNormals.clear(); + for (auto& facet : fFacets) { + auto& v1 = fVertices[facet[0]]; + auto& v2 = fVertices[facet[1]]; + auto& v3 = fVertices[facet[2]]; + using Vec3d = Vec3f; + auto norm = triangleNormal(Vec3d{v1[0], v1[1], v1[2]}, Vec3d{v2[0], v2[1], v2[2]}, Vec3d{v3[0], v3[1], v3[2]}); + fOutwardNormals.emplace_back(Vertex_t{norm.x, norm.y, norm.z}); + } +} + +// NOLINTEND \ No newline at end of file diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index cdc301a47cb6b..a5983cab8e257 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -15,6 +15,7 @@ #include "GPUCommonMath.h" #include "GPUTPCGMPolynomialField.h" #include "MathUtils/Utils.h" +#include "ReconstructionDataFormats/HelixHelper.h" #include "ReconstructionDataFormats/Vertex.h" using namespace o2::base; @@ -62,9 +63,9 @@ void PropagatorImpl::updateField() } const value_type xyz[3] = {0.}; if (mFieldFast) { - mFieldFast->GetBz(xyz, mBz); + mFieldFast->GetBz(xyz, mNominalBz); } else { - mBz = mField->GetBz(xyz[0], xyz[1], xyz[2]); + mNominalBz = mField->GetBz(xyz[0], xyz[1], xyz[2]); } } @@ -170,7 +171,7 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va signCorr = -dir; // sign of eloss correction is not imposed } - gpu::gpustd::array b{}; + std::array b{}; while (math_utils::detail::abs(dx) > Epsilon) { auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); if (dir < 0) { @@ -180,28 +181,105 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va auto xyz0 = track.getXYZGlo(); getFieldXYZ(xyz0, &b[0]); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateTo(x, b)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { - return false; - } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // taking into account all the three components of the magnetic field + // and correcting for the crossed material. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + std::array b{}; + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); + getFieldXYZ(xyz0, &b[0]); + + auto correct = [&track, &linRef, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = linRef.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(linRef, mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, linRef.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = linRef.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), linRef.getQ2P2()); } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + return res; + }; + + if (!track.propagateTo(x, linRef, b)) { + return false; + } + if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -231,7 +309,7 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value signCorr = -dir; // sign of eloss correction is not imposed } - gpu::gpustd::array b{}; + std::array b{}; while (math_utils::detail::abs(dx) > Epsilon) { auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); if (dir < 0) { @@ -241,27 +319,36 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value auto xyz0 = track.getXYZGlo(); getFieldXYZ(xyz0, &b[0]); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateParamTo(x, b)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - if (!track.correctForELoss(((signCorr < 0) ? -mb.length : mb.length) * mb.meanRho)) { - return false; - } - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); - } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -277,8 +364,7 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty //---------------------------------------------------------------- // // Propagates the track to the plane X=xk (cm) - // taking into account all the three components of the magnetic field - // and correcting for the crossed material. + // Use bz only and correct for the crossed material. // // maxStep - maximal step for propagation // tofInfo - optional container for track length and PID-dependent TOF integration @@ -298,30 +384,101 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty } auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); - + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; if (!track.propagateTo(x, bZ)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - // - if (!track.correctForMaterial(mb.meanX2X0, mb.getXRho(signCorr))) { - return false; - } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type bZ, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // Use bz only and correct for the crossed material if requested. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); + + auto correct = [&track, &linRef, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = linRef.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForMaterial(linRef, mb.meanX2X0, mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, linRef.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = linRef.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), linRef.getQ2P2()); } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + return res; + }; + + if (!track.propagateTo(x, linRef, bZ)) { // linRef also updated + return false; + } + if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; } dx = xToGo - track.getX(); } @@ -359,34 +516,153 @@ GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type auto x = track.getX() + step; auto xyz0 = track.getXYZGlo(); + auto correct = [&track, &xyz0, tofInfo, matCorr, signCorr, this]() { + bool res = true; + if (matCorr != MatCorrType::USEMatCorrNONE) { + auto xyz1 = track.getXYZGlo(); + auto mb = this->getMatBudget(matCorr, xyz0, xyz1); + if (!track.correctForELoss(mb.getXRho(signCorr))) { + res = false; + } + if (tofInfo) { + tofInfo->addStep(mb.length, track.getQ2P2()); // fill L,ToF info using already calculated step length + tofInfo->addX2X0(mb.meanX2X0); + tofInfo->addXRho(mb.getXRho(signCorr)); + } + } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght + auto xyz1 = track.getXYZGlo(); + math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); + } + return res; + }; + if (!track.propagateParamTo(x, bZ)) { return false; } if (maxSnp > 0 && math_utils::detail::abs(track.getSnp()) >= maxSnp) { + correct(); + return false; + } + if (!correct()) { + return false; + } + dx = xToGo - track.getX(); + } + track.setX(xToGo); + return true; +} + +//_______________________________________________________________________ +template +template +GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, bool bzOnly, value_type maxSnp, value_type maxStep, + MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + const value_T MaxPhiLoc = math_utils::detail::asin(maxSnp), MaxPhiLocSafe = 0.95 * MaxPhiLoc; + auto bz = getNominalBz(); + if (math_utils::detail::abs(bz) > constants::math::Almost0) { + o2::track::TrackAuxPar traux(track, bz); + o2::track::TrackAuxPar crad; + value_type r0 = math_utils::detail::sqrt(track.getX() * track.getX() + track.getY() * track.getY()); + value_type dr = (r - r0); + value_type rTmp = r - (math_utils::detail::abs(dr) > 1. ? (dr > 0 ? 0.5 : -0.5) : 0.5 * dr); // 1st propagate a few mm short of the targer R + crad.rC = rTmp; + crad.c = crad.cc = 1.f; + crad.s = crad.ss = crad.cs = 0.f; + o2::track::CrossInfo cross; + cross.circlesCrossInfo(crad, traux, 0.); + if (cross.nDCA < 1) { return false; } - if (matCorr != MatCorrType::USEMatCorrNONE) { - auto xyz1 = track.getXYZGlo(); - auto mb = getMatBudget(matCorr, xyz0, xyz1); - // - if (!track.correctForELoss(mb.getXRho(signCorr))) { + double phiCross[2] = {}, dphi[2] = {}; + auto curv = track.getCurvature(bz); + bool clockwise = curv < 0; // q+ in B+ or q- in B- goes clockwise + auto phiLoc = math_utils::detail::asin(track.getSnp()); + auto phi0 = phiLoc + track.getAlpha(); + o2::math_utils::detail::bringTo02Pi(phi0); + for (int i = 0; i < cross.nDCA; i++) { + // track pT direction angle at crossing points: + // == angle of the tangential to track circle at the crossing point X,Y + // == normal to the radial vector from the track circle center {X-cX, Y-cY} + // i.e. the angle of the vector {Y-cY, -(X-cx)} + auto normX = double(cross.yDCA[i]) - double(traux.yC), normY = -(double(cross.xDCA[i]) - double(traux.xC)); + if (!clockwise) { + normX = -normX; + normY = -normY; + } + phiCross[i] = math_utils::detail::atan2(normY, normX); + o2::math_utils::detail::bringTo02Pi(phiCross[i]); + dphi[i] = phiCross[i] - phi0; + if (dphi[i] > o2::constants::math::PI) { + dphi[i] -= o2::constants::math::TwoPI; + } else if (dphi[i] < -o2::constants::math::PI) { + dphi[i] += o2::constants::math::TwoPI; + } + } + int sel = cross.nDCA == 1 ? 0 : (clockwise ? (dphi[0] < dphi[1] ? 0 : 1) : (dphi[1] < dphi[0] ? 0 : 1)); + auto deltaPhi = dphi[sel]; + + while (1) { + auto phiLocFin = phiLoc + deltaPhi; + // case1 + if (math_utils::detail::abs(phiLocFin) < MaxPhiLocSafe) { // just 1 step propagation + auto deltaX = (math_utils::detail::sin(phiLocFin) - track.getSnp()) / track.getCurvature(bz); + if (!track.propagateTo(track.getX() + deltaX, bz)) { + return false; + } + break; + } + if (math_utils::detail::abs(deltaPhi) < (2 * MaxPhiLocSafe)) { // still can go in 1 step with one extra rotation + auto rot = phiLoc + 0.5 * deltaPhi; + if (!track.rotate(track.getAlpha() + rot)) { + return false; + } + phiLoc -= rot; + continue; // should be ok for the case 1 now. + } + + auto rot = phiLoc + (deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe); + if (!track.rotate(track.getAlpha() + rot)) { return false; } + phiLoc -= rot; // = +- MaxPhiLocSafe - if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length - tofInfo->addX2X0(mb.meanX2X0); - tofInfo->addXRho(mb.getXRho(signCorr)); + // propagate to phiLoc = +-MaxPhiLocSafe + auto tgtPhiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + auto deltaX = (math_utils::detail::sin(tgtPhiLoc) - track.getSnp()) / track.getCurvature(bz); + if (!track.propagateTo(track.getX() + deltaX, bz)) { + return false; } - } else if (tofInfo) { // if tofInfo filling was requested w/o material correction, we need to calculate the step lenght - auto xyz1 = track.getXYZGlo(); - math_utils::Vector3D stepV(xyz1.X() - xyz0.X(), xyz1.Y() - xyz0.Y(), xyz1.Z() - xyz0.Z()); - tofInfo->addStep(stepV.R(), track.getP2Inv()); + deltaPhi -= tgtPhiLoc - phiLoc; + phiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + continue; // should be of for the case 1 now. } - dx = xToGo - track.getX(); + bz = getBz(math_utils::Point3D{value_type(cross.xDCA[sel]), value_type(cross.yDCA[sel]), value_type(track.getZ())}); } - track.setX(xToGo); - return true; + // do final step till target R, also covers Bz = 0; + value_type xfin; + if (!track.getXatLabR(r, xfin, bz)) { + return false; + } + return propagateToX(track, xfin, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr); +} + +template +GPUd() bool PropagatorImpl::propagateToAlphaX(TrackParCov_t& track, TrackPar_t* linRef, value_type alpha, value_type x, bool bzOnly, value_type maxSnp, value_type maxStep, int minSteps, + MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + // propagate to alpha,X, if needed in a few steps + auto snp = track.getSnpAt(alpha, x, getNominalBz()); + // apply safety factor 0.9 for crude rotation estimate + if (math_utils::detail::abs(snp) < maxSnp * 0.9 && (linRef ? track.rotate(alpha, *linRef, getNominalBz()) : track.rotate(alpha))) { + auto dx = math_utils::detail::abs(x - track.getX()); + if (dx < Epsilon) { + return true; + } + return propagateTo(track, linRef, x, bzOnly, maxSnp, math_utils::detail::min(dx / minSteps, maxStep), matCorr, tofInfo, signCorr); + } + return false; } //_______________________________________________________________________ @@ -439,6 +715,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } value_type crv = track.getCurvature(bZ); @@ -459,6 +739,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const o2::dataformats::Verte #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -488,9 +772,13 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } - value_type crv = track.getCurvature(mBz); + value_type crv = track.getCurvature(mNominalBz); value_type tgfv = -(crv * x - snp) / (crv * y + csp); sn = tgfv / math_utils::detail::sqrt(1.f + tgfv * tgfv); cs = math_utils::detail::sqrt((1. - sn) * (1. + sn)); @@ -508,6 +796,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: #elif !defined(GPUCA_NO_FMT) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx; #endif + if (dca) { // provide default DCA for failed propag + dca->set(o2::track::DefaultDCA, o2::track::DefaultDCA, + o2::track::DefaultDCACov, o2::track::DefaultDCACov, o2::track::DefaultDCACov); + } return false; } track = tmpT; @@ -524,7 +816,7 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const o2::dataformats: template GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D& vtx, TrackPar_t& track, value_type bZ, value_type maxStep, PropagatorImpl::MatCorrType matCorr, - gpu::gpustd::array* dca, track::TrackLTIntegral* tofInfo, + std::array* dca, track::TrackLTIntegral* tofInfo, int signCorr, value_type maxD) const { // propagate track to DCA to the vertex @@ -537,6 +829,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } value_type crv = track.getCurvature(bZ); @@ -558,6 +854,10 @@ GPUd() bool PropagatorImpl::propagateToDCA(const math_utils::Point3D::propagateToDCA(const math_utils::Point3D GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Point3D& vtx, TrackPar_t& track, value_type maxStep, PropagatorImpl::MatCorrType matCorr, - gpu::gpustd::array* dca, track::TrackLTIntegral* tofInfo, + std::array* dca, track::TrackLTIntegral* tofInfo, int signCorr, value_type maxD) const { // propagate track to DCA to the vertex @@ -585,9 +885,13 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin // Estimate the impact parameter neglecting the track curvature value_type d = math_utils::detail::abs(x * snp - y * csp); if (d > maxD) { + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } - value_type crv = track.getCurvature(mBz); + value_type crv = track.getCurvature(mNominalBz); value_type tgfv = -(crv * x - snp) / (crv * y + csp); sn = tgfv / math_utils::detail::sqrt(1.f + tgfv * tgfv); cs = math_utils::detail::sqrt((1. - sn) * (1. + sn)); @@ -606,6 +910,10 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin #else LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << " for vertex " << vtx.X() << ' ' << vtx.Y() << ' ' << vtx.Z(); #endif + if (dca) { // provide default DCA for failed propag + (*dca)[0] = o2::track::DefaultDCA; + (*dca)[1] = o2::track::DefaultDCA; + } return false; } track = tmpT; @@ -618,7 +926,28 @@ GPUd() bool PropagatorImpl::propagateToDCABxByBz(const math_utils::Poin //____________________________________________________________ template -GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const +GPUd() float PropagatorImpl::estimateLTIncrement(const o2::track::TrackParametrization& trc, + const o2::math_utils::Point3D& posStart, + const o2::math_utils::Point3D& posEnd) const +{ + // estimate helical step increment between 2 point + float dX = posEnd.X() - posStart.X(), dY = posEnd.Y() - posStart.Y(), dZ = posEnd.Z() - posStart.Z(), d2XY = dX * dX + dY * dY; + if (getNominalBz() != 0) { // circular arc = 2*R*asin(dXY/2R) + float b[3]; + o2::math_utils::Point3D posAv(0.5 * (posEnd.X() + posStart.X()), 0.5 * (posEnd.Y() + posStart.Y()), 0.5 * (posEnd.Z() + posStart.Z())); + getFieldXYZ(posAv, b); + float curvH = math_utils::detail::abs(0.5f * trc.getCurvature(b[2])), asArg = curvH * math_utils::detail::sqrt(d2XY); + if (curvH > 0.f) { + d2XY = asArg < 1.f ? math_utils::detail::asin(asArg) / curvH : o2::constants::math::PIHalf / curvH; + d2XY *= d2XY; + } + } + return math_utils::detail::sqrt(d2XY + dZ * dZ); +} + +//____________________________________________________________ +template +GPUd() value_T PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const { value_T xdca = 0., ydca = 0., length = 0.; // , zdca = 0. // zdca might be used in future o2::math_utils::CircleXY c; @@ -637,7 +966,7 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& return math_utils::detail::abs(trc.getY() * math_utils::detail::sqrt(1. + trc.getTgl() * trc.getTgl())); // distance from the current point to DCA } }; - trc.getCircleParamsLoc(mBz, c); + trc.getCircleParamsLoc(mNominalBz, c); if (c.rC != 0.) { // helix auto distC = math_utils::detail::sqrt(c.getCenterD2()); // distance from the circle center to origin if (distC > 1.e-3) { @@ -648,7 +977,7 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& auto angcos = (v0x * v1x + v0y * v1y) / (c.rC * c.rC); if (math_utils::detail::abs(angcos) < 1.f) { auto ang = math_utils::detail::acos(angcos); - if ((trc.getSign() > 0.f) == (mBz > 0.f)) { + if ((trc.getSign() > 0.f) == (mNominalBz > 0.f)) { ang = -ang; // we need signeg angle c.rC = -c.rC; // we need signed curvature for zdca } @@ -665,8 +994,10 @@ GPUd() void PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegral& length = straigh_line_approx(); } // since we assume the track or its parent comes from the beam-line or decay, add XY(?) distance to it - length += math_utils::detail::sqrt(xdca * xdca + ydca * ydca); - lt.addStep(length, trc.getP2Inv()); + value_T dcaT = math_utils::detail::sqrt(xdca * xdca + ydca * ydca); + length += dcaT; + lt.addStep(length, trc.getQ2P2()); + return dcaT; } //____________________________________________________________ @@ -720,6 +1051,35 @@ GPUd() void PropagatorImpl::getFieldXYZImpl(const math_utils::Point3D +template +GPUd() T PropagatorImpl::getBzImpl(const math_utils::Point3D xyz) const +{ + T bz = 0; + if (mGPUField) { +#if defined(GPUCA_GPUCODE_DEVICE) && defined(GPUCA_HAS_GLOBAL_SYMBOL_CONSTANT_MEM) + const auto* f = &GPUCA_CONSMEM.param.polynomialField; // Access directly from constant memory on GPU (copied here to avoid complicated header dependencies) +#else + const auto* f = mGPUField; +#endif + constexpr value_type kCLight1 = 1. / o2::gpu::gpu_common_constants::kCLight; + bz = f->GetFieldBz(xyz.X(), xyz.Y(), xyz.Z()) * kCLight1; + } else { +#ifndef GPUCA_GPUCODE + if (mFieldFast) { + mFieldFast->GetBz(xyz, bz); // Must not call the host-only function in GPU compilation + } else { +#ifdef GPUCA_STANDALONE + LOG(fatal) << "Normal field cannot be used in standalone benchmark"; +#else + bz = mField->GetBz(xyz.X(), xyz.Y(), xyz.Z()); +#endif + } +#endif + } + return bz; +} + template GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D xyz, float* bxyz) const { @@ -732,18 +1092,30 @@ GPUd() void PropagatorImpl::getFieldXYZ(const math_utils::Point3D(xyz, bxyz); } +template +GPUd() float PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + +template +GPUd() double PropagatorImpl::getBz(const math_utils::Point3D xyz) const +{ + return getBzImpl(xyz); +} + namespace o2::base { +#if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code. template class PropagatorImpl; -#ifndef GPUCA_GPUCODE_DEVICE -template class PropagatorImpl; +template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; +template bool GPUdni() PropagatorImpl::propagateToR::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, bool, float, float, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; #endif -#ifndef __HIPCC__ // TODO: Fixme: must prevent HIP from compiling this, should file bug report -template bool GPUd() PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; -template bool GPUd() PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, float, float, bool, float, float, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; -#ifndef GPUCA_GPUCODE_DEVICE +#ifndef GPUCA_GPUCODE +template class PropagatorImpl; template bool PropagatorImpl::propagateToAlphaX::TrackPar_t>(PropagatorImpl::TrackPar_t&, double, double, bool, double, double, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; template bool PropagatorImpl::propagateToAlphaX::TrackParCov_t>(PropagatorImpl::TrackParCov_t&, double, double, bool, double, double, int, PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral*, int) const; #endif -#endif } // namespace o2::base diff --git a/Detectors/Base/src/Stack.cxx b/Detectors/Base/src/Stack.cxx index 2a39b9574793d..de69c866e7b82 100644 --- a/Detectors/Base/src/Stack.cxx +++ b/Detectors/Base/src/Stack.cxx @@ -15,6 +15,7 @@ #include "DetectorsBase/Stack.h" #include "DetectorsBase/Detector.h" +#include #include "DetectorsCommonDataFormats/DetID.h" #include "SimulationDataFormat/MCTrack.h" @@ -82,7 +83,6 @@ Stack::Stack(Int_t size) } auto& param = o2::sim::StackParam::Instance(); - LOG(info) << param; TransportFcn transportPrimary; if (param.transportPrimary.compare("none") == 0) { transportPrimary = [](const TParticle& p, const std::vector& particles) { @@ -362,6 +362,7 @@ TParticle* Stack::PopNextTrack(Int_t& iTrack) // LOG(info) << "SEEDING NEW TRACK USING HASH" << hash; // init seed per track gRandom->SetSeed(hash); + o2::base::VMCSeederService::instance().setSeed(); // NOTE: THE BETTER PLACE WOULD BE IN PRETRACK HOOK BUT THIS DOES NOT SEEM TO WORK // WORKS ONLY WITH G3 SINCE G4 DOES NOT CALL THIS FUNCTION } // .trackSeed ? @@ -636,14 +637,17 @@ void Stack::Print(Option_t* option) const void Stack::addHit(int iDet) { + // translate detector ID to bit id + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + if (mIndexOfCurrentTrack < mNumberOfPrimaryParticles) { auto& part = mTracks->at(mIndexOfCurrentTrack); - part.setHit(iDet); + part.setHit(bitID); } else { Int_t iTrack = mTrackIDtoParticlesEntry[mIndexOfCurrentTrack]; auto& part = mParticles[iTrack]; - part.setHit(iDet); + part.setHit(bitID); } mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); mHitCounter++; @@ -652,7 +656,9 @@ void Stack::addHit(int iDet, Int_t iTrack) { mHitCounter++; auto& part = mParticles[iTrack]; - part.setHit(iDet); + // fetch the bit encoding for hits + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + part.setHit(bitID); mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); } diff --git a/Detectors/Base/src/TGeoGeometryUtils.cxx b/Detectors/Base/src/TGeoGeometryUtils.cxx new file mode 100644 index 0000000000000..6f06eff17a6d7 --- /dev/null +++ b/Detectors/Base/src/TGeoGeometryUtils.cxx @@ -0,0 +1,144 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.cxx +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace base +{ + +namespace +{ +// some helpers to interpret TGeo TBuffer3D output +// and convert it to surface triangles (reengineered from TGeo code) + +std::vector BuildVertexLoop(const TBuffer3D& buf, + const std::vector& segs) +{ + // adjacency list + std::unordered_map> adj; + + for (int s : segs) { + int a = buf.fSegs[3 * s + 1]; + int b = buf.fSegs[3 * s + 2]; + adj[a].push_back(b); + adj[b].push_back(a); + } + + // start from any vertex + int start = adj.begin()->first; + int prev = -1; + int curr = start; + + std::vector loop; + + while (true) { + loop.push_back(curr); + + const auto& nbrs = adj[curr]; + int next = -1; + + for (int n : nbrs) { + if (n != prev) { + next = n; + break; + } + } + + if (next == -1 || next == start) { + break; + } + + prev = curr; + curr = next; + } + return loop; +} + +std::vector> ExtractPolygons(const TBuffer3D& buf) +{ + std::vector> polys; + Int_t idx = 0; + + for (Int_t ip = 0; ip < buf.NbPols(); ++ip) { + + idx++; // color + Int_t nseg = buf.fPols[idx++]; + + std::vector segs(nseg); + for (Int_t i = 0; i < nseg; ++i) { + segs[i] = buf.fPols[idx++]; + } + + auto verts = BuildVertexLoop(buf, segs); + if (verts.size() >= 3) { + polys.push_back(std::move(verts)); + } + } + + return polys; +} + +std::vector> + Triangulate(const std::vector>& polys) +{ + std::vector> tris; + for (const auto& poly : polys) { + int nv = poly.size(); + if (nv < 3) { + continue; + } + + int v0 = poly[0]; + for (int i = 1; i < nv - 1; ++i) { + tris.push_back({{v0, poly[i], poly[i + 1]}}); + } + } + return tris; +} + +TGeoTessellated* MakeTessellated(const TBuffer3D& buf) +{ + auto polys = ExtractPolygons(buf); + auto tris = Triangulate(polys); + int i = 0; + auto* tess = new TGeoTessellated("tess"); + const Double_t* p = buf.fPnts; + for (auto& t : tris) { + tess->AddFacet( + TGeoTessellated::Vertex_t{p[3 * t[0]], p[3 * t[0] + 1], p[3 * t[0] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[1]], p[3 * t[1] + 1], p[3 * t[1] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[2]], p[3 * t[2] + 1], p[3 * t[2] + 2]}); + } + tess->CloseShape(); + return tess; +} +} // end anonymous namespace + +///< Transform any (primitive) TGeoShape to a TGeoTessellated +TGeoTessellated* TGeoGeometryUtils::TGeoShapeToTGeoTessellated(TGeoShape const* shape) +{ + auto& buf = shape->GetBuffer3D(TBuffer3D::kRawSizes | TBuffer3D::kRaw | TBuffer3D::kCore, false); + auto tes = MakeTessellated(buf); + return tes; +} + +} // namespace base +} // namespace o2 diff --git a/Detectors/Base/src/VMCSeederService.cxx b/Detectors/Base/src/VMCSeederService.cxx new file mode 100644 index 0000000000000..5bf4e1ed5641b --- /dev/null +++ b/Detectors/Base/src/VMCSeederService.cxx @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsBase/VMCSeederService.h" +#include "TVirtualMC.h" +#include // for FairLogger +#include // for ROOT JIT helpers + +using namespace o2::base; + +void VMCSeederService::initSeederFunction(TVirtualMC const* vmc) +{ + if (strcmp(vmc->GetName(), "TGeant3TGeo") == 0) { + // Geant3 doesn't need anything special in our context + mSeederFcn = []() {}; + } else if (strcmp(vmc->GetName(), "TGeant4") == 0) { + // dynamically get access to the Geant4_VMC seeding function (without this function linking against Geant4) + std::string G4func("std::function G4func() { gSystem->Load(\"libgeant4vmc\"); return [](){ ((TGeant4*)TVirtualMC::GetMC())->SetRandomSeed(); };}"); + mSeederFcn = o2::conf::JITAndEvalFunction(G4func, "G4func()", "std::function", "VMCSEEDERFUNC123"); + } else { + LOG(warn) << "Unknown VMC engine or unimplemented VMC seeding function"; + mSeederFcn = []() {}; + } +} + +// constructor +VMCSeederService::VMCSeederService() +{ + auto vmc = TVirtualMC::GetMC(); + if (vmc) { + LOG(info) << "Seeder initializing for " << vmc->GetName(); + initSeederFunction(vmc); + } else { + LOG(fatal) << " Seeder could not be initialized (no VMC instance found)"; + } +} + +void VMCSeederService::setSeed() const +{ + // Make sure gRandom is sufficiently well initialized + // by calling Rndm() once. + // This is ok since in any case gRandom->SetSeed(seed); gRandom->GetSeed() != seed; + gRandom->Rndm(); + mSeederFcn(); +} diff --git a/Detectors/Base/src/bvh2_extra_kernels.h b/Detectors/Base/src/bvh2_extra_kernels.h new file mode 100644 index 0000000000000..70e43202a53c4 --- /dev/null +++ b/Detectors/Base/src/bvh2_extra_kernels.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_EXTRA + +namespace bvh::v2::extra +{ + +// reusable geometry kernels used in multiple places +// for interaction with BVH2 structures + +// determines if a point is inside the bounding box +template +bool contains(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + auto min = box.min; + auto max = box.max; + return (p[0] >= min[0] && p[0] <= max[0]) && (p[1] >= min[1] && p[1] <= max[1]) && + (p[2] >= min[2] && p[2] <= max[2]); +} + +// determines the largest squared distance of point to any of the bounding box corners +template +auto RmaxSqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + // construct the 8 corners to get the maximal distance + const auto minCorner = box.min; + const auto maxCorner = box.max; + using Vec3 = bvh::v2::Vec; + // these are the corners of the bounding box + const std::array, 8> corners{ + Vec3{minCorner[0], minCorner[1], minCorner[2]}, Vec3{minCorner[0], minCorner[1], maxCorner[2]}, + Vec3{minCorner[0], maxCorner[1], minCorner[2]}, Vec3{minCorner[0], maxCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], minCorner[1], minCorner[2]}, Vec3{maxCorner[0], minCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], maxCorner[1], minCorner[2]}, Vec3{maxCorner[0], maxCorner[1], maxCorner[2]}}; + + T Rmax_sq{0}; + for (const auto& corner : corners) { + float R_sq = 0.; + const auto dx = corner[0] - p[0]; + R_sq += dx * dx; + const auto dy = corner[1] - p[1]; + R_sq += dy * dy; + const auto dz = corner[2] - p[2]; + R_sq += dz * dz; + Rmax_sq = std::max(Rmax_sq, R_sq); + } + return Rmax_sq; +}; + +// determines the minimum squared distance of point to a bounding box ("safey square") +template +auto SafetySqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + T sqDist{0.0}; + for (int i = 0; i < 3; i++) { + T v = p[i]; + if (v < box.min[i]) { + sqDist += (box.min[i] - v) * (box.min[i] - v); + } else if (v > box.max[i]) { + sqDist += (v - box.max[i]) * (v - box.max[i]); + } + } + return sqDist; +}; + +} // namespace bvh::v2::extra + +#endif \ No newline at end of file diff --git a/Detectors/Base/src/bvh2_third_party.h b/Detectors/Base/src/bvh2_third_party.h new file mode 100644 index 0000000000000..5cf7772269642 --- /dev/null +++ b/Detectors/Base/src/bvh2_third_party.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_THIRD_PARTY + +// A single entry header into third-party BVH2 installed in ROOT +// Good place to manage compiler warnings etc. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wpsabi" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wpsabi" +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wattributes" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 5051) +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/Detectors/Base/test/README.md b/Detectors/Base/test/README.md index a545d6091846d..f5f9fd4c04b29 100644 --- a/Detectors/Base/test/README.md +++ b/Detectors/Base/test/README.md @@ -30,3 +30,5 @@ auto mb = mbl.getMatBudget(xyz0[0],xyz0[1],xyz0[2], xyz1[0],xyz1[1],xyz1[2]); std::cout << "= " << mb.meanRho << " = " << mb.meanX2X0 << "\n"; ``` + +Macro `extractLUTLayers.C` can be used to extract layers covering certain radius range to obtain more compact LUT. diff --git a/Detectors/Base/test/buildMatBudLUT.C b/Detectors/Base/test/buildMatBudLUT.C index e198f9e8b786b..85f8343a2d35d 100644 --- a/Detectors/Base/test/buildMatBudLUT.C +++ b/Detectors/Base/test/buildMatBudLUT.C @@ -23,13 +23,13 @@ #include #endif -#ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version +#ifndef GPUCA_ALIGPUCODE // this part is invisible on GPU version o2::base::MatLayerCylSet mbLUT; bool testMBLUT(const std::string& lutFile = "matbud.root"); -bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomNamePrefix = "o2sim"); +bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomNamePrefix = "o2sim", const std::string& opts = ""); struct LrData { float rMin = 0.f; @@ -44,14 +44,14 @@ struct LrData { std::vector lrData; void configLayers(); -bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std::string& geomNamePrefix) +bool buildMatBudLUT(int nTst, int maxLr, const std::string& outFile, const std::string& geomNamePrefix, const std::string& opts) { auto geomName = o2::base::NameConf::getGeomFileName(geomNamePrefix); if (gSystem->AccessPathName(geomName.c_str())) { // if needed, create geometry std::cout << geomName << " does not exist. Will create it on the fly\n"; std::stringstream str; // constructing an **unaligned** geom (Geant3 used since faster initialization) --> can be avoided by passing an existing geometry - str << "${O2_ROOT}/bin/o2-sim-serial -n 0 -e TGeant3 --configKeyValues=\"align-geom.mDetectors=none\" --field 0 -o " << geomNamePrefix; + str << "${O2_ROOT}/bin/o2-sim-serial -n 0 -e TGeant3 --configKeyValues \"" << opts << "\" --field 0 -o " << geomNamePrefix; gSystem->Exec(str.str().c_str()); } o2::base::GeometryManager::loadGeometry(geomNamePrefix); @@ -190,11 +190,12 @@ void configLayers() o2::itsmft::ChipMappingITS mp; int nStave = 0; - // rMin rMax zHalf - lrData.emplace_back(LrData(0.0f, 1.8f, 30.f)); + // rMin rMax zHalf + lrData.emplace_back(LrData(0.0f, 1.8f, 50.f)); // beam pipe - lrData.emplace_back(LrData(lrData.back().rMax, 2.0f, 30.f)); + lrData.emplace_back(LrData(lrData.back().rMax, 1.92f, 50.f)); + lrData.emplace_back(LrData(lrData.back().rMax, 2.2f, 50.f)); // ITS Inner Barrel drStep = 0.1; @@ -203,7 +204,7 @@ void configLayers() zBin = 0.5; do { lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 5.2 + kToler); + } while (lrData.back().rMax < 5 - kToler); // air space between Inner and Middle Barrels zSpanH = 40.; @@ -216,19 +217,19 @@ void configLayers() nStave = mp.getNStavesOnLr(3); // Lr 3 zSpanH = 55.; zBin = 0.5; - drStep = 0.2; + drStep = 0.3; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 20.5 + kToler); + } while (lrData.back().rMax < 21.4 - kToler); drStep = 0.5; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 24. + kToler); + } while (lrData.back().rMax < 23.4 - kToler); nStave = mp.getNStavesOnLr(3); // Lr 4 drStep = 0.2; @@ -236,19 +237,21 @@ void configLayers() auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 25.6 + kToler); + } while (lrData.back().rMax < 26.2 - kToler); drStep = 0.5; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 29. + kToler); + } while (lrData.back().rMax < 29. - kToler); //=================================================================================== // air space between Middle and Outer Barrels zSpanH = 80.f; - lrData.emplace_back(LrData(lrData.back().rMax, 33.5, zSpanH)); + zBin = 10.; + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 33.5, zSpanH, zBin, rphiBin)); //=================================================================================== // ITS Outer barrel @@ -258,55 +261,64 @@ void configLayers() zBin = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 36. + kToler); + } while (lrData.back().rMax < 36. - kToler); drStep = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 38.5 + kToler); + } while (lrData.back().rMax < 38.5 - kToler); nStave = mp.getNStavesOnLr(6); // Lr 6 drStep = 0.25; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 41. + kToler); + } while (lrData.back().rMax < 41. - kToler); drStep = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; - rphiBin = rmean * TMath::Pi() * 2 / (nStave * 10); + rphiBin = rmean * TMath::Pi() * 2 / (nStave * 15); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 44. + kToler); + } while (lrData.back().rMax < 44. - kToler); //=================================================================================== zSpanH = 100.f; zBin = 5.; - lrData.emplace_back(LrData(lrData.back().rMax, 47., zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 44.8, zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 46.2, zSpanH, zBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 47.0, zSpanH, zBin)); drStep = 2.; zBin = 5.; rphiBin = 2.; do { lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 53. + kToler); + } while (lrData.back().rMax < 55. - kToler); zSpanH = 120.f; - lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH)); + zBin = 10.; + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH, zBin, rphiBin)); + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 60.5, zSpanH, zBin, rphiBin)); + rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; + lrData.emplace_back(LrData(lrData.back().rMax, 61.5, zSpanH, zBin, rphiBin)); zSpanH = 150.f; - drStep = 4.; + drStep = 3.5; zBin = 15.; - rphiBin = 10; do { + auto rmean = lrData.back().rMax + drStep / 2; + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); lrData.emplace_back(LrData(lrData.back().rMax, lrData.back().rMax + drStep, zSpanH, zBin, rphiBin)); - } while (lrData.back().rMax < 68.5 + kToler); + } while (lrData.back().rMax < 68.5 - kToler); zSpanH = 250.f; zBin = 25.; @@ -323,14 +335,14 @@ void configLayers() { auto rmean = (lrData.back().rMax + 78.5) / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); - lrData.emplace_back(LrData(lrData.back().rMax, 78.5, zSpanH, zBin, rphiBin)); + lrData.emplace_back(LrData(lrData.back().rMax, 78.8, zSpanH, zBin, rphiBin)); } // zSpanH = 250.f; zBin = 2; { auto rmean = (lrData.back().rMax + 78.5) / 2; - rphiBin = rmean * TMath::Pi() * 2 / (NSect * 12); + rphiBin = rmean * TMath::Pi() * 2 / (NSect * 24); lrData.emplace_back(LrData(lrData.back().rMax, 84.5, zSpanH, zBin, rphiBin)); } diff --git a/Detectors/Base/test/extractLUTLayers.C b/Detectors/Base/test/extractLUTLayers.C new file mode 100644 index 0000000000000..113a12e6a07a5 --- /dev/null +++ b/Detectors/Base/test/extractLUTLayers.C @@ -0,0 +1,55 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#endif + +// Macro to extract layers covering selected radial range into the separate LUT file. + +o2::base::MatLayerCylSet* extractLUTLayers(const o2::base::MatLayerCylSet* src, const std::string& outName, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + auto* cp = src->extractCopy(rmin, rmax, toler); + if (!cp) { + LOGP(error, "failed to extract layers for {} < r < {}", rmin, rmax); + } + if (outName.size()) { + cp->writeToFile(outName); + } + return cp; +} + +void extractLUTLayers(const std::string& fname, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + const auto src = o2::base::MatLayerCylSet::loadFromFile(fname); + if (!src) { + LOGP(error, "failed to open source LUT from {}", fname); + return; + } + auto fnameOut = std::regex_replace(fname, std::regex(R"(.root)"), fmt::format("_r{}_{}.root", rmin, rmax)); + auto cp = extractLUTLayers(src, fnameOut, rmin, rmax, toler); +} + +void extractLUTLayers(long timestamp, float rmin = 0., float rmax = 84., float toler = 1e-6) +{ + auto& mg = o2::ccdb::BasicCCDBManager::instance(); + mg.setTimestamp(timestamp); + const auto src = o2::base::MatLayerCylSet::rectifyPtrFromFile(mg.get("GLO/Param/MatLUT")); + if (!src) { + LOGP(error, "failed to open load LUT from CCDB for timestamp {}", timestamp); + return; + } + auto fnameOut = fmt::format("matbudLUT_ts{}_r{}_{}.root", timestamp, rmin, rmax); + auto cp = extractLUTLayers(src, fnameOut, rmin, rmax, toler); +} diff --git a/Detectors/Base/test/rescaleLUT.C b/Detectors/Base/test/rescaleLUT.C new file mode 100644 index 0000000000000..9e25c796e43d1 --- /dev/null +++ b/Detectors/Base/test/rescaleLUT.C @@ -0,0 +1,82 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DetectorsBase/MatLayerCylSet.h" +#include "Framework/Logger.h" +#include "CCDB/BasicCCDBManager.h" +#include +#endif + +// Macro to extract layers covering selected radial range into the separate LUT file. + +void rescaleLUT(o2::base::MatLayerCylSet* src, const std::string& outName) +{ + struct RescRange { + float rMin, rMax, factor; + }; + std::vector task = { + // put here radial ranges in increasing order with corresponding factors to rescale + {0.1f, 6.f, 1.05}, // e.g. rescale layers covering 0.1 rmax) { + LOGP(error, "rMax={:.2f} of range {} is larger then rMin={:.2f} of range {}, must be in increasing order", rmin, il - 1, rmax, il); + return; + } + o2::base::Ray ray(std::max(src->getRMin(), rmin), 0., 0., std::min(src->getRMax(), rmax), 0., 0.); + if (!src->getLayersRange(ray, lmin, lmax)) { + LOGP(error, "No layers found for {:.2f} < r < {:.2f}", rmin, rmax); + return; + } + if (lmin == lmax) { + LOGP(error, "rMax={:.2f} of range {} and rMin={:.2f} of range {}, correspond to the same slice {} with {:.2f}getLayer(lmin).getRMin(), src->getLayer(lmin).getRMax()); + return; + } + } + + for (size_t il = 0; il < task.size(); il++) { + src->scaleLayersByR(task[il].rMin, task[il].rMax, task[il].factor); + } + if (outName.size()) { + src->writeToFile(outName); + } +} + +void rescaleLUT(const std::string& fname) +{ + auto src = o2::base::MatLayerCylSet::loadFromFile(fname); + if (!src) { + LOGP(error, "failed to open source LUT from {}", fname); + return; + } + auto fnameOut = std::regex_replace(fname, std::regex(R"(.root)"), "_rescaled.root"); + rescaleLUT(src, fnameOut); +} + +void rescaleLUT(long timestamp = -1) +{ + auto& mg = o2::ccdb::BasicCCDBManager::instance(); + mg.setTimestamp(timestamp); + auto src = o2::base::MatLayerCylSet::rectifyPtrFromFile(mg.get("GLO/Param/MatLUT")); + if (!src) { + LOGP(error, "failed to open load LUT from CCDB for timestamp {}", timestamp); + return; + } + auto fnameOut = fmt::format("matbudLUT_ts{}_rescaled.root", timestamp); + rescaleLUT(src, fnameOut); +} diff --git a/Detectors/Base/test/testDCAFitter.cxx b/Detectors/Base/test/testDCAFitter.cxx deleted file mode 100644 index 60e0f08f4a5a1..0000000000000 --- a/Detectors/Base/test/testDCAFitter.cxx +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#define BOOST_TEST_MODULE Test DCAFitter class -#define BOOST_TEST_MAIN -#define BOOST_TEST_DYN_LINK -#include - -#include "DetectorsBase/DCAFitter.h" -#include - -namespace o2 -{ -namespace base -{ - -void checkResults(const DCAFitter& fitter, const std::array xyz) -{ - int nCand = fitter.getNCandidates(); - BOOST_CHECK(nCand > 0); - for (int ic = 0; ic < nCand; ic++) { - const auto& vtx = fitter.getPCACandidate(ic); - float dx = vtx.x - xyz[0], dy = vtx.y - xyz[1], dz = vtx.z - xyz[2]; - float dst = TMath::Sqrt(dx * dx + dy * dy + dz * dz); - - const auto &trc0 = fitter.getTrack0(ic), &trc1 = fitter.getTrack1(ic); // track parameters at V0 - printf("Candidate %d: DCA:%+e Vtx: %+e %+e %+e [Diff to true: %+e %+e %+e -> %+e]\n", - ic, fitter.getChi2AtPCACandidate(ic), vtx.x, vtx.y, vtx.z, dx, dy, dz, dst); - printf("Track X-parameters at PCA: %+e %+e\n", trc0.getX(), trc1.getX()); - trc0.print(); - trc1.print(); - BOOST_CHECK(dst < 5e-2); - } -} - -BOOST_AUTO_TEST_CASE(PairDCAFitter) -{ - double bz = 5.0; - - // create V0 - std::array cv = {1e-6, 0, 1e-6, 0, 0, 1e-6, 0, 0, 0, 1e-6, 0, 0, 0, 0, 1e-5}; - std::array pr = {0., 0., -0.2, 0.6, 1.}; - DCAFitter::Track t0(0., 0., pr, cv); - t0.propagateTo(10, bz); - - std::array xyz; - t0.getXYZGlo(xyz); - printf("true vertex : %+e %+e %+e\n", xyz[0], xyz[1], xyz[2]); - - DCAFitter::Track tA(t0), tB(t0); - tB.setParam(-tA.getParam(4), 4); - tB.setParam(-0.5 * tA.getParam(3), 3); - tB.setParam(-0.2 * tA.getParam(2), 2); - - tA.rotate(-0.3); - tB.rotate(0.3); - - printf("True track params at PCA:\n"); - tA.print(); - tB.print(); - - tA.propagateTo(tA.getX() + 5., bz); - tB.propagateTo(tB.getX() + 8., bz); - - DCAFitter df(bz, 10.); - - // check with abs DCA minimization - { - df.setUseAbsDCA(true); - // we may supply track directly to the fitter (they are not modified) - int nCand = df.process(tA, tB); - printf("\n\nTesting with abs DCA minimization: %d candidates found\n", nCand); - // we can have up to 2 candidates - checkResults(df, xyz); - } - - // check with weighted DCA minimization - { - df.setUseAbsDCA(false); - // we may supply track directly to the fitter (they are not modified) - DCAFitter::TrcAuxPar trcAAux(tA, bz), trcBAux(tB, bz); - int nCand = df.process(tA, trcAAux, tB, trcBAux); - printf("\n\nTesting with abs DCA minimization: %d candidates found\n", nCand); - // we can have up to 2 candidates - checkResults(df, xyz); - } - - { - // direct minimization w/o preliminary propagation to XY crossing - df.setUseAbsDCA(true); - int nCand = df.processAsIs(tA, tB); - printf("\n\nTesting with abs DCA minimization w/o taking to XY crossing: %d candidates found\n", nCand); - checkResults(df, xyz); - } - - { - // direct minimization w/o preliminary propagation to XY crossing (errors unreliable) - df.setUseAbsDCA(false); - int nCand = df.processAsIs(tA, tB); - printf("\n\nTesting with weighted DCA minimization w/o taking to XY crossing: %d candidates found\n", nCand); - checkResults(df, xyz); - } -} - -} // namespace base -} // namespace o2 diff --git a/Detectors/Base/test/testMatBudLUT.cxx b/Detectors/Base/test/testMatBudLUT.cxx index a334ef48ec710..4daa992368dba 100644 --- a/Detectors/Base/test/testMatBudLUT.cxx +++ b/Detectors/Base/test/testMatBudLUT.cxx @@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(MatBudLUT) std::string geomPrefix("matBudGeom"); std::string matBudFile("matbud"); matBudFile += std::to_string(getpid()) + ".root"; - BOOST_CHECK(buildMatBudLUT(2, 20, matBudFile, geomPrefix + std::to_string(getpid()))); // generate LUT + BOOST_CHECK(buildMatBudLUT(2, 20, matBudFile, geomPrefix + std::to_string(getpid()), "align-geom.mDetectors=none")); // generate LUT BOOST_CHECK(testMBLUT(matBudFile)); // test LUT manipulations #endif //!GPUCA_ALIGPUCODE diff --git a/Detectors/CMakeLists.txt b/Detectors/CMakeLists.txt index d96c02ec42625..eef692ff18ca7 100644 --- a/Detectors/CMakeLists.txt +++ b/Detectors/CMakeLists.txt @@ -36,7 +36,8 @@ add_subdirectory(FOCAL) add_subdirectory(GlobalTracking) add_subdirectory(GlobalTrackingWorkflow) add_subdirectory(Vertexing) -add_subdirectory(StrangenessTracking) +add_subdirectory(GLOQC) + if(BUILD_ANALYSIS) add_subdirectory(AOD) endif() @@ -47,6 +48,8 @@ add_subdirectory(Calibration) add_subdirectory(DCS) add_subdirectory(Align) +add_subdirectory(ForwardAlign) + if(BUILD_SIMULATION) add_subdirectory(gconfig) diff --git a/Detectors/CPV/base/src/Geometry.cxx b/Detectors/CPV/base/src/Geometry.cxx index b31b57cb19be1..08bb5504e01da 100644 --- a/Detectors/CPV/base/src/Geometry.cxx +++ b/Detectors/CPV/base/src/Geometry.cxx @@ -68,7 +68,8 @@ short Geometry::areNeighbours(unsigned short absId1, unsigned short absId2) short rowdiff = TMath::Abs(relId1[1] - relId2[1]); short coldiff = TMath::Abs(relId1[2] - relId2[2]); - if ((coldiff <= 1) && (rowdiff <= 1)) { // At least common vertex + // if ((coldiff <= 1) && (rowdiff <= 1)) { // At least common vertex + if (coldiff + rowdiff <= 1) { // Common side return 1; } else { if ((relId2[1] > relId1[1]) && (relId2[2] > relId1[2] + 1)) { diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx index b1c61d25da9c8..f2a08e280f954 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVBadMapCalibDevice.cxx @@ -174,7 +174,7 @@ void CPVBadMapCalibDevice::sendOutput(DataAllocator& output) output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "CPV_BadChanMap", subSpec}, info); } - output.snapshot(o2::framework::Output{"CPV", "BADMAPCHANGE", 0, o2::framework::Lifetime::Timeframe}, mMapDiff); + output.snapshot(o2::framework::Output{"CPV", "BADMAPCHANGE", 0}, mMapDiff); } bool CPVBadMapCalibDevice::differFromCurrent() diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx index b5e98f00d021f..d7f187e3a88cc 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVGainCalibDevice.cxx @@ -143,7 +143,7 @@ void CPVGainCalibDevice::sendOutput(DataAllocator& output) fout.Close(); } // Anyway send change to QC - output.snapshot(o2::framework::Output{"CPV", "GAINDIFF", 0, o2::framework::Lifetime::Timeframe}, mGainRatio); + output.snapshot(o2::framework::Output{"CPV", "GAINDIFF", 0}, mGainRatio); } void CPVGainCalibDevice::calculateGains() diff --git a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx index 125e8e1bc8c40..5dd414ab7aeb8 100644 --- a/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx +++ b/Detectors/CPV/calib/CPVCalibWorkflow/src/CPVPedestalCalibDevice.cxx @@ -118,7 +118,7 @@ void CPVPedestalCalibDevice::sendOutput(DataAllocator& output) } // Anyway send change to QC LOG(info) << "[CPVPedestalCalibDevice - run] Writing "; - output.snapshot(o2::framework::Output{"CPV", "PEDDIFF", 0, o2::framework::Lifetime::Timeframe}, mPedDiff); + output.snapshot(o2::framework::Output{"CPV", "PEDDIFF", 0}, mPedDiff); // Write pedestal distributions to calculate bad map std::string filename = mPath + "CPVPedestals.root"; diff --git a/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C b/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C index 3b5af09190fe7..9d62cf1a13baa 100644 --- a/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C +++ b/Detectors/CPV/calib/macros/makeBadMapFromPedestalRun.C @@ -75,7 +75,7 @@ void makeBadMapFromPedestalRun(const char* ccdbURI = "http://ccdb-test.cern.ch:8 } o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbURI); // or http://localhost:8080 for a local installation api.storeAsTFileAny(&badMap, "CPV/Calib/BadChannelMap", metadata, timeStamp, timeStamp + 31536000000); } diff --git a/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h index 97c741bb0193d..71833d6ac9843 100644 --- a/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/GainCalibratorSpec.h @@ -101,7 +101,7 @@ class CPVGainCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("calibdigs"); // fill statistics - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits"; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits"; mCalibrator->process(digits); // fill TimeSlot with digits from 1 event and check slots for finalization // inform about results and send output to ccdb diff --git a/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h index 3473664bfefef..f272bf9deb3d7 100644 --- a/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/NoiseCalibratorSpec.h @@ -116,7 +116,7 @@ class CPVNoiseCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("digits"); auto&& trigrecs = pc.inputs().get>("trigrecs"); - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; auto& slotTF = mCalibrator->getSlotForTF(tfcounter); for (auto trigrec = trigrecs.begin(); trigrec != trigrecs.end(); trigrec++) { // event loop @@ -134,7 +134,7 @@ class CPVNoiseCalibratorSpec : public o2::framework::Task auto infoVecSize = mCalibrator->getCcdbInfoBadChannelMapVector().size(); auto badMapVecSize = mCalibrator->getBadChannelMapVector().size(); if (infoVecSize > 0) { - LOG(info) << "Created " << infoVecSize << " ccdb infos and " << badMapVecSize << " BadChannelMap objects for TF " << tfcounter; + LOG(detail) << "Created " << infoVecSize << " ccdb infos and " << badMapVecSize << " BadChannelMap objects for TF " << tfcounter; } sendOutput(pc.outputs()); } diff --git a/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h b/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h index 16d565e8e1d41..9e452e2fd8886 100644 --- a/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h +++ b/Detectors/CPV/calib/testWorkflow/PedestalCalibratorSpec.h @@ -81,7 +81,7 @@ class CPVPedestalCalibratorSpec : public o2::framework::Task auto&& digits = pc.inputs().get>("digits"); auto&& trigrecs = pc.inputs().get>("trigrecs"); - LOG(info) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; + LOG(detail) << "Processing TF " << tfcounter << " with " << digits.size() << " digits in " << trigrecs.size() << " trigger records."; auto& slotTF = mCalibrator->getSlotForTF(tfcounter); for (auto trigrec = trigrecs.begin(); trigrec != trigrecs.end(); trigrec++) { // event loop @@ -99,7 +99,7 @@ class CPVPedestalCalibratorSpec : public o2::framework::Task auto infoVecSize = mCalibrator->getCcdbInfoPedestalsVector().size(); auto pedsVecSize = mCalibrator->getPedestalsVector().size(); if (infoVecSize > 0) { - LOG(info) << "Created " << infoVecSize << " ccdb infos and " << pedsVecSize << " pedestal objects for TF " << tfcounter; + LOG(detail) << "Created " << infoVecSize << " ccdb infos and " << pedsVecSize << " pedestal objects for TF " << tfcounter; } sendOutput(pc.outputs()); } diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index 31f4227742385..4e259c24f44a6 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -23,7 +23,6 @@ #include "DataFormatsCPV/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "CPVReconstruction/CTFHelper.h" class TTree; @@ -33,10 +32,10 @@ namespace o2 namespace cpv { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -47,7 +46,7 @@ class CTFCoder : public o2::ctf::CTFCoderBase template o2::ctf::CTFIOSize decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER& cluVec); - void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; + void createCoders(const std::vector& bufVec, ctf::CTFCoderBase::OpType op) final; private: template @@ -86,13 +85,13 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader()); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODECPV(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODECPV(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODECPV(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODECPV(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -134,12 +132,13 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCLUSTER auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc, entries, posX, posZ; - std::vector orbitInc; + std::vector bcInc; + std::vector orbitInc; + std::vector entries, posX, posZ; std::vector energy, status; o2::ctf::CTFIOSize iosize; -#define DECODECPV(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODECPV(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODECPV(bcInc, CTF::BLC_bcIncTrig); iosize += DECODECPV(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h index 607d426d11c89..0c86c7ccc7f17 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFHelper.h @@ -50,7 +50,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -59,92 +59,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I& operator--() + inline I operator++(int) { - mIndex--; - return (I&)(*this); + I res = *(static_cast(this)); + ++mIndex; + return res; } - const I operator++(int) + inline I& operator--() noexcept { - auto res = *this; - ++mIndex; - return res; + mIndex--; + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } - - difference_type operator-(size_t idx) const { return mIndex - idx; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].getBCData().orbit == mData[mIndex - 1].getBCData().orbit) { - return mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc); } else { - return mData[mIndex].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc); } } return 0; @@ -154,9 +148,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].getBCData().orbit == mData[id - 1].getBCData().orbit) { - return mData[id].getBCData().bc - mData[id - 1].getBCData().bc; + return value_type(mData[id].getBCData().bc - mData[id - 1].getBCData().bc); } else { - return mData[id].getBCData().bc; + return value_type(mData[id].getBCData().bc); } } return 0; @@ -165,15 +159,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0; + return value_type(id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0); } }; @@ -206,10 +200,10 @@ class CTFHelper }; //_______________________________________________ - class Iter_energy : public _Iter + class Iter_energy : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { return mData[mIndex].getPackedEnergy(); } value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedEnergy(); } }; diff --git a/Detectors/CPV/reconstruction/src/CTFCoder.cxx b/Detectors/CPV/reconstruction/src/CTFCoder.cxx index 7c94e125dc449..b86706dee6729 100644 --- a/Detectors/CPV/reconstruction/src/CTFCoder.cxx +++ b/Detectors/CPV/reconstruction/src/CTFCoder.cxx @@ -41,10 +41,11 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc = 0, entries = 0, cluPosX = 0, cluPosZ = 0; - uint32_t orbitInc = 0; + int16_t bcInc = 0; + int32_t orbitInc = 0; + uint16_t entries = 0, cluPosX = 0, cluPosZ = 0; uint8_t energy = 0, status = 0; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CPV/workflow/CMakeLists.txt b/Detectors/CPV/workflow/CMakeLists.txt index 04e4b075a7ec4..ba21452393b99 100644 --- a/Detectors/CPV/workflow/CMakeLists.txt +++ b/Detectors/CPV/workflow/CMakeLists.txt @@ -12,6 +12,8 @@ o2_add_library(CPVWorkflow SOURCES src/RecoWorkflow.cxx src/ReaderSpec.cxx + src/DigitReaderSpec.cxx + src/ClusterReaderSpec.cxx src/WriterSpec.cxx src/ClusterizerSpec.cxx src/DigitsPrinterSpec.cxx @@ -41,3 +43,13 @@ o2_add_executable(cluster-writer-workflow COMPONENT_NAME cpv SOURCES src/cluster-writer-workflow.cxx PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(digit-reader-workflow + COMPONENT_NAME cpv + SOURCES src/digit-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(cluster-reader-workflow + COMPONENT_NAME cpv + SOURCES src/cluster-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h new file mode 100644 index 0000000000000..ee1c195b0aec5 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterReaderSpec.h + +#ifndef O2_CPV_CLUSTERREADER +#define O2_CPV_CLUSTERREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Cluster.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class ClusterReader : public o2::framework::Task +{ + public: + ClusterReader(bool useMC = true); + ~ClusterReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mClusters, *mClustersInp = &mClusters; + std::vector mTRs, *mTRsInp = &mTRs; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mClusterTreeName = "o2sim"; + std::string mClusterBranchName = "CPVCluster"; + std::string mTRBranchName = "CPVClusterTrigRec"; + std::string mClusterMCTruthBranchName = "CPVClusterTrueMC"; +}; + +/// create a processor spec +/// read CPV Cluster data from a root file +framework::DataProcessorSpec getCPVClusterReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_CLUSTERREADER */ diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..00fce64f89125 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h @@ -0,0 +1,69 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file DigitReaderSpec.h + +#ifndef O2_CPV_DIGITREADER +#define O2_CPV_DIGITREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class DigitReader : public o2::framework::Task +{ + public: + DigitReader(bool useMC = true); + ~DigitReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mDigits, *mDigitsInp = &mDigits; + std::vector mTRs, *mTRsInp = &mTRs; + // std::vector mMCTruth, *mMCTruthInp = &mMCTruth; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mDigitTreeName = "o2sim"; + std::string mDigitBranchName = "CPVDigit"; + std::string mTRBranchName = "CPVDigitTrigRecords"; + std::string mDigitMCTruthBranchName = "CPVDigitMCTruth"; +}; + +/// create a processor spec +/// read CPV Digit data from a root file +framework::DataProcessorSpec getCPVDigitReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_DIGITREADER */ diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h index 09de778360d74..7192b1b2f6353 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h index 24c229179fe1d..a1851ebb97377 100644 --- a/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CPV/workflow/include/CPVWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace cpv class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace cpv } // namespace o2 diff --git a/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx new file mode 100644 index 0000000000000..f9d0817325c36 --- /dev/null +++ b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/ClusterReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +ClusterReader::ClusterReader(bool useMC) +{ + mUseMC = useMC; +} + +void ClusterReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-clusters-infile")); + connectTree(mInputFileName); +} + +void ClusterReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mClusters.size() << " Clusters in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "CLUSTERS", 0}, mClusters); + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRIGRECS", 0}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRUEMC", 0}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void ClusterReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mClusterTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mClusterBranchName.c_str(), &mClustersInp); + if (mUseMC) { + if (mTree->GetBranch(mClusterMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mClusterMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVClusterReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "CLUSTERS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "CLUSTERTRUEMC", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-cluster-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-clusters-infile", VariantType::String, "cpvclusters.root", {"Name of the input Cluster file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx index 87d0b86a98f5c..e004c3cec8949 100644 --- a/Detectors/CPV/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/CPV/workflow/src/ClusterizerSpec.cxx @@ -49,14 +49,14 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) if (!digits.size()) { // nothing to process LOG(info) << "ClusterizerSpec::run() : no digits; moving on"; mOutputClusters.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusters); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0}, mOutputClusters); mOutputClusterTrigRecs.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusterTrigRecs); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0}, mOutputClusterTrigRecs); mCalibDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0, o2::framework::Lifetime::Timeframe}, mCalibDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0}, mCalibDigits); if (mPropagateMC) { mOutputTruthCont.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0}, mOutputTruthCont); } return; } @@ -73,12 +73,12 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) LOG(debug) << "CPVClusterizer::run() : Received " << digitsTR.size() << " TR, calling clusterizer ..."; - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusters); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusterTrigRecs); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERS", 0}, mOutputClusters); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRIGRECS", 0}, mOutputClusterTrigRecs); if (mPropagateMC) { - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0, o2::framework::Lifetime::Timeframe}, mOutputTruthCont); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CLUSTERTRUEMC", 0}, mOutputTruthCont); } - ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0, o2::framework::Lifetime::Timeframe}, mCalibDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "CALIBDIGITS", 0}, mCalibDigits); LOG(info) << "Finished, wrote " << mOutputClusters.size() << " clusters, " << mOutputClusterTrigRecs.size() << "TR and " << mOutputTruthCont.getIndexedSize() << " Labels"; } o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getClusterizerSpec(bool propagateMC) diff --git a/Detectors/CPV/workflow/src/DigitReaderSpec.cxx b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..20fe497eb5d0c --- /dev/null +++ b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file DigitReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/DigitReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +DigitReader::DigitReader(bool useMC) +{ + mUseMC = useMC; +} + +void DigitReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-digits-infile")); + connectTree(mInputFileName); +} + +void DigitReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{mOrigin, "DIGITTRIGREC", 0}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void DigitReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mDigitTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsInp); + if (mUseMC) { + if (mTree->GetBranch(mDigitMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVDigitReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "DIGITS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "DIGITTRIGREC", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-digit-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-digits-infile", VariantType::String, "cpvdigits.root", {"Name of the input Digit file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx index 25173c7a64555..518a646e23cb9 100644 --- a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx @@ -25,12 +25,13 @@ namespace o2 namespace cpv { -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_CPV"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -51,8 +52,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_CPV"); auto& triggers = pc.outputs().make>(OutputRef{"triggers"}); auto& clusters = pc.outputs().make>(OutputRef{"clusters"}); @@ -73,7 +74,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe}, @@ -81,16 +82,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "CPV", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionary")); + inputs.emplace_back("ctf_CPV", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CPV", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "cpv-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } } // namespace cpv diff --git a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx index 8901028e7ac66..54fb1354ad60c 100644 --- a/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyEncoderSpec.cxx @@ -26,7 +26,7 @@ namespace o2 namespace cpv { -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,13 +48,13 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto triggers = pc.inputs().get>("triggers"); auto clusters = pc.inputs().get>("clusters"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"CPV", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"CPV", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, triggers, clusters); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -70,12 +70,14 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); inputs.emplace_back("clusters", "CPV", "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionary")); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -84,11 +86,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"CPV", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CPV", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } } // namespace cpv diff --git a/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx index 5dcac96160610..766902a2fdc95 100644 --- a/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx +++ b/Detectors/CPV/workflow/src/RawToDigitConverterSpec.cxx @@ -136,11 +136,11 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } mOutputDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); mOutputTriggerRecords.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); mOutputHWErrors.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); return; // empty TF, nothing to process } } @@ -272,11 +272,11 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) if (skipTF) { // Send no digits mOutputDigits.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); mOutputTriggerRecords.clear(); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); // Send errors - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); return; } } @@ -305,15 +305,15 @@ void RawToDigitConverterSpec::run(framework::ProcessingContext& ctx) digitBuffer.clear(); LOG(info) << "[CPVRawToDigitConverter - run] Sending " << mOutputDigits.size() << " digits in " << mOutputTriggerRecords.size() << "trigger records."; - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); - ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITS", 0}, mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "DIGITTRIGREC", 0}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"CPV", "RAWHWERRORS", 0}, mOutputHWErrors); } //_____________________________________________________________________________ o2::framework::DataProcessorSpec o2::cpv::reco_workflow::getRawToDigitConverterSpec(bool askDISTSTF, bool isPedestal, bool useBadChannelMap, bool useGainCalibration) { std::vector inputs; - inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"CPV", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"CPV", "RAWDATA"}, o2::framework::Lifetime::Timeframe); // receive at least 1 guaranteed input (which will allow to acknowledge the TF) if (askDISTSTF) { inputs.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); diff --git a/Detectors/CPV/workflow/src/RecoWorkflow.cxx b/Detectors/CPV/workflow/src/RecoWorkflow.cxx index da4ffee8aa6e8..66b5f3dbe588a 100644 --- a/Detectors/CPV/workflow/src/RecoWorkflow.cxx +++ b/Detectors/CPV/workflow/src/RecoWorkflow.cxx @@ -24,6 +24,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsCPV/TriggerRecord.h" #include "CPVWorkflow/RecoWorkflow.h" +#include "CPVWorkflow/DigitReaderSpec.h" #include "CPVWorkflow/ClusterizerSpec.h" #include "CPVWorkflow/ReaderSpec.h" #include "CPVWorkflow/WriterSpec.h" @@ -107,7 +108,8 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // Digits to .... if (inputType == InputType::Digits) { if (!disableRootInp) { - specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(propagateMC)); + // specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); } if (isEnabled(OutputType::Clusters)) { diff --git a/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx new file mode 100644 index 0000000000000..33f389f15f9da --- /dev/null +++ b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/ClusterReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CPV/workflow/src/digit-reader-workflow.cxx b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx new file mode 100644 index 0000000000000..dc068573b1218 --- /dev/null +++ b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx @@ -0,0 +1,56 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/DigitReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx index d7e79c4cea430..90fedb1d5eb83 100644 --- a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::cpv::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index 26c2c25dc83ef..47ce765de289a 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -51,6 +51,22 @@ o2-ctf-reader-workflow --ctf-input input.lst --onlyDet ITS,TPC,TOF --its-entropy See below for the details of `--ctf-dict` option. +One can pause the writing if available disk space is low using a combination of following options: +```bash +--require-free-disk : pause writing operation if available disk space is below this margin in bytes (if > 0) or this fraction of total (if < 0) +``` + +```bash +--wait-for-free-disk : if paused due to the low disk space, recheck after this time (in s) +``` + +```bash +--max-wait-for-free-disk : produce fatal if paused due to the low disk space for more than this amount( in s). +``` + + + + ## CTF reader workflow `o2-ctf-reader-workflow` should be the 1st workflow in the piped chain of CTF processing. @@ -79,11 +95,24 @@ comma-separated list of detectors to read, Overrides skipDet ``` comma-separated list of detectors to skip + +By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. + +``` +--ctf-data-subspec arg (=0) +``` +allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). + ``` --max-tf arg (=-1) ``` max CTFs to process (<= 0 : infinite) +``` +--max-tf-per-file arg (=-1) +``` +max TFs to process from every CTF file (<= 0 : infinite) + ``` --loop arg (=0) ``` @@ -120,6 +149,8 @@ There is a possibility to read remote root files directly, w/o caching them loca 2) provide proper regex to define remote files, e.g. for the example above: `--remote-regex "^root://.+/eos/aliceo2/.+"`. 3) pass an option `--copy-cmd no-copy`. +## Selective TF reading + ``` --select-ctf-ids ``` @@ -127,24 +158,25 @@ This is a `ctf-reader` device local option allowing selective reading of particu Note that the index corresponds not to the entry of the TF in the CTF tree but to the reader own counter incremented throught all input files (e.g. if the 10 CTF files with 20 TFs each are provided for the input and the selection of TFs `0,2,22,66` is provided, the reader will inject to the DPL the TFs at entries 0 and 2 from the 1st CTF file, entry 5 of the second file, entry 6 of the 3d and will finish the job. -For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) -or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). -For example, ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +--ir-frames-files --skip-skimmed-out-tf ``` -will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +This option (used for skimming) allow to push to DPL only those TFs which overlap with selected BC-ranges provided via input root file (for various formats see `o2::utils::IRFrameSelector::loadIRFrames` method). + ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +--ir-frames-files ``` -will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. - -By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. +This option allows to push to DPL only those TFs which overlap with the ` ` (separators can be any whitespace, comma or semicolon) records provided via text file (assuming that there are some entries for a given run, otherwise the option is ignored). +Multiple ranges per run and multiple runs can be mentioned in a single input file. The range limits can be indicated either as a UNIX timestamp in `ms` or as an orbit number (in the fill the run belongs to). +In case an option ``` ---ctf-data-subspec arg (=0) +--invert-irframe-selection ``` -allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). +is provided, the selections above are inverted: TFs matching some of the provided ranges will be discarded, while the rest will be pushed to the DPL + +At the end of the processing the `ctf-writer` will create a local file `ctf_read_ntf.txt` containing only the number of TFs pushed to the DPL. +In case no TF passed the selections above, this file will contain 0. ## Support for externally provided encoding dictionaries @@ -180,3 +212,18 @@ Additionally, one may throttle on the free SHM by providing an option to the rea Note that by default the reader reads into the memory the CTF data and prepares all output messages but injects them only once the rate-limiter allows that. With the option `--limit-tf-before-reading` set also the preparation of the data to inject will be conditioned by the green light from the rate-limiter. + + +## Modifying ITS/MFT CTF output + +For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) +or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). +For example, +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +``` +will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +``` +will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. diff --git a/Detectors/CTF/test/test_ctf_io_cpv.cxx b/Detectors/CTF/test/test_ctf_io_cpv.cxx index 2648cd40374cb..34a383a6875a0 100644 --- a/Detectors/CTF/test/test_ctf_io_cpv.cxx +++ b/Detectors/CTF/test/test_ctf_io_cpv.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test CPVCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CPVReconstruction/CTFCoder.h" #include "DataFormatsCPV/CTF.h" @@ -22,11 +28,15 @@ #include #include #include +#include #include using namespace o2::cpv; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector clusters; @@ -55,6 +65,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, clusters); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_ctp.cxx b/Detectors/CTF/test/test_ctf_io_ctp.cxx index 25c93f360baf1..be9cdd1667ba8 100644 --- a/Detectors/CTF/test/test_ctf_io_ctp.cxx +++ b/Detectors/CTF/test/test_ctf_io_ctp.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test CTPCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CTPReconstruction/CTFCoder.h" #include "DataFormatsCTP/CTF.h" @@ -25,8 +31,11 @@ #include using namespace o2::ctp; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; TStopwatch sw; @@ -49,6 +58,7 @@ BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, lumi); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_emcal.cxx b/Detectors/CTF/test/test_ctf_io_emcal.cxx index fb91254f6847f..4c6f6f8193672 100644 --- a/Detectors/CTF/test/test_ctf_io_emcal.cxx +++ b/Detectors/CTF/test/test_ctf_io_emcal.cxx @@ -12,8 +12,15 @@ #define BOOST_TEST_MODULE Test EMCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" +#include "DataFormatsEMCAL/Constants.h" #include "EMCALReconstruction/CTFCoder.h" #include "DataFormatsEMCAL/CTF.h" #include "Framework/Logger.h" @@ -24,8 +31,11 @@ #include using namespace o2::emcal; +namespace boost_data = boost::unit_test::data; -BOOST_AUTO_TEST_CASE(CTFTest) +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; + +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector cells; @@ -41,8 +51,33 @@ BOOST_AUTO_TEST_CASE(CTFTest) while (tower < 17665) { float timeCell = gRandom->Rndm() * 1500 - 600.; float en = gRandom->Rndm() * 250.; - int stat = gRandom->Integer(5); - cells.emplace_back(tower, en, timeCell, (ChannelType_t)stat); + // In case of cell type 3 cases must be distinguished (FEE, LEDMON, TRU) + // In case the cell is a FEE cell the cell type is correlated with the energy + int readoutsource = gRandom->Integer(3); // + ChannelType_t chantype = ChannelType_t::HIGH_GAIN; + switch (readoutsource) { + case 0: { + // Cell is a FEE cell, determine cell type according to HGLG transition + const auto ENHGLG = o2::emcal::constants::EMCAL_HGLGTRANSITION * o2::emcal::constants::EMCAL_ADCENERGY; + if (en >= ENHGLG) { + chantype = ChannelType_t::LOW_GAIN; + } else { + chantype = ChannelType_t::HIGH_GAIN; + } + break; + } + case 1: + chantype = ChannelType_t::LEDMON; + break; + case 2: + chantype = ChannelType_t::TRU; + break; + + default: + std::cerr << "Unknown type" << std::endl; + break; + } + cells.emplace_back(tower, en, timeCell, chantype); tower += 1 + gRandom->Integer(100); } uint32_t trigBits = gRandom->Integer(0xFFFFFFFF); // will be converted internally to uint16_t by the coder @@ -53,6 +88,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, cells); // compress } sw.Stop(); @@ -126,9 +162,9 @@ BOOST_AUTO_TEST_CASE(CTFTest) for (size_t i = 0; i < cells.size(); i++) { const auto& cor = cells[i]; const auto& cdc = cellsD[i]; - BOOST_CHECK_EQUAL(cor.getPackedTowerID(), cdc.getPackedTowerID()); - BOOST_CHECK_EQUAL(cor.getPackedTime(), cdc.getPackedTime()); - BOOST_CHECK_EQUAL(cor.getPackedEnergy(), cdc.getPackedEnergy()); - BOOST_CHECK_EQUAL(cor.getPackedCellStatus(), cdc.getPackedCellStatus()); + BOOST_CHECK_EQUAL(cor.getTowerIDEncoded(), cdc.getTowerIDEncoded()); + BOOST_CHECK_EQUAL(cor.getTimeStampEncoded(), cdc.getTimeStampEncoded()); + BOOST_CHECK_EQUAL(cor.getEnergyEncoded(), cdc.getEnergyEncoded()); + BOOST_CHECK_EQUAL(cor.getCellTypeEncoded(), cdc.getCellTypeEncoded()); } } diff --git a/Detectors/CTF/test/test_ctf_io_fdd.cxx b/Detectors/CTF/test/test_ctf_io_fdd.cxx index a1da64af04968..3dd87c1618598 100644 --- a/Detectors/CTF/test/test_ctf_io_fdd.cxx +++ b/Detectors/CTF/test/test_ctf_io_fdd.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FDDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FDDReconstruction/CTFCoder.h" #include "FDDBase/Constants.h" @@ -23,8 +29,11 @@ #include using namespace o2::fdd; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -81,6 +90,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_ft0.cxx b/Detectors/CTF/test/test_ctf_io_ft0.cxx index 4b7bee2460909..6e2746444e662 100644 --- a/Detectors/CTF/test/test_ctf_io_ft0.cxx +++ b/Detectors/CTF/test/test_ctf_io_ft0.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FT0CTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FT0Reconstruction/CTFCoder.h" #include "FT0Base/FT0DigParam.h" @@ -23,8 +29,11 @@ #include using namespace o2::ft0; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -87,6 +96,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_fv0.cxx b/Detectors/CTF/test/test_ctf_io_fv0.cxx index f9bf4a8a878e5..2e558b91a8e1c 100644 --- a/Detectors/CTF/test/test_ctf_io_fv0.cxx +++ b/Detectors/CTF/test/test_ctf_io_fv0.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test FV0CTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "FV0Reconstruction/CTFCoder.h" #include "FV0Base/Constants.h" @@ -23,8 +29,11 @@ #include using namespace o2::fv0; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; std::vector channels; @@ -59,6 +68,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, digits, channels); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_hmpid.cxx b/Detectors/CTF/test/test_ctf_io_hmpid.cxx index 88df30c8419c0..fd12ee7eb1e36 100644 --- a/Detectors/CTF/test/test_ctf_io_hmpid.cxx +++ b/Detectors/CTF/test/test_ctf_io_hmpid.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test HMPIDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "HMPIDReconstruction/CTFCoder.h" #include "DataFormatsHMP/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::hmpid; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector digits; @@ -53,6 +62,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, digits); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_itsmft.cxx b/Detectors/CTF/test/test_ctf_io_itsmft.cxx index 806ad8536d69d..13cbdf7745961 100644 --- a/Detectors/CTF/test/test_ctf_io_itsmft.cxx +++ b/Detectors/CTF/test/test_ctf_io_itsmft.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test ITSMFTCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -26,8 +32,11 @@ #include using namespace o2::itsmft; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CompressedClustersTest) +BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansVersion) { std::vector rofRecVec; @@ -73,6 +82,7 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, o2::detectors::DetID::ITS); + coder.setANSVersion(ansVersion); coder.encode(vec, rofRecVec, cclusVec, pattVec, pattIdConverter, 0); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_mch.cxx b/Detectors/CTF/test/test_ctf_io_mch.cxx index 9a6553ba70d81..a12ca58a574da 100644 --- a/Detectors/CTF/test/test_ctf_io_mch.cxx +++ b/Detectors/CTF/test/test_ctf_io_mch.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test MCHCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "MCHCTF/CTFCoder.h" #include "DataFormatsMCH/CTF.h" @@ -26,8 +32,11 @@ #include using namespace o2::mch; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector rofs; std::vector digs; @@ -59,6 +68,7 @@ BOOST_AUTO_TEST_CASE(CTFTest, *boost::unit_test::enabled()) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, rofs, digs); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_mid.cxx b/Detectors/CTF/test/test_ctf_io_mid.cxx index 2e8ee98b87e7a..2a7122f96e1da 100644 --- a/Detectors/CTF/test/test_ctf_io_mid.cxx +++ b/Detectors/CTF/test/test_ctf_io_mid.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test MIDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "CommonUtils/IRFrameSelector.h" #include "MIDCTF/CTFCoder.h" @@ -25,8 +31,11 @@ #include using namespace o2::mid; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::array, NEvTypes> colData{}; std::array, NEvTypes> rofData{}; @@ -73,6 +82,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, tfData); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_phos.cxx b/Detectors/CTF/test/test_ctf_io_phos.cxx index f564dfc2b8fc8..6850642ba129e 100644 --- a/Detectors/CTF/test/test_ctf_io_phos.cxx +++ b/Detectors/CTF/test/test_ctf_io_phos.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test PHSCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "PHOSReconstruction/CTFCoder.h" #include "DataFormatsPHOS/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::phos; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector cells; @@ -51,6 +60,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, cells); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_tof.cxx b/Detectors/CTF/test/test_ctf_io_tof.cxx index b770dbb2f78d5..276e773f77c69 100644 --- a/Detectors/CTF/test/test_ctf_io_tof.cxx +++ b/Detectors/CTF/test/test_ctf_io_tof.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test TOFCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsTOF/CTF.h" #include "TOFBase/Geo.h" #include "TOFBase/Digit.h" @@ -25,8 +31,11 @@ #include using namespace o2::tof; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CompressedClustersTest) +BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansVersion) { std::vector digits; @@ -86,6 +95,7 @@ BOOST_AUTO_TEST_CASE(CompressedClustersTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, rows, digits, pattVec); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_tpc.cxx b/Detectors/CTF/test/test_ctf_io_tpc.cxx index 10b5316524baa..cb087e6f2a38f 100644 --- a/Detectors/CTF/test/test_ctf_io_tpc.cxx +++ b/Detectors/CTF/test/test_ctf_io_tpc.cxx @@ -12,8 +12,15 @@ #define BOOST_TEST_MODULE Test TPCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTPC/CTF.h" #include "CommonUtils/NameConf.h" #include "TPCReconstruction/CTFCoder.h" @@ -24,15 +31,28 @@ #include using namespace o2::tpc; +namespace boost_data = boost::unit_test::data; -BOOST_AUTO_TEST_CASE(CTFTest) +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; +inline std::vector CombineColumns(true, false); + +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(CombineColumns), ansVersion, combineColumns) { + std::vector triggers, triggersR; CompressedClusters c; c.nAttachedClusters = 99; c.nUnattachedClusters = 88; c.nAttachedClustersReduced = 77; c.nTracks = 66; + triggers.emplace_back(); + triggers.back().orbit = 1234; + triggers.back().triggerWord.triggerEntries[0] = (10 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PhT & 0x7) << 12) | 0x8000; + triggers.back().triggerWord.triggerEntries[1] = (30 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PP & 0x7) << 12) | 0x8000; + triggers.emplace_back(); + triggers.back().orbit = 1236; + triggers.back().triggerWord.triggerEntries[0] = (40 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::Cal & 0x7) << 12) | 0x8000; + std::vector bVec; CompressedClustersFlat* ccFlat = nullptr; size_t sizeCFlatBody = CTFCoder::alignSize(ccFlat); @@ -43,7 +63,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); coder.setCompClusAddresses(c, buff); - coder.setCombineColumns(true); + coder.setCombineColumns(combineColumns); } ccFlat->set(sz, c); @@ -86,9 +106,43 @@ BOOST_AUTO_TEST_CASE(CTFTest) sw.Start(); std::vector vecIO; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); - coder.setCombineColumns(true); - coder.encode(vecIO, c, c); // compress + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setCombineColumns(combineColumns); + coder.setANSVersion(ansVersion); + // prepare trigger info + o2::tpc::detail::TriggerInfo trigComp; + for (const auto& trig : triggers) { + for (int it = 0; it < o2::tpc::TriggerWordDLBZS::MaxTriggerEntries; it++) { + if (trig.triggerWord.isValid(it)) { + trigComp.deltaOrbit.push_back(trig.orbit); + trigComp.deltaBC.push_back(trig.triggerWord.getTriggerBC(it)); + trigComp.triggerType.push_back(trig.triggerWord.getTriggerType(it)); + } else { + break; + } + } + } + // transform trigger info to differential form + uint32_t prevOrbit = -1; + uint16_t prevBC = -1; + if (trigComp.triggerType.size()) { + prevOrbit = trigComp.firstOrbit = trigComp.deltaOrbit[0]; + prevBC = trigComp.deltaBC[0]; + trigComp.deltaOrbit[0] = 0; + for (size_t it = 1; it < trigComp.triggerType.size(); it++) { + if (trigComp.deltaOrbit[it] == prevOrbit) { + auto bc = trigComp.deltaBC[it]; + trigComp.deltaBC[it] -= prevBC; + prevBC = bc; + trigComp.deltaOrbit[it] = 0; + } else { + auto orb = trigComp.deltaOrbit[it]; + trigComp.deltaOrbit[it] -= prevOrbit; + prevOrbit = orb; + } + } + } + coder.encode(vecIO, c, c, trigComp); // compress } sw.Stop(); LOG(info) << "Compressed in " << sw.CpuTime() << " s"; @@ -124,7 +178,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); coder.setCombineColumns(true); - coder.decode(ctfImage, vecIn); // decompress + coder.decode(ctfImage, vecIn, triggersR); // decompress } sw.Stop(); LOG(info) << "Decompressed in " << sw.CpuTime() << " s"; @@ -142,4 +196,6 @@ BOOST_AUTO_TEST_CASE(CTFTest) BOOST_CHECK(countOrig->solenoidBz == countDeco->solenoidBz); BOOST_CHECK(countOrig->maxTimeBin == countDeco->maxTimeBin); BOOST_CHECK(memcmp(vecIn.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.size() - sizeof(o2::tpc::CompressedClustersCounters)) == 0); + BOOST_CHECK(triggers.size() == triggersR.size()); + BOOST_CHECK(memcmp(triggers.data(), triggersR.data(), triggers.size() * sizeof(o2::tpc::TriggerInfoDLBZS)) == 0); } diff --git a/Detectors/CTF/test/test_ctf_io_trd.cxx b/Detectors/CTF/test/test_ctf_io_trd.cxx index 158ccd187e6b8..66203074ce817 100644 --- a/Detectors/CTF/test/test_ctf_io_trd.cxx +++ b/Detectors/CTF/test/test_ctf_io_trd.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test TRDCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "TRDReconstruction/CTFCoder.h" #include "DataFormatsTRD/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::trd; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector triggers; std::vector tracklets; @@ -70,6 +79,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, triggers, tracklets, digits); // compress } sw.Stop(); diff --git a/Detectors/CTF/test/test_ctf_io_zdc.cxx b/Detectors/CTF/test/test_ctf_io_zdc.cxx index 6222951c080e6..3ae6f5b991ad1 100644 --- a/Detectors/CTF/test/test_ctf_io_zdc.cxx +++ b/Detectors/CTF/test/test_ctf_io_zdc.cxx @@ -12,7 +12,13 @@ #define BOOST_TEST_MODULE Test ZDCCTFIO #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK + +#undef NDEBUG +#include + #include +#include +#include #include "CommonUtils/NameConf.h" #include "ZDCReconstruction/CTFCoder.h" #include "DataFormatsZDC/CTF.h" @@ -24,8 +30,11 @@ #include using namespace o2::zdc; +namespace boost_data = boost::unit_test::data; + +inline std::vector ANSVersions{o2::ctf::ANSVersionCompat, o2::ctf::ANSVersion1}; -BOOST_AUTO_TEST_CASE(CTFTest) +BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions), ansVersion) { std::vector bcdata; std::vector chandata; @@ -72,7 +81,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) pedsdata[i].ir = irPed; for (int ic = 0; ic < NChannels; ic++) { pedsdata[i].data[ic] = gRandom->Integer(0xffff); - pedsdata[i].scaler[ic] = (i > 0 ? pedsdata[i].scaler[ic - 1] : 0) + gRandom->Integer(20); + pedsdata[i].scaler[ic] = (ic > 0 ? pedsdata[i].scaler[ic - 1] : 0) + gRandom->Integer(20); } irPed.orbit++; } @@ -81,6 +90,7 @@ BOOST_AUTO_TEST_CASE(CTFTest) std::vector vec; { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); + coder.setANSVersion(ansVersion); coder.encode(vec, bcdata, chandata, pedsdata); // compress } sw.Stop(); diff --git a/Detectors/CTF/utils/CMakeLists.txt b/Detectors/CTF/utils/CMakeLists.txt index 025f7586a0e45..f1b55e027f64a 100644 --- a/Detectors/CTF/utils/CMakeLists.txt +++ b/Detectors/CTF/utils/CMakeLists.txt @@ -29,3 +29,7 @@ o2_add_test_root_macro(dumpCTF.C o2_add_test_root_macro(CTFdict2CCDBfiles.C PUBLIC_LINK_LIBRARIES O2::CTFWorkflow fmt::fmt LABELS ctf COMPILE_ONLY) + +o2_add_test_root_macro(convCTFDict.C + PUBLIC_LINK_LIBRARIES O2::CTFWorkflow fmt::fmt + LABELS ctf COMPILE_ONLY) diff --git a/Detectors/CTF/utils/convCTFDict.C b/Detectors/CTF/utils/convCTFDict.C new file mode 100644 index 0000000000000..1b42cda3e2a1b --- /dev/null +++ b/Detectors/CTF/utils/convCTFDict.C @@ -0,0 +1,121 @@ +// Copyright 2019-2023 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "DetectorsCommonDataFormats/CTFHeader.h" +#include "DetectorsCommonDataFormats/CTFDictHeader.h" +#include "DetectorsCommonDataFormats/EncodedBlocks.h" +#include "DataFormatsCTP/CTF.h" +#include "DataFormatsTRD/CTF.h" +#include "DataFormatsTOF/CTF.h" +#include "DataFormatsTPC/CTF.h" +#include "DataFormatsFT0/CTF.h" +#include "DataFormatsFV0/CTF.h" +#include "DataFormatsFDD/CTF.h" +#include "DataFormatsEMCAL/CTF.h" +#include "DataFormatsITSMFT/CTF.h" +#include "DataFormatsPHOS/CTF.h" +#include "DataFormatsZDC/CTF.h" +#include "DataFormatsMID/CTF.h" +#include "DataFormatsMCH/CTF.h" +#include "DataFormatsHMP/CTF.h" +#include "DataFormatsCPV/CTF.h" + +#include +#include + +#endif + +template +size_t appendToTree(TTree& tree, const std::string brname, T& ptr) +{ + size_t s = 0; + auto* br = tree.GetBranch(brname.c_str()); + auto* pptr = &ptr; + if (br) { + br->SetAddress(&pptr); + } else { + br = tree.Branch(brname.c_str(), &pptr); + } + int res = br->Fill(); + if (res < 0) { + throw std::runtime_error(fmt::format("Failed to fill CTF branch {}", brname)); + } + s += res; + br->ResetAddress(); + return s; +} + +template +void conv(const std::vector* buff) +{ + const auto& ctf = C::getImage(buff->data()); + const auto& ctfhead = ctf.getHeader(); + const auto& dictHead = (const o2::ctf::CTFDictHeader&)ctfhead; + o2::ctf::CTFHeader header{}; + TFile fl(Form("ctf_dictionaryTree_%s_%u_0.root", dictHead.det.getName(), dictHead.dictTimeStamp), "recreate"); + TTree* tree = new TTree("ccdb_object", "O2 CTF dictionary"); + header.detectors.set(dictHead.det); + ctf.appendToTree(*tree, dictHead.det.getName()); + appendToTree(*tree, "CTFHeader", header); + tree->SetEntries(1); + tree->Write(tree->GetName(), TObject::kSingleKey); + delete tree; + fl.Close(); + printf("Stored dictionary to %s\n", fl.GetName()); +} + +void convCTFDict(const char* det, long ts = 0, const char* ccdburl = "http://alice-ccdb.cern.ch") +{ + auto& cm = o2::ccdb::BasicCCDBManager::instance(); + if (ts > 0) { + cm.setTimestamp(ts); + } + std::string pth = std::string(Form("%s/Calib/CTFDictionary", det)); + const auto* buff = cm.get>(pth); + if (!buff) { + printf("Failed to fetch from %s for %ld\n", pth.c_str(), cm.getTimestamp()); + } + std::string dets{det}; + if (dets == "ITS" || dets == "MFT") { + conv(buff); + } else if (dets == "TPC") { + conv(buff); + } else if (dets == "TRD") { + conv(buff); + } else if (dets == "TOF") { + conv(buff); + } else if (dets == "FT0") { + conv(buff); + } else if (dets == "FV0") { + conv(buff); + } else if (dets == "FDD") { + conv(buff); + } else if (dets == "EMC") { + conv(buff); + } else if (dets == "PHS") { + conv(buff); + } else if (dets == "ZDC") { + conv(buff); + } else if (dets == "MID") { + conv(buff); + } else if (dets == "MCH") { + conv(buff); + } else if (dets == "HMP") { + conv(buff); + } else if (dets == "CTP") { + conv(buff); + } else if (dets == "CPV") { + conv(buff); + } +} diff --git a/Detectors/CTF/workflow/CMakeLists.txt b/Detectors/CTF/workflow/CMakeLists.txt index fd00cf98e348b..f7adeaed991c0 100644 --- a/Detectors/CTF/workflow/CMakeLists.txt +++ b/Detectors/CTF/workflow/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC) + o2_add_library(CTFWorkflow SOURCES src/CTFWriterSpec.cxx src/CTFReaderSpec.cxx @@ -54,5 +56,10 @@ o2_add_executable(writer-workflow o2_add_executable(reader-workflow SOURCES src/ctf-reader-workflow.cxx COMPONENT_NAME ctf + TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::CTFWorkflow) +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 501541f25cf27..081e6cf4d968a 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -31,8 +31,12 @@ struct CTFReaderInp { std::string remoteRegex{}; std::string metricChannel{}; std::string fileIRFrames{}; + std::string fileRunTimeSpans{}; + std::string dictOpt{}; std::vector ctfIDs{}; + bool reverseCTFIDs{false}; bool skipSkimmedOutTF = false; + bool invertIRFramesSelection = false; bool allowMissingDetectors = false; bool checkTFLimitBeforeReading = false; bool sup0xccdb = false; @@ -40,10 +44,12 @@ struct CTFReaderInp { int64_t delay_us = 0; int maxLoops = 0; int maxTFs = -1; + int maxTFsPerFile = -1; unsigned int subspec = 0; unsigned int decSSpecEMC = 0; int tfRateLimit = -999; size_t minSHM = 0; + bool shuffle{false}; }; /// create a processor spec diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index e6a231b464e03..4100ebb37c61d 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -11,9 +11,14 @@ /// @file CTFReaderSpec.cxx +#include #include +#include +#include + #include #include +#include #include "Framework/Logger.h" #include "Framework/ControlService.h" @@ -45,8 +50,10 @@ #include "DataFormatsZDC/CTF.h" #include "DataFormatsHMP/CTF.h" #include "DataFormatsCTP/CTF.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" #include "Algorithm/RangeTokenizer.h" -#include #include using namespace o2::framework; @@ -81,6 +88,8 @@ class CTFReaderSpec : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final; private: + void runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo); + void loadRunTimeSpans(const std::string& flname); void openCTFFile(const std::string& flname); bool processTF(ProcessingContext& pc); void checkTreeEntries(); @@ -91,15 +100,22 @@ class CTFReaderSpec : public o2::framework::Task void tryToFixCTFHeader(CTFHeader& ctfHeader) const; CTFReaderInp mInput{}; o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector + std::map>> mRunTimeRanges; std::unique_ptr mFileFetcher; std::unique_ptr mCTFFile; std::unique_ptr mCTFTree; bool mRunning = false; bool mUseLocalTFCounter = false; + bool mIFRamesOut = false; + int mConvRunTimeRangesToOrbits = -1; // not defined yet int mCTFCounter = 0; + int mCTFCounterAcc = 0; int mNFailedFiles = 0; int mFilesRead = 0; - int mTFLength = 128; + int mTFLength = 32; + int mNWaits = 0; + int mRunNumberPrev = -1; + long mTotalWaitTime = 0; long mLastSendTime = 0L; long mCurrTreeEntry = 0L; long mImposeRunStartMS = 0L; @@ -127,8 +143,8 @@ void CTFReaderSpec::stopReader() return; } LOGP(info, "CTFReader stops processing, {} files read, {} files failed", mFilesRead - mNFailedFiles, mNFailedFiles); - LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops()); + LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs ({} accepted) in {} loops, spent {:.2} s in {} data waiting states", + mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mCTFCounterAcc, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -143,9 +159,16 @@ void CTFReaderSpec::stopReader() void CTFReaderSpec::init(InitContext& ic) { mInput.ctfIDs = o2::RangeTokenizer::tokenize(ic.options().get("select-ctf-ids")); + if ((mInput.reverseCTFIDs = ic.options().get("reverse-select-ctf-ids"))) { + std::reverse(mInput.ctfIDs.begin(), mInput.ctfIDs.end()); + } mUseLocalTFCounter = ic.options().get("local-tf-counter"); mImposeRunStartMS = ic.options().get("impose-run-start-timstamp"); mInput.checkTFLimitBeforeReading = ic.options().get("limit-tf-before-reading"); + mInput.maxTFs = ic.options().get("max-tf"); + mInput.maxTFs = mInput.maxTFs > 0 ? mInput.maxTFs : 0x7fffffff; + mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); + mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mRunning = true; mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); @@ -157,7 +180,114 @@ void CTFReaderSpec::init(InitContext& ic) const auto& hbfu = o2::raw::HBFUtils::Instance(); mTFLength = hbfu.nHBFPerTF; LOGP(info, "IRFrames will be selected from {}, assumed TF length: {} HBF", mInput.fileIRFrames, mTFLength); + mIFRamesOut = true; + } + if (!mInput.fileRunTimeSpans.empty()) { + loadRunTimeSpans(mInput.fileRunTimeSpans); + mIFRamesOut = true; + } +} + +void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) +{ + // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit + mIRFrameSelector.clear(); + auto ent = mRunTimeRanges.find(timingInfo.runNumber); + if (ent == mRunTimeRanges.end()) { + LOGP(info, "RunTimeRanges selection was provided but run {} has no entries, all TFs will be processed", timingInfo.runNumber); + return; + } + o2::parameters::AggregatedRunInfo rinfo; + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + rinfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb, timingInfo.runNumber); + if (rinfo.runNumber != timingInfo.runNumber || rinfo.orbitsPerTF < 1) { + LOGP(fatal, "failed to extract AggregatedRunInfo for run {}", timingInfo.runNumber); + } + mTFLength = rinfo.orbitsPerTF; + std::vector frames; + for (const auto& rng : ent->second) { + long orbMin = 0, orbMax = 0; + if (mConvRunTimeRangesToOrbits > 0) { + orbMin = rinfo.orbitSOR + (rng.first - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + orbMax = rinfo.orbitSOR + (rng.second - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + } else { + orbMin = rng.first; + orbMax = rng.second; + } + if (orbMin < 0) { + orbMin = 0; + } + if (orbMax < 0) { + orbMax = 0; + } + if (timingInfo.runNumber > 523897) { + orbMin = (orbMin / rinfo.orbitsPerTF) * rinfo.orbitsPerTF; + orbMax = (orbMax / rinfo.orbitsPerTF + 1) * rinfo.orbitsPerTF - 1; + } + LOGP(info, "TFs overlapping with orbits {}:{} will be {}", orbMin, orbMax, mInput.invertIRFramesSelection ? "rejected" : "selected"); + frames.emplace_back(InteractionRecord{0, uint32_t(orbMin)}, InteractionRecord{o2::constants::lhc::LHCMaxBunches, uint32_t(orbMax)}); + } + mIRFrameSelector.setOwnList(frames, true); +} + +void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) +{ + std::ifstream inputFile(flname); + if (!inputFile) { + LOGP(fatal, "Failed to open selected run/timespans file {}", flname); + } + std::string line; + size_t cntl = 0, cntr = 0; + while (std::getline(inputFile, line)) { + cntl++; + for (char& ch : line) { // Replace semicolons and tabs with spaces for uniform processing + if (ch == ';' || ch == '\t' || ch == ',') { + ch = ' '; + } + } + o2::utils::Str::trim(line); + if (line.size() < 1 || line[0] == '#') { + continue; + } + auto tokens = o2::utils::Str::tokenize(line, ' '); + auto logError = [&cntl, &line]() { LOGP(error, "Expected format for selection is tripplet , failed on line#{}: {}", cntl, line); }; + if (tokens.size() >= 3) { + int run = 0; + long rmin, rmax; + try { + run = std::stoi(tokens[0]); + rmin = std::stol(tokens[1]); + rmax = std::stol(tokens[2]); + } catch (...) { + logError(); + continue; + } + + constexpr long ISTimeStamp = 1514761200000L; + int convmn = rmin > ISTimeStamp ? 1 : 0, convmx = rmax > ISTimeStamp ? 1 : 0; // values above ISTimeStamp are timestamps (need to be converted to orbits) + if (rmin > rmax) { + LOGP(fatal, "Provided range limits are not in increasing order, entry is {}", line); + } + if (mConvRunTimeRangesToOrbits == -1) { + if (convmn != convmx) { + LOGP(fatal, "Provided range limits should be both consistent either with orbit number or with unix timestamp in ms, entry is {}", line); + } + mConvRunTimeRangesToOrbits = convmn; // need to convert to orbit if time + LOGP(info, "Interpret selected time-spans input as {}", mConvRunTimeRangesToOrbits == 1 ? "timstamps(ms)" : "orbits"); + } else { + if (mConvRunTimeRangesToOrbits != convmn || mConvRunTimeRangesToOrbits != convmx) { + LOGP(fatal, "Provided range limits should are not consistent with previously determined {} input, entry is {}", mConvRunTimeRangesToOrbits == 1 ? "timestamps" : "orbits", line); + } + } + + mRunTimeRanges[run].emplace_back(rmin, rmax); + cntr++; + } else { + logError(); + } } + LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), flname); + inputFile.close(); } ///_______________________________________ @@ -167,11 +297,35 @@ void CTFReaderSpec::openCTFFile(const std::string& flname) mFilesRead++; mCTFFile.reset(TFile::Open(flname.c_str())); if (!mCTFFile || !mCTFFile->IsOpen() || mCTFFile->IsZombie()) { - throw std::runtime_error("failed to open CTF file"); + throw std::runtime_error(fmt::format("failed to open CTF file {}, skipping", flname)); } mCTFTree.reset((TTree*)mCTFFile->Get(std::string(o2::base::NameConf::CTFTREENAME).c_str())); if (!mCTFTree) { - throw std::runtime_error("failed to load CTF tree from"); + throw std::runtime_error(fmt::format("failed to load CTF tree from {}, skipping", flname)); + } + if (mCTFTree->GetEntries() < 1) { + throw std::runtime_error(fmt::format("CTF tree in {} has 0 entries, skipping", flname)); + } + if (mInput.shuffle) { + if (mInput.ctfIDs.empty()) { + int entries = mCTFTree->GetEntries(); + if (mInput.maxTFs > 0) { + entries = std::min(entries, mInput.maxTFs); + } + if (mInput.maxTFsPerFile > 0) { + entries = std::min(entries, mInput.maxTFsPerFile); + } + mInput.ctfIDs.clear(); + mInput.ctfIDs.resize(entries); + std::iota(mInput.ctfIDs.begin(), mInput.ctfIDs.end(), 0); + } + std::random_device dev; + std::mt19937 gen{dev()}; + std::shuffle(mInput.ctfIDs.begin(), mInput.ctfIDs.end(), gen); + LOGP(info, "will shuffle reading of CTF entries in this order:"); + for (int i{0}; i < (int)mInput.ctfIDs.size(); ++i) { + LOGP(info, "\tTF {:02} -> {:02}", i, mInput.ctfIDs[i]); + } } } catch (const std::exception& e) { LOG(error) << "Cannot process " << flname << ", reason: " << e.what(); @@ -192,15 +346,16 @@ void CTFReaderSpec::run(ProcessingContext& pc) mInput.tfRateLimit = std::stoi(pc.services().get().device()->fConfig->GetValue("timeframes-rate-limit")); } std::string tfFileName; - if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done - LOG(info) << "All CTFs from selected range were injected, stopping"; - mRunning = false; - } + bool waitAcknowledged = false; + long startWait = 0; while (mRunning) { - if (mCTFTree) { // there is a tree open with multiple CTF - if (mInput.ctfIDs.empty() || mInput.ctfIDs[mSelIDEntry] == mCTFCounter) { // no selection requested or matching CTF ID is found + if (mCTFTree) { // there is a tree open with multiple CTF + if (mInput.ctfIDs.empty() || mInput.ctfIDs[mSelIDEntry] == mCTFCounter || mInput.shuffle || mInput.reverseCTFIDs) { // no selection requested or matching CTF ID is found LOG(debug) << "TF " << mCTFCounter << " of " << mInput.maxTFs << " loop " << mFileFetcher->getNLoops(); + if (mInput.shuffle || mInput.reverseCTFIDs) { + mCurrTreeEntry = mInput.ctfIDs[mSelIDEntry]; + } mSelIDEntry++; if (processTF(pc)) { break; @@ -219,16 +374,47 @@ void CTFReaderSpec::run(ProcessingContext& pc) mRunning = false; break; } + if (!waitAcknowledged) { + startWait = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + waitAcknowledged = true; + } pc.services().get().waitFor(5); continue; } + if (waitAcknowledged) { + long waitTime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - startWait; + mTotalWaitTime += waitTime; + if (++mNWaits > 1) { + LOGP(warn, "Resuming reading after waiting for data {:.2} s (accumulated {:.2} s delay in {} waits)", 1e-6 * waitTime, 1e-6 * mTotalWaitTime, mNWaits); + } + waitAcknowledged = false; + } LOG(info) << "Reading CTF input " << ' ' << tfFileName; openCTFFile(tfFileName); } + + if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done + LOGP(info, "All CTFs from selected range were injected, stopping"); + mRunning = false; + } else if (mRunning && !mCTFTree && mFileFetcher->getNextFileInQueue().empty() && !mFileFetcher->isRunning()) { // previous tree was done, can we read more? + mRunning = false; + } + if (!mRunning) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); stopReader(); + const std::string dummy{"ctf_read_ntf.txt"}; + if (mCTFCounterAcc == 0) { + LOGP(warn, "No TF passed selection, writing a 0 to file {}", dummy); + } + try { + std::ofstream outfile; + outfile.open(dummy, std::ios::out | std::ios::trunc); + outfile << mCTFCounterAcc << std::endl; + } catch (...) { + LOGP(error, "Failed to write {}", dummy); + } } } @@ -251,7 +437,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) } if (mUseLocalTFCounter) { - ctfHeader.tfCounter = mCTFCounter; + ctfHeader.tfCounter = mCTFCounterAcc; } LOG(info) << ctfHeader; @@ -262,27 +448,35 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) timingInfo.tfCounter = ctfHeader.tfCounter; timingInfo.runNumber = ctfHeader.run; + if (mRunTimeRanges.size() && timingInfo.runNumber != mRunNumberPrev) { + runTimeRangesToIRFrameSelector(timingInfo); + } + mRunNumberPrev = timingInfo.runNumber; + gsl::span irSpan{}; if (mIRFrameSelector.isSet()) { o2::InteractionRecord ir0(0, timingInfo.firstTForbit); - // we cannot have GRPECS via DPL CCDB fetcher in the CTFReader, so we use mTFLength extracted from the HBFUtils o2::InteractionRecord ir1(o2::constants::lhc::LHCMaxBunches - 1, timingInfo.firstTForbit < 0xffffffff - (mTFLength - 1) ? timingInfo.firstTForbit + (mTFLength - 1) : 0xffffffff); - auto irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); - if (irSpan.size() == 0 && mInput.skipSkimmedOutTF) { - LOGP(info, "Skimming did not define any selection for TF [{}] : [{}]", ir0.asString(), ir1.asString()); + irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); + bool acc = true; + if (mInput.skipSkimmedOutTF) { + acc = (irSpan.size() > 0) ? !mInput.invertIRFramesSelection : mInput.invertIRFramesSelection; + LOGP(info, "IRFrame selection contains {} frames for TF [{}] : [{}]: {}use this TF (selection inversion mode is {})", + irSpan.size(), ir0.asString(), ir1.asString(), acc ? "" : "do not ", mInput.invertIRFramesSelection ? "ON" : "OFF"); + } + if (!acc) { return false; - } else { - if (mInput.checkTFLimitBeforeReading) { - limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); - } - LOGP(info, "{} IR-Frames are selected for TF [{}] : [{}]", irSpan.size(), ir0.asString(), ir1.asString()); } - auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); + if (mInput.checkTFLimitBeforeReading) { + limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); + } } else { if (mInput.checkTFLimitBeforeReading) { limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); } } - + if (mIFRamesOut) { + auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); + } // send CTF Header pc.outputs().snapshot({"header", mInput.subspec}, ctfHeader); @@ -302,6 +496,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) processDetector(DetID::CPV, ctfHeader, pc); processDetector(DetID::ZDC, ctfHeader, pc); processDetector(DetID::CTP, ctfHeader, pc); + mCTFCounterAcc++; // send sTF acknowledge message if (!mInput.sup0xccdb) { @@ -336,8 +531,13 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) ///_______________________________________ void CTFReaderSpec::checkTreeEntries() { - // check if the tree has entries left, if needed, close current tree/file - if (++mCurrTreeEntry >= mCTFTree->GetEntries()) { // this file is done, check if there are other files + bool reachedEnd{false}; + if (mInput.shuffle || mInput.reverseCTFIDs) { // last entry is last id + reachedEnd = (mCurrTreeEntry == mInput.ctfIDs.back()); + } else { // check if the tree has entries left, if needed, close current tree/file + reachedEnd = (++mCurrTreeEntry >= mCTFTree->GetEntries()); + } + if (reachedEnd || (mInput.maxTFsPerFile > 0 && mCurrTreeEntry >= mInput.maxTFsPerFile)) { // this file is done, check if there are other files mCTFTree.reset(); mCTFFile->Close(); mCTFFile.reset(); @@ -439,18 +639,21 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); } } - if (!inp.fileIRFrames.empty()) { + if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { outputs.emplace_back(OutputLabel{"selIRFrames"}, "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } if (!inp.sup0xccdb) { outputs.emplace_back(OutputSpec{{"TFDist"}, o2::header::gDataOriginFLP, o2::header::gDataDescriptionDISTSTF, 0xccdb}); } - options.emplace_back(ConfigParamSpec{"select-ctf-ids", VariantType::String, "", {"comma-separated list CTF IDs to inject (from cumulative counter of CTFs seen)"}}); + options.emplace_back(ConfigParamSpec{"reverse-select-ctf-ids", VariantType::Bool, false, {"reverse order of to inject CTF IDs"}}); options.emplace_back(ConfigParamSpec{"impose-run-start-timstamp", VariantType::Int64, 0L, {"impose run start time stamp (ms), ignored if 0"}}); options.emplace_back(ConfigParamSpec{"local-tf-counter", VariantType::Bool, false, {"reassign header.tfCounter from local TF counter"}}); options.emplace_back(ConfigParamSpec{"fetch-failure-threshold", VariantType::Float, 0.f, {"Fail if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); options.emplace_back(ConfigParamSpec{"limit-tf-before-reading", VariantType::Bool, false, {"Check TF limiting before reading new TF, otherwhise before injecting it"}}); + options.emplace_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); + options.emplace_back(ConfigParamSpec{"max-tf-per-file", VariantType::Int, -1, {"max TFs to process per ctf file (<= 0 : infinite)"}}); + if (!inp.metricChannel.empty()) { options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, inp.metricChannel, {"Out-of-band channel config for TF throttling"}}); } diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index d9d3afcfeaefa..ba4542969a712 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -44,7 +44,10 @@ #include "DataFormatsCPV/CTF.h" #include "DataFormatsZDC/CTF.h" #include "DataFormatsCTP/CTF.h" -#include "rANS/rans.h" + +#include "rANS/histogram.h" +#include "rANS/compat.h" + #include #include #include @@ -59,6 +62,7 @@ #include #include #include +#include using namespace o2::framework; @@ -88,7 +92,7 @@ size_t appendToTree(TTree& tree, const std::string brname, T& ptr) } using DetID = o2::detectors::DetID; -using FTrans = o2::rans::FrequencyTable; +using FTrans = o2::rans::DenseHistogram; class CTFWriterSpec : public o2::framework::Task { @@ -125,23 +129,29 @@ class CTFWriterSpec : public o2::framework::Task bool mStoreMetaFile = false; bool mRejectCurrentTF = false; bool mFallBackDirUsed = false; + bool mFallBackDirProvided = false; int mReportInterval = -1; int mVerbosity = 0; int mSaveDictAfter = 0; // if positive and mWriteCTF==true, save dictionary after each mSaveDictAfter TFs processed uint32_t mPrevDictTimeStamp = 0; // timestamp of the previously stored dictionary uint32_t mDictTimeStamp = 0; // timestamp of the currently stored dictionary - size_t mMinSize = 0; // if > 0, accumulate CTFs in the same tree until the total size exceeds this minimum - size_t mMaxSize = 0; // if > MinSize, and accumulated size will exceed this value, stop accumulation (even if mMinSize is not reached) - size_t mChkSize = 0; // if > 0 and fallback storage provided, reserve this size per CTF file in production on primary storage - size_t mAccCTFSize = 0; // so far accumulated size (if any) - size_t mCurrCTFSize = 0; // size of currently processed CTF - size_t mNCTF = 0; // total number of CTFs written - size_t mNCTFPrevDict = 0; // total number of CTFs used for previous dictionary version - size_t mNAccCTF = 0; // total number of CTFs accumulated in the current file - size_t mCTFAutoSave = 0; // if > 0, autosave after so many TFs - size_t mNCTFFiles = 0; // total number of CTF files written - int mMaxCTFPerFile = 0; // max CTFs per files to store - int mRejRate = 0; // CTF rejection rule (>0: percentage to reject randomly, <0: reject if timeslice%|value|!=0) + size_t mMinSize = 0; // if > 0, accumulate CTFs in the same tree until the total size exceeds this minimum + size_t mMaxSize = 0; // if > MinSize, and accumulated size will exceed this value, stop accumulation (even if mMinSize is not reached) + size_t mChkSize = 0; // if > 0 and fallback storage provided, reserve this size per CTF file in production on primary storage + size_t mAccCTFSize = 0; // so far accumulated size (if any) + size_t mCurrCTFSize = 0; // size of currently processed CTF + size_t mNCTF = 0; // total number of CTFs written + size_t mNCTFPrevDict = 0; // total number of CTFs used for previous dictionary version + size_t mNAccCTF = 0; // total number of CTFs accumulated in the current file + int mWaitDiskFull = 0; // if mCheckDiskFull triggers, pause for this amount of ms before new attempt + int mWaitDiskFullMax = -1; // produce fatal mCheckDiskFull block the workflow for more than this time (in ms) + float mCheckDiskFull = 0.; // wait for if available abs. disk space is < mCheckDiskFull (if >0) or if its fraction is < -mCheckDiskFull (if <0) + long mCTFAutoSave = 0; // if > 0, autosave after so many TFs + size_t mNCTFFiles = 0; // total number of CTF files written + int mMaxCTFPerFile = 0; // max CTFs per files to store + int mRejRate = 0; // CTF rejection rule (>0: percentage to reject randomly, <0: reject if timeslice%|value|!=0) + int mCTFFileCompression = 0; // CTF file compression level (if >= 0) + bool mFillMD5 = false; std::vector mTFOrbits{}; // 1st orbits of TF accumulated in current file o2::framework::DataTakingContext mDataTakingContext{}; o2::framework::TimingInfo mTimingInfo{}; @@ -210,11 +220,13 @@ void CTFWriterSpec::init(InitContext& ic) } mSaveDictAfter = ic.options().get("save-dict-after"); - mCTFAutoSave = ic.options().get("save-ctf-after"); + mCTFAutoSave = ic.options().get("save-ctf-after"); + mCTFFileCompression = ic.options().get("ctf-file-compression"); mCTFMetaFileDir = ic.options().get("meta-output-dir"); if (mCTFMetaFileDir != "/dev/null") { mCTFMetaFileDir = o2::utils::Str::rectifyDirectory(mCTFMetaFileDir); mStoreMetaFile = true; + mFillMD5 = ic.options().get("md5-for-meta"); } mDictDir = o2::utils::Str::rectifyDirectory(ic.options().get("ctf-dict-dir")); mCTFDir = ic.options().get("output-dir"); @@ -227,6 +239,7 @@ void CTFWriterSpec::init(InitContext& ic) mCTFDirFallBack = ic.options().get("output-dir-alt"); if (mCTFDirFallBack != "/dev/null") { mCTFDirFallBack = o2::utils::Str::rectifyDirectory(mCTFDirFallBack); + mFallBackDirProvided = true; } mCreateRunEnvDir = !ic.options().get("ignore-partition-run-dir"); mMinSize = ic.options().get("min-file-size"); @@ -247,6 +260,11 @@ void CTFWriterSpec::init(InitContext& ic) } } } + + mCheckDiskFull = ic.options().get("require-free-disk"); + mWaitDiskFull = 1000 * ic.options().get("wait-for-free-disk"); + mWaitDiskFullMax = 1000 * ic.options().get("max-wait-for-free-disk"); + mChkSize = std::max(size_t(mMinSize * 1.1), mMaxSize); o2::utils::createDirectoriesIfAbsent(LOCKFileDir); @@ -302,7 +320,7 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det sz = ctfBuffer.size(); } if (mCreateDict) { - if (!mFreqsAccumulation[det].size()) { + if (mFreqsAccumulation[det].empty()) { mFreqsAccumulation[det].resize(C::getNBlocks()); mFreqsMetaData[det].resize(C::getNBlocks()); } @@ -328,8 +346,13 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det } return true; }()) { - auto newProbBits = static_cast(o2::rans::computeRenormingPrecision(freq)); - mdSave = o2::ctf::Metadata{0, 0, md.messageWordSize, md.coderType, md.streamSize, newProbBits, md.opt, freq.getMinSymbol(), freq.getMaxSymbol(), static_cast(freq.size()), 0, 0}; + auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); + auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); + mdSave = ctf::detail::makeMetadataRansDict(newProbBits, + static_cast(histogramView.getMin()), + static_cast(histogramView.getMax()), + static_cast(histogramView.size()), + md.opt); mFreqsAccumulation[det][ib] = std::move(freq); } } @@ -416,35 +439,72 @@ void CTFWriterSpec::run(ProcessingContext& pc) mCurrCTFSize = estimateCTFSize(pc); if (mWriteCTF && !mRejectCurrentTF) { prepareTFTreeAndFile(); + + int totalWait = 0, nwaitCycles = 0; + while ((mFallBackDirUsed || !mFallBackDirProvided) && mCheckDiskFull) { // we are on the physical disk and not on the RAM disk + constexpr size_t MB = 1024 * 1024; + constexpr int showFirstN = 10, prsecaleWarnings = 50; + try { + const auto si = std::filesystem::space(mCTFFileOut->GetName()); + std::string wmsg{}; + if (mCheckDiskFull > 0.f && si.available < mCheckDiskFull) { + nwaitCycles++; + wmsg = fmt::format("Disk has {} MB available while at least {} MB is requested, wait for {} ms (on top of {} ms)", si.available / MB, size_t(mCheckDiskFull) / MB, mWaitDiskFull, totalWait); + } else if (mCheckDiskFull < 0.f && float(si.available) / si.capacity < -mCheckDiskFull) { // relative margin requested + nwaitCycles++; + wmsg = fmt::format("Disk has {:.3f}% available while at least {:.3f}% is requested, wait for {} ms (on top of {} ms)", si.capacity ? float(si.available) / si.capacity * 100.f : 0., -mCheckDiskFull, mWaitDiskFull, totalWait); + } else { + nwaitCycles = 0; + } + if (nwaitCycles) { + if (mWaitDiskFullMax > 0 && totalWait > mWaitDiskFullMax) { + closeTFTreeAndFile(); // try to save whatever we have + LOGP(fatal, "Disk has {} MB available out of {} MB after waiting for {} ms", si.available / MB, si.capacity / MB, mWaitDiskFullMax); + } + if (nwaitCycles < showFirstN + 1 || (prsecaleWarnings && (nwaitCycles % prsecaleWarnings) == 0)) { + LOG(alarm) << wmsg; + } + pc.services().get().waitFor((unsigned int)(mWaitDiskFull)); + totalWait += mWaitDiskFull; + continue; + } + } catch (std::exception const& e) { + LOG(fatal) << "unable to query disk space info for path " << mCurrentCTFFileNameFull << ", reason: " << e.what(); + } + break; + } } // create header CTFHeader header{mTimingInfo.runNumber, mTimingInfo.creation, mTimingInfo.firstTForbit, mTimingInfo.tfCounter}; size_t szCTF = 0; mSizeReport = ""; - szCTF += processDet(pc, DetID::ITS, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TPC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TRD, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::TOF, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::PHS, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::CPV, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::EMC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::HMP, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MFT, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MCH, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::MID, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::ZDC, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FT0, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FV0, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); - szCTF += processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); + std::array szCTFperDet{0}; // DetID::TST is between FDD and CTP and remains empty + szCTFperDet[DetID::ITS] = processDet(pc, DetID::ITS, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TPC] = processDet(pc, DetID::TPC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TRD] = processDet(pc, DetID::TRD, header, mCTFTreeOut.get()); + szCTFperDet[DetID::TOF] = processDet(pc, DetID::TOF, header, mCTFTreeOut.get()); + szCTFperDet[DetID::PHS] = processDet(pc, DetID::PHS, header, mCTFTreeOut.get()); + szCTFperDet[DetID::CPV] = processDet(pc, DetID::CPV, header, mCTFTreeOut.get()); + szCTFperDet[DetID::EMC] = processDet(pc, DetID::EMC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::HMP] = processDet(pc, DetID::HMP, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MFT] = processDet(pc, DetID::MFT, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MCH] = processDet(pc, DetID::MCH, header, mCTFTreeOut.get()); + szCTFperDet[DetID::MID] = processDet(pc, DetID::MID, header, mCTFTreeOut.get()); + szCTFperDet[DetID::ZDC] = processDet(pc, DetID::ZDC, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FT0] = processDet(pc, DetID::FT0, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FV0] = processDet(pc, DetID::FV0, header, mCTFTreeOut.get()); + szCTFperDet[DetID::FDD] = processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); + szCTFperDet[DetID::CTP] = processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); + szCTF = std::accumulate(szCTFperDet.begin(), szCTFperDet.end(), 0); if (mReportInterval > 0 && (mTimingInfo.tfCounter % mReportInterval) == 0) { - LOGP(important, "CTF {} size report:{}", mTimingInfo.tfCounter, mSizeReport); + LOGP(important, "CTF {} size report:{} - Total:{}", mTimingInfo.tfCounter, mSizeReport, fmt::group_digits(szCTF)); } mTimer.Stop(); if (mWriteCTF && !mRejectCurrentTF) { szCTF += appendToTree(*mCTFTreeOut.get(), "CTFHeader", header); + size_t prevSizeMB = mAccCTFSize / (1 << 20); mAccCTFSize += szCTF; mCTFTreeOut->SetEntries(++mNAccCTF); mTFOrbits.push_back(mTimingInfo.firstTForbit); @@ -462,7 +522,7 @@ void CTFWriterSpec::run(ProcessingContext& pc) if (mAccCTFSize >= mMinSize || (mMaxCTFPerFile > 0 && mNAccCTF >= mMaxCTFPerFile)) { closeTFTreeAndFile(); - } else if (mCTFAutoSave > 0 && mNAccCTF % mCTFAutoSave == 0) { + } else if ((mCTFAutoSave > 0 && mNAccCTF % mCTFAutoSave == 0) || (mCTFAutoSave < 0 && int(prevSizeMB / (-mCTFAutoSave)) != size_t(mAccCTFSize / (1 << 20)) / (-mCTFAutoSave))) { mCTFTreeOut->AutoSave("override"); } } else { @@ -473,6 +533,9 @@ void CTFWriterSpec::run(ProcessingContext& pc) if (mCreateDict && mSaveDictAfter > 0 && (mNCTF % mSaveDictAfter) == 0) { storeDictionaries(); } + int dummy = 0; + pc.outputs().snapshot({"ctfdone", 0}, dummy); + pc.outputs().snapshot(Output{"CTF", "SIZES", 0}, szCTFperDet); } //___________________________________________________________________ @@ -515,7 +578,7 @@ void CTFWriterSpec::prepareTFTreeAndFile() closeTFTreeAndFile(); mFallBackDirUsed = false; auto ctfDir = mCTFDir.empty() ? o2::utils::Str::rectifyDirectory("./") : mCTFDir; - if (mChkSize > 0 && (mCTFDirFallBack != "/dev/null")) { + if (mChkSize > 0 && mFallBackDirProvided) { createLockFile(0); auto sz = getAvailableDiskSpace(ctfDir, 0); // check main storage if (sz < mChkSize) { @@ -535,6 +598,9 @@ void CTFWriterSpec::prepareTFTreeAndFile() mCurrentCTFFileName = o2::base::NameConf::getCTFFileName(mTimingInfo.runNumber, mTimingInfo.firstTForbit, mTimingInfo.tfCounter, mHostName); mCurrentCTFFileNameFull = fmt::format("{}{}", ctfDir, mCurrentCTFFileName); mCTFFileOut.reset(TFile::Open(fmt::format("{}{}", mCurrentCTFFileNameFull, TMPFileEnding).c_str(), "recreate")); // to prevent premature external usage, use temporary name + if (mCTFFileCompression >= 0) { + mCTFFileOut->SetCompressionLevel(mCTFFileCompression); + } mCTFTreeOut = std::make_unique(std::string(o2::base::NameConf::CTFTREENAME).c_str(), "O2 CTF tree"); mNCTFFiles++; @@ -551,13 +617,13 @@ void CTFWriterSpec::closeTFTreeAndFile() mCTFTreeOut.reset(); mCTFFileOut->Close(); mCTFFileOut.reset(); - if (!TMPFileEnding.empty()) { - std::filesystem::rename(o2::utils::Str::concat_string(mCurrentCTFFileNameFull, TMPFileEnding), mCurrentCTFFileNameFull); - } // write CTF file metaFile data + auto actualFileName = TMPFileEnding.empty() ? mCurrentCTFFileNameFull : o2::utils::Str::concat_string(mCurrentCTFFileNameFull, TMPFileEnding); if (mStoreMetaFile) { o2::dataformats::FileMetaData ctfMetaData; - ctfMetaData.fillFileData(mCurrentCTFFileNameFull); + if (!ctfMetaData.fillFileData(actualFileName, mFillMD5, TMPFileEnding)) { + throw std::runtime_error("metadata file was requested but not created"); + } ctfMetaData.setDataTakingContext(mDataTakingContext); ctfMetaData.type = mMetaDataType; ctfMetaData.priority = mFallBackDirUsed ? "low" : "high"; @@ -568,10 +634,15 @@ void CTFWriterSpec::closeTFTreeAndFile() std::ofstream metaFileOut(metaFileNameTmp); metaFileOut << ctfMetaData; metaFileOut.close(); + if (!TMPFileEnding.empty()) { + std::filesystem::rename(actualFileName, mCurrentCTFFileNameFull); + } std::filesystem::rename(metaFileNameTmp, metaFileName); } catch (std::exception const& e) { LOG(error) << "Failed to store CTF meta data file " << metaFileName << ", reason: " << e.what(); } + } else if (!TMPFileEnding.empty()) { + std::filesystem::rename(actualFileName, mCurrentCTFFileNameFull); } } catch (std::exception const& e) { LOG(error) << "Failed to finalize CTF file " << mCurrentCTFFileNameFull << ", reason: " << e.what(); @@ -730,19 +801,25 @@ DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outTyp return DataProcessorSpec{ "ctf-writer", inputs, - Outputs{}, + Outputs{{OutputLabel{"ctfdone"}, "CTF", "DONE", 0, Lifetime::Timeframe}, + {"CTF", "SIZES", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask(dets, outType, verbosity, reportInterval)}, // RS FIXME once global/local options clash is solved, --output-type will become device option Options{ //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, - {"save-ctf-after", VariantType::Int, 0, {"if > 0, autosave CTF tree with multiple CTFs after every N CTFs"}}, + {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, + {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, + {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, + {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, + {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, + {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; } diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index c90efc2732536..fc50c971c5d20 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -21,6 +21,7 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/ConfigurableParam.h" #include "Algorithm/RangeTokenizer.h" +#include "DetectorsBase/DPLWorkflowUtils.h" // Specific detectors specs #include "ITSMFTWorkflow/EntropyDecoderSpec.h" @@ -38,6 +39,9 @@ #include "CPVWorkflow/EntropyDecoderSpec.h" #include "ZDCWorkflow/EntropyDecoderSpec.h" #include "CTPWorkflow/EntropyDecoderSpec.h" +#ifdef WITH_OPENMP +#include +#endif using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -48,11 +52,12 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options; options.push_back(ConfigParamSpec{"ctf-input", VariantType::String, "none", {"comma-separated list CTF input files"}}); + options.push_back(ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}); options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::ALL}, {"comma-separated list of detectors to accept. Overrides skipDet"}}); options.push_back(ConfigParamSpec{"skipDet", VariantType::String, std::string{DetID::NONE}, {"comma-separate list of detectors to skip"}}); - options.push_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); + options.push_back(ConfigParamSpec{"shuffle", VariantType::Bool, false, {"shuffle TF sending order (for debug)"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files or no-copy to avoid copying"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access options.push_back(ConfigParamSpec{"ctf-file-regex", VariantType::String, ".*o2_ctf_run.+\\.root$", {"regex string to identify CTF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access @@ -63,7 +68,9 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-data-subspec", VariantType::Int, 0, {"subspec to use for decoded CTF messages (use non-0 if CTF writer will be attached downstream)"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); options.push_back(ConfigParamSpec{"ir-frames-files", VariantType::String, "", {"If non empty, inject selected IRFrames from this file"}}); + options.push_back(ConfigParamSpec{"run-time-span-file", VariantType::String, "", {"If non empty, inject selected IRFrames from this text file (run, min/max orbit or unix time)"}}); options.push_back(ConfigParamSpec{"skip-skimmed-out-tf", VariantType::Bool, false, {"Do not process TFs with empty IR-Frame coverage"}}); + options.push_back(ConfigParamSpec{"invert-irframe-selection", VariantType::Bool, false, {"Select only frames mentioned in ir-frames-file (skip-skimmed-out-tf applied to TF not selected!)"}}); // options.push_back(ConfigParamSpec{"its-digits", VariantType::Bool, false, {"convert ITS clusters to digits"}}); options.push_back(ConfigParamSpec{"mft-digits", VariantType::Bool, false, {"convert MFT clusters to digits"}}); @@ -72,6 +79,7 @@ void customize(std::vector& workflowOptions) // options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); options.push_back(ConfigParamSpec{"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}); + options.push_back(ConfigParamSpec{"combine-devices", VariantType::Bool, false, {"combine multiple DPL devices (entropy decoders)"}}); std::swap(workflowOptions, options); } @@ -111,11 +119,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (ctfInput.delay_us < 0) { ctfInput.delay_us = 0; } - int n = configcontext.options().get("max-tf"); - ctfInput.maxTFs = n > 0 ? n : 0x7fffffff; ctfInput.maxFileCache = std::max(1, configcontext.options().get("max-cached-files")); + ctfInput.shuffle = configcontext.options().get("shuffle"); ctfInput.copyCmd = configcontext.options().get("copy-cmd"); ctfInput.tffileRegex = configcontext.options().get("ctf-file-regex"); ctfInput.remoteRegex = configcontext.options().get("remote-regex"); @@ -123,65 +130,137 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.sup0xccdb = !configcontext.options().get("send-diststf-0xccdb"); ctfInput.minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); ctfInput.fileIRFrames = configcontext.options().get("ir-frames-files"); + ctfInput.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); ctfInput.skipSkimmedOutTF = configcontext.options().get("skip-skimmed-out-tf"); + ctfInput.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); + ctfInput.dictOpt = configcontext.options().get("ctf-dict"); int verbosity = configcontext.options().get("ctf-reader-verbosity"); int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); std::string chanFmt = configcontext.options().get("metric-feedback-channel-format"); if (rateLimitingIPCID > -1 && !chanFmt.empty()) { - ctfInput.metricChannel = fmt::format(chanFmt, o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + ctfInput.metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + } + if (!ctfInput.fileRunTimeSpans.empty()) { + ctfInput.skipSkimmedOutTF = true; + } + if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { + LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); } specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); + auto pipes = configcontext.options().get("pipeline"); + std::unordered_map plines; + auto ptokens = o2::utils::Str::tokenize(pipes, ','); + for (auto& token : ptokens) { + auto split = token.find(":"); + if (split == std::string::npos) { + throw std::runtime_error("bad pipeline definition. Syntax :"); + } + auto key = token.substr(0, split); + token.erase(0, split + 1); + size_t error; + auto value = std::stoll(token, &error, 10); + if (token[error] != '\0') { + throw std::runtime_error("Bad pipeline definition. Expecting integer"); + } + if (value > 1) { + plines[key] = value; + } + } + + std::vector decSpecsV; + + auto addSpecs = [&decSpecsV, &plines](DataProcessorSpec&& s) { + auto entry = plines.find(s.name); + size_t mult = (entry == plines.end() || entry->second < 2) ? 1 : entry->second; + if (mult > decSpecsV.size()) { + decSpecsV.resize(mult); + } + decSpecsV[mult - 1].push_back(s); + }; + // add decoders for all allowed detectors. if (ctfInput.detMask[DetID::ITS]) { - specs.push_back(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec)); + addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::ITS), verbosity, configcontext.options().get("its-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MFT]) { - specs.push_back(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec)); + addSpecs(o2::itsmft::getEntropyDecoderSpec(DetID::getDataOrigin(DetID::MFT), verbosity, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TPC]) { - specs.push_back(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TRD]) { - specs.push_back(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TOF]) { - specs.push_back(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FT0]) { - specs.push_back(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FV0]) { - specs.push_back(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FDD]) { - specs.push_back(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MID]) { - specs.push_back(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MCH]) { - specs.push_back(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec)); + addSpecs(o2::mch::getEntropyDecoderSpec(verbosity, "mch-entropy-decoder", ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::EMC]) { - specs.push_back(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC)); + addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::PHS]) { - specs.push_back(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CPV]) { - specs.push_back(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::ZDC]) { - specs.push_back(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::HMP]) { - specs.push_back(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CTP]) { - specs.push_back(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); + } + + bool combine = configcontext.options().get("combine-devices"); + if (!combine) { + for (auto& decSpecs : decSpecsV) { + for (auto& s : decSpecs) { + specs.push_back(s); + } + } + } else { + std::vector remaining; + if (decSpecsV.size() && decSpecsV[0].size()) { + specs.push_back(specCombiner("EntropyDecoders", decSpecsV[0], remaining)); // processing w/o pipelining + } + bool updatePipelines = false; + for (size_t i = 1; i < decSpecsV.size(); i++) { // add pipelined processes separately, consider combining them to separate groups (need to have modify argument of pipeline option) + if (decSpecsV[i].size() > 1) { + specs.push_back(specCombiner(fmt::format("EntropyDecodersP{}", i + 1), decSpecsV[i], remaining)); // processing pipelining multiplicity i+1 + updatePipelines = true; + pipes += fmt::format(",EntropyDecodersP{}:{}", i + 1, i + 1); + } else { + for (auto& s : decSpecsV[i]) { + specs.push_back(s); + } + } + } + for (auto& s : remaining) { + specs.push_back(s); + } + if (updatePipelines) { + configcontext.options().override("pipeline", pipes); + } } return std::move(specs); diff --git a/Detectors/CTP/macro/CMakeLists.txt b/Detectors/CTP/macro/CMakeLists.txt index 14c8e0a1ee070..8608c1a8b7846 100644 --- a/Detectors/CTP/macro/CMakeLists.txt +++ b/Detectors/CTP/macro/CMakeLists.txt @@ -33,6 +33,10 @@ o2_add_test_root_macro(CheckAOD2CTPDigits.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB LABELS ctp) +o2_add_test_root_macro(CheckAOD2CTPDigitsII.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) o2_add_test_root_macro(TestConfig.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB @@ -41,3 +45,43 @@ o2_add_test_root_macro(dumpCTPRO.C PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP O2::CCDB LABELS ctp) +o2_add_test_root_macro(GetAndSave.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(ReadCTPRunScalersFromFile.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(GetScalersForRun.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(PlotPbLumi.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(PlotOrbit.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(TestFetcher.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(CreateBKForRun.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(CheckCTPConfig.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(GetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) +o2_add_test_root_macro(TestGetRates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CCDB + LABELS ctp) \ No newline at end of file diff --git a/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C b/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C new file mode 100644 index 0000000000000..14e60194bdd82 --- /dev/null +++ b/Detectors/CTP/macro/CheckAOD2CTPDigitsII.C @@ -0,0 +1,183 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file checkAOD2CTPDigits.C +/// \brief create CTP config, test it and add to database +/// \author Roman Lietava + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "TFile.h" +#include "TTree.h" +#include +#include +#include +#include "TKey.h" +#include "TTreeReader.h" +#include "TTreeReaderValue.h" +#include "CommonDataFormat/InteractionRecord.h" +#endif +// To produce CTP digits: +// o2-raw-tf-reader-workflow -b --onlyDet CTP,FT0 --input-data list.txt | o2-ctp-reco-workflow -b --use-verbose-mode +// To produce AOD: +// WORKFLOW_PARAMETERS="AOD" WORKFLOW_DETECTORS="CTP,FT0,FV0" IGNORE_EXISTING_SHMFILES=1 $O2_ROOT/prodtests/full-system-test/run-workflow-on-inputlist.sh TF list.txt +using namespace o2::ctp; +int CheckAOD2CTPDigits(bool files = 1) +{ + if (files == 0) { + return 0; + } + int fRunNumber; + ULong64_t fGlobalBC; + ULong64_t fTriggerMask; + ULong64_t fInputMask; + std::unique_ptr file(TFile::Open("AO2D.root")); + if (file == nullptr) { + std::cout << "Can not open file AO2D.root" << std::endl; + return 1; + } + file->ls(); + TIter keyList(file->GetListOfKeys()); + TKey* key; + TTree* tree; + std::map bc2classmask; + std::map bc2inputmask; + // Find aod trigger info + int i = 0; + while ((key = (TKey*)keyList())) { + std::cout << "loop:" << i << " " << key->GetName() << std::endl; + i++; + std::string name = key->GetName(); + if (name.find("metaData") != std::string::npos) { + std::cout << "Skipping:" << name << std::endl; + continue; + } + tree = (TTree*)file->Get(Form("%s/O2bc_001", key->GetName())); + if (tree != 0) { + std::cout << "found O2bc" << std::endl; + tree->SetBranchAddress("fRunNumber", &fRunNumber); + tree->SetBranchAddress("fGlobalBC", &fGlobalBC); + tree->SetBranchAddress("fTriggerMask", &fTriggerMask); + tree->SetBranchAddress("fInputMask", &fInputMask); + std::cout << "# of entries:" << tree->GetEntries() << std::endl; + int NN = tree->GetEntries(); + int Nloop = NN; + for (int n{0}; n < Nloop; ++n) { + tree->GetEvent(n); + if (fTriggerMask) { + bc2classmask[fGlobalBC] = fTriggerMask; + // std::cout << std::dec << n << " Run:" << fRunNumber << " GBC:" << std::hex << fGlobalBC << " TM:0x" << std::hex << fTriggerMask << " count:" << bc2classmask.count(fGlobalBC) << std::endl; + } + if (fInputMask) { + bc2inputmask[fGlobalBC] = fInputMask; + } + if (1) { + if (fInputMask || fTriggerMask) { + auto ir = o2::InteractionRecord::long2IR(fGlobalBC); + // auto bcc = ir.orbit; + // if(fTriggerMask) std::cout << "===>"; + // std::cout << std::hex << ir.orbit << " " << ir.bc << " " << fInputMask << " " << fTriggerMask << std::dec << " (" << ir.orbit << " " << ir.bc << ")" << std::endl; + } + } + } + } else { + std::cout << "return 1" << std::endl; + return 1; + } + } + // return 0; + // Read CTP digits and check if every class mask in digits is in AOD + // CTP digits + TFile* fileDigits = TFile::Open("ctpdigitsWOD.root"); + // + fileDigits->ls(); + o2::ctp::CTPDigit* dig = new o2::ctp::CTPDigit; + // + // tree->Print(); + TTreeReader reader("o2sim", fileDigits); + TTreeReaderValue> ctpdigs(reader, "CTPDigits"); + // TTreeReaderArray ctpdigs(reader,"CTPDigits"); + bool firstE = true; + // + std::cout << "Doing ctpdigits" << std::endl; + std::map bc2classmaskD; + std::map bc2inputmaskD; + std::bitset<48> tvxmask; + tvxmask.set(2); + int nnotfinp = 0; + int nokinp = 0; + int nnotf = 0; + int nok = 0; + while (reader.Next()) { + if (ctpdigs.GetSetupStatus() < 0) { + std::cout << "Error:" << std::dec << ctpdigs.GetSetupStatus() << " for:" << ctpdigs.GetBranchName() << std::endl; + return 1; + } + // std::cout << "size:" << std::dec << ctpdigs.GetSize() << std::endl; + // std::cout << "size:" << std::dec << ctpdigs->size() << std::endl; + for (auto const& dig : *ctpdigs) { + ULong64_t gbc = dig.intRecord.toLong(); + if (dig.CTPClassMask.count()) { + bc2classmaskD[gbc] = dig.CTPClassMask.to_ullong(); + // int del = 280+17; + // auto it = bc2classmask.find (gbc); + // if(it != bc2classmask.end()) { + if (bc2classmask.count(gbc)) { + // bc2classmask.erase(gbc); + nok++; + // std::cout << std::hex << gbc << " tc aod clsmask:" << bc2classmask[gbc] << " " << dig.CTPClassMask.to_ullong() << std::endl; + // dig.printStream(std::cout); + } else { + std::cout << std::dec << dig.intRecord.orbit << " " << dig.intRecord.bc << " " << std::hex << dig.intRecord.toLong() << " not found " << bc2classmask.count(gbc) << std::endl; + nnotf++; + } + } + if (dig.CTPInputMask.count()) { + bc2inputmaskD[gbc] = dig.CTPInputMask.to_ullong(); + // auto it = bc2inputmask.find (gbc); + // if(it != bc2inputmask.end()) { + if (bc2inputmask.count(gbc)) { + // bc2inputmask.erase(gbc); + nokinp++; + // std::cout << std::hex << gbc << " ir aod inpmask:" << bc2inputmask[gbc] << " " << dig.CTPInputMask.to_ullong() << std::endl; + } else { + std::cout << std::dec << dig.intRecord.orbit << " " << dig.intRecord.bc << " " << std::hex << dig.intRecord.toLong() << " not found " << bc2inputmask.count(gbc) << std::endl; + nnotfinp++; + } + } + } + } + std::cout << "TClasses ===> nok:" << nok << " NOT found in digits:" << nnotf << " left:" << bc2classmask.size() << std::endl; + std::cout << "Inputs ===> nok:" << nokinp << " NOT found in digits:" << nnotfinp << " left:" << bc2inputmask.size() << std::endl; + nok = 0; + nokinp = 0; + nnotf = 0; + nnotfinp = 0; + for (auto const& tm : bc2classmask) { + if (bc2classmaskD.count(tm.first)) { + nok++; + } else { + nnotf++; + } + } + for (auto const& tm : bc2inputmask) { + if (bc2inputmaskD.count(tm.first)) { + nokinp++; + } else { + nnotfinp++; + } + } + + std::cout << "TClasses ===> nok:" << nok << " NOT found in aod:" << nnotf << " left:" << bc2classmaskD.size() << std::endl; + std::cout << "Inputs ===> nok:" << nokinp << " NOT found in aod:" << nnotfinp << " left:" << bc2inputmaskD.size() << std::endl; + return 0; +} diff --git a/Detectors/CTP/macro/CheckCTPConfig.C b/Detectors/CTP/macro/CheckCTPConfig.C new file mode 100644 index 0000000000000..24a5e354f3fcd --- /dev/null +++ b/Detectors/CTP/macro/CheckCTPConfig.C @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CreateCTPConfig.C +/// \brief create CTP config, test it and add to database +/// \author Roman Lietava + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include +#include +#include +#endif +using namespace o2::ctp; +int CheckCTPConfig(std::string cfgRun3str = "/home/rl/backup24/runs/559781.rcfg2", int writeToFile = 0) +{ + // + // run3 config + // + if (cfgRun3str.find(".rcfg") == std::string::npos) { + std::cout << "No file name:" << cfgRun3str << std::endl; + return 1; + } else { + std::string filename = cfgRun3str; + std::ifstream in; + in.open(filename); + if (!in) { + std::cout << "Can not open file:" << filename << std::endl; + return 2; + } + std::stringstream buffer; + buffer << in.rdbuf(); + cfgRun3str = buffer.str(); + } + // + CTPConfiguration ctpcfg; + int ret = ctpcfg.loadConfigurationRun3(cfgRun3str); + ctpcfg.printStream(std::cout); + std::cout << "CTP config done" << std::endl; + // ctpcfg.checkConfigConsistency(); + auto ctpclasses = ctpcfg.getCTPClasses(); + for (auto const& cls : ctpclasses) { + std::cout << cls.descriptor->name << ":" << std::hex << cls.descriptor->getInputsMask() << std::endl; + } + return ret; +} diff --git a/Detectors/CTP/macro/CreateBKForRun.C b/Detectors/CTP/macro/CreateBKForRun.C new file mode 100644 index 0000000000000..06ef9eac5f900 --- /dev/null +++ b/Detectors/CTP/macro/CreateBKForRun.C @@ -0,0 +1,74 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#endif +using namespace o2::ctp; + +void CreateBKForRun() +{ + std::vector runs = {558124, 558126, 558215, 558217, 558221, 558244, 558247}; + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + // + std::string filename = "BKcounters.txt"; + std::ofstream outfile(filename); + if (!outfile) { + Error("", "Failed to open file %s", filename.c_str()); + return; + } + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + for (auto const& runNumber : runs) { + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << runNumber << " Timestamp:" << timeStamp << std::endl; + // + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + auto ctpscalers = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + } + // + ctpscalers->convertRawToO2(); + std::vector& ctpcls = ctpcfg->getCTPClasses(); + std::vector clslist = ctpcfg->getTriggerClassList(); + auto times = ctpscalers->getTimeLimit(); + for (size_t i = 0; i < clslist.size(); i++) { + // std::cout << i << " " << ctpcls[i].name ; + std::array cnts = ctpscalers->getIntegralForClass(i); + if (clslist[i] != (int)cnts[0]) { + LOG(fatal) << "cls list incompatible with counters"; + } + std::cout << std::setw(21) << ctpcls[cnts[0]].name; + outfile << runNumber << ", " << ctpcls[i].name << ", " << std::get<1>(times) / 1000; + for (int j = 1; j < 7; j++) { + // std::cout << std::setw(21) << " " << cnts[j]; + std::cout << ", " << cnts[j]; + outfile << ", " << cnts[j]; + } + std::cout << std::endl; + outfile << std::endl; + } + } + // ctpscalers->printFromZero(std::cout); + outfile.close(); +} diff --git a/Detectors/CTP/macro/CreateCTPConfig.C b/Detectors/CTP/macro/CreateCTPConfig.C index 1c47cf1e75cac..a3e4b0c8c35f1 100644 --- a/Detectors/CTP/macro/CreateCTPConfig.C +++ b/Detectors/CTP/macro/CreateCTPConfig.C @@ -19,6 +19,7 @@ #include "CCDB/CcdbApi.h" #include "CCDB/BasicCCDBManager.h" #include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" #include #include #include @@ -77,11 +78,14 @@ ferst 1 \n\ ctpcfg.loadConfigurationRun3(cfgRun3str); ctpcfg.printStream(std::cout); std::cout << "CTP config done" << std::endl; - CTPRunManager* man = new CTPRunManager; - man->setCCDBHost("http://ccdb-test.cern.ch:8080"); - man->saveRunConfigToCCDB(&ctpcfg, 1665784953); - // uint64_t classmask = ctpcfg.getClassMaskForInputMask(0x4); - // std::cout << "classmask:" << std::hex << classmask << std::dec << std::endl; + ctpcfg.checkConfigConsistency(); + if (0) { + o2::ctp::ctpCCDBManager* man = new ctpCCDBManager; + man->setCCDBHost("http://ccdb-test.cern.ch:8080"); + man->saveRunConfigToCCDB(&ctpcfg, 1665784953); + // uint64_t classmask = ctpcfg.getClassMaskForInputMask(0x4); + // std::cout << "classmask:" << std::hex << classmask << std::dec << std::endl; + } if (writeToFile == 1) { std::unique_ptr myFile(TFile::Open("CTPConfig.root", "RECREATE")); myFile->WriteObject(&ctpcfg, "CTPConfig"); diff --git a/Detectors/CTP/macro/GetAndSave.C b/Detectors/CTP/macro/GetAndSave.C new file mode 100644 index 0000000000000..ff70a3055c957 --- /dev/null +++ b/Detectors/CTP/macro/GetAndSave.C @@ -0,0 +1,62 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "TFile.h" +#include "TString.h" +#include +#include +#include +#endif +using namespace o2::ctp; +void GetAndSave(std::string ccdbHost = "http://ccdb-test.cern.ch:8080") +{ + std::string CCDBPathCTPScalers = "CTP/Calib/Scalers"; + // std::vector runs = {"518541","518543","518546","518547"}; + // std::vector timestamps = {1655116302316,1655118513690,1655121997478,1655123792911}; + std::vector runs = {"519903", "519904", "519905", "519906"}; + std::vector timestamps = {1656658674161, 1656660737184, 1656667772462, 1656669421115}; + // std::vector runs = {"518543"}; + // std::vector timestamps = {1655118513690}; + int i = 0; + ctpCCDBManager mng; + // mng.setCCDBHost(ccdbHost); + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(ccdbHost); + for (auto const& run : runs) { + CTPConfiguration ctpcfg; + CTPRunScalers scl; + std::map metadata; // can be empty + metadata["runNumber"] = run; + CTPRunScalers* ctpscalers = mgr.getSpecific(CCDBPathCTPScalers, timestamps[i], metadata); + if (ctpscalers == nullptr) { + std::cout << run << " CTPRunScalers not in database, timestamp:" << timestamps[i] << std::endl; + } else { + // ctpscalers->printStream(std::cout); + std::string name = run + ".root"; + TFile* myFile = TFile::Open(name.c_str(), "RECREATE"); + myFile->WriteObject(ctpscalers, "CTPRunScalers"); + // myFile->Write(); + std::cout << run << " ok" << std::endl; + } + i++; + } +} diff --git a/Detectors/CTP/macro/GetRates.C b/Detectors/CTP/macro/GetRates.C new file mode 100644 index 0000000000000..8894d7935b99e --- /dev/null +++ b/Detectors/CTP/macro/GetRates.C @@ -0,0 +1,55 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "Framework/Logger.h" +#endif +using namespace o2::ctp; + +void GetRates(int run = 559617) +{ + uint64_t inputmaskCum = 0, classmackCum = 0; + int ntrigSel = 0; + + auto& cmb = o2::ccdb::BasicCCDBManager::instance(); + auto ctpcfg = cmb.getSpecificForRun("CTP/Config/Config", run); + if (!ctpcfg) { + LOGP(error, "Can not get config for run {}", run); + return; + } + CTPConfiguration ctpconfig; + ctpconfig.loadConfigurationRun3(ctpcfg->getConfigString()); + ctpconfig.printStream(std::cout); + auto& triggerclasses = ctpconfig.getCTPClasses(); + LOGP(info, "Found {} trigger classes", triggerclasses.size()); + int indexInList = 0; + for (const auto& trgclass : triggerclasses) { + uint64_t inputmask = 0; + if (trgclass.descriptor != nullptr) { + inputmask = trgclass.descriptor->getInputsMask(); + // LOGP(info, "inputmask: {:#x}", inputmask); + } + trgclass.printStream(std::cout); + // std::cout << indexInList << ": " << trgclass.name << ", input mask 0x" << std::hex << inputmask << ", class mask 0x" << trgclass.classMask << std::dec << std::endl; + indexInList++; + if (trgclass.cluster->getClusterDetNames().find("TRD") != std::string::npos || trgclass.cluster->getClusterDetNames().find("trd") != std::string::npos) { + LOGP(info, "Found TRD trigger cluster, class mask: {:#x}, input mask: {:#x}", trgclass.classMask, inputmask); + inputmaskCum |= inputmask; + classmackCum |= trgclass.classMask; + ntrigSel++; + } + } + + LOGP(info, "Found {} triggers with TRD: classMasks: {:#x} inputMasks: {:#x}", ntrigSel, classmackCum, inputmaskCum); +} diff --git a/Detectors/CTP/macro/GetScalers.C b/Detectors/CTP/macro/GetScalers.C index e37e1e02bc188..1f104850c8c39 100644 --- a/Detectors/CTP/macro/GetScalers.C +++ b/Detectors/CTP/macro/GetScalers.C @@ -12,6 +12,7 @@ /// \file TestCTPScalers.C /// \brief create CTP scalers, test it and add to database /// \author Roman Lietava +// root -b -q "GetScalers.C(\"519499\", 1656286373953)" #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -19,6 +20,8 @@ #include "CCDB/BasicCCDBManager.h" #include "DataFormatsCTP/Scalers.h" #include "DataFormatsCTP/Configuration.h" +// #include "BookkeepingApi/BkpClientFactory.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" #include #include #include @@ -33,7 +36,7 @@ void GetScalers(std::string srun, long time, std::string ccdbHost = "http://ccdb // std::cout << stol(hd["SOR"]) << "\n"; CTPConfiguration ctpcfg; CTPRunScalers scl; - CTPRunManager mng; + o2::ctp::ctpCCDBManager mng; mng.setCCDBHost(ccdbHost); bool ok; // ctpcfg = mng.getConfigFromCCDB(time, srun); @@ -41,15 +44,22 @@ void GetScalers(std::string srun, long time, std::string ccdbHost = "http://ccdb // return; scl = mng.getScalersFromCCDB(time, srun, ok); if (ok == 1) { + scl.printStream(std::cout); scl.convertRawToO2(); - scl.printO2(std::cout); - scl.printFromZero(std::cout); - scl.printIntegrals(); - scl.printRates(); - // std::vector clsses; - // clsses = ctpcfg.getTriggerClassList(); - // std::cout << clsses.size() << std::endl; - // for(auto const& i : clsses) std::cout << i << std::endl; + // scl.printO2(std::cout); + // scl.printFromZero(std::cout); + // scl.printIntegrals(); + // scl.printRates(); + std::cout << "TVX,TSC,TCE,ZNC:" << std::endl; + scl.printInputRateAndIntegral(3); + scl.printInputRateAndIntegral(4); + scl.printInputRateAndIntegral(5); + scl.printInputRateAndIntegral(26); + std::cout << " TVX,TVX&TCE,TVX&TSC,TVX&VCH:" << std::endl; + scl.printClassBRateAndIntegral(3); + scl.printClassBRateAndIntegral(4); + scl.printClassBRateAndIntegral(5); + scl.printClassBRateAndIntegral(6); } else { std::cout << "Can not find run, please, check parameters" << std::endl; } diff --git a/Detectors/CTP/macro/GetScalersForRun.C b/Detectors/CTP/macro/GetScalersForRun.C new file mode 100644 index 0000000000000..a96d79d569213 --- /dev/null +++ b/Detectors/CTP/macro/GetScalersForRun.C @@ -0,0 +1,141 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include "Common/CCDB/ctpRateFetcher.h" +#endif +using namespace o2::ctp; + +void GetScalersForRun(int runNumber = 0, int fillN = 0, bool test = 1) +{ + if (test == 0) { + return; + } + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << "Timestamp:" << timeStamp << std::endl; + // + std::string sfill = std::to_string(fillN); + std::map metadata; + metadata["fillNumber"] = sfill; + auto lhcifdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp); + auto bfilling = lhcifdata->getBunchFilling(); + std::vector bcs = bfilling.getFilledBCs(); + std::cout << "Number of interacting bc:" << bcs.size() << std::endl; + // + std::string srun = std::to_string(runNumber); + metadata.clear(); // can be empty + metadata["runNumber"] = srun; + auto ctpscalers = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + } + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + } + std::cout << "all good" << std::endl; + ctpscalers->convertRawToO2(); + std::vector ctpcls = ctpcfg->getCTPClasses(); + // std::vector clslist = ctpcfg->getTriggerClassList(); + std::vector clslist = ctpscalers->getClassIndexes(); + std::map clsIndexToScaler; + std::cout << "Classes:"; + int i = 0; + for (auto const& cls : clslist) { + std::cout << cls << " "; + clsIndexToScaler[cls] = i; + i++; + } + std::cout << std::endl; + int tsc = 255; + int tce = 255; + int vch = 255; + int iznc = 255; + for (auto const& cls : ctpcls) { + if (cls.name.find("CMTVXTSC-B-NOPF-CRU") != std::string::npos) { + tsc = cls.getIndex(); + std::cout << cls.name << ":" << tsc << std::endl; + } + if (cls.name.find("CMTVXTCE-B-NOPF-CRU") != std::string::npos) { + tce = cls.getIndex(); + std::cout << cls.name << ":" << tce << std::endl; + } + if (cls.name.find("CMTVXVCH-B-NOPF-CRU") != std::string::npos) { + vch = cls.getIndex(); + std::cout << cls.name << ":" << vch << std::endl; + } + // if (cls.name.find("C1ZNC-B-NOPF-CRU") != std::string::npos) { + if (cls.name.find("C1ZNC-B-NOPF") != std::string::npos) { + iznc = cls.getIndex(); + std::cout << cls.name << ":" << iznc << std::endl; + } + } + std::vector recs = ctpscalers->getScalerRecordO2(); + if (recs[0].scalersInps.size() == 48) { + std::cout << "ZNC:"; + int inp = 26; + double_t nbc = bcs.size(); + double_t frev = 11245; + double_t sigmaratio = 28.; + double_t time0 = recs[0].epochTime; + double_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + double_t integral = recs[recs.size() - 1].scalersInps[inp - 1] - recs[0].scalersInps[inp - 1]; + double_t rate = integral / Trun; + double_t rat = integral / Trun / nbc / frev; + double_t mu = -TMath::Log(1 - rat); + double_t pp = 1 - mu / (TMath::Exp(mu) - 1); + double_t ratepp = mu * nbc * frev; + double_t integralpp = ratepp * Trun; + std::cout << "Rate:" << rate / sigmaratio << " Integral:" << integral << " mu:" << mu << " Pileup prob:" << pp; + std::cout << " Integralpp:" << integralpp << " Ratepp:" << ratepp / sigmaratio << std::endl; + // ctpscalers->printInputRateAndIntegral(26); + } else { + std::cout << "Inputs not available" << std::endl; + } + // + if (tsc != 255) { + std::cout << "TSC:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[tsc] + 1); + } + if (tce != 255) { + std::cout << "TCE:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[tce] + 1); + } + // std::cout << "TCE input:" << ctpscalers->printInputRateAndIntegral(5) << std::endl;; + if (vch != 255) { + std::cout << "VCH:"; + ctpscalers->printClassBRateAndIntegral(clsIndexToScaler[vch] + 1); + } + if (iznc != 255) { + std::cout << "ZNC class:"; + // uint64_t integral = recs[recs.size() - 1].scalers[iznc].l1After - recs[0].scalers[iznc].l1After; + auto zncrate = ctpscalers->getRateGivenT(timeStamp * 1.e-3, iznc, 6); + std::cout << "ZNC class rate:" << zncrate.first / 28. << std::endl; + } else { + std::cout << "ZNC class not available, rate from input:" << std::endl; + } + auto zncinprate = ctpscalers->getRateGivenT((timeStamp + 100) * 1.e-3, 25, 7); + std::cout << "ZNC inp rate:" << zncinprate.first / 28. << " " << zncinprate.second / 28. << std::endl; + + // ctpRateFetcher ctprate; + // auto fetcherRate = ctprate.fetch(&ccdbMgr, timeStamp, runNumber, "ZNChadronic"); + // std::cout << "Result from the fetcher (ZNC): " << fetcherRate << std::endl; +} diff --git a/Detectors/CTP/macro/PlotOrbit.C b/Detectors/CTP/macro/PlotOrbit.C new file mode 100644 index 0000000000000..9f70341ecc0da --- /dev/null +++ b/Detectors/CTP/macro/PlotOrbit.C @@ -0,0 +1,100 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +// root -b -q "GetScalers.C(\"519499\", 1656286373953)" +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include +#include +#include +#endif +//"http://ccdb-test.cern.ch:8080" +using namespace o2::ctp; +void PlotOrbit(int runNumber) +{ // + std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + // Timestamp + auto soreor = ccdbMgr.getRunDuration(runNumber); + // uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + uint64_t timeStamp = soreor.first + 60 * 1000; + std::cout << "Timestamp:" << timeStamp << std::endl; + // + // Scalers + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + // ccdbMgr.setURL("http://ccdb-test.cern.ch:8080"); + auto scl = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + if (scl == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + return; + } + scl->convertRawToO2(); + // return ; + std::vector recs = scl->getScalerRecordRaw(); + std::cout << "raw rec size:" << recs.size() << std::endl; + // + // + // Anal + // + // Times + int64_t time0 = recs[0].epochTime; + int64_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + // double_t orbit0 = recs[0].intRecord.orbit; + int64_t orbit0 = scl->getOrbitLimitFromRaw().first; + int64_t orbitL = scl->getOrbitLimitFromRaw().second; + int n = recs.size() - 1; + std::cout << " Run duration:" << Trun << " Scalers size:" << n + 1 << std::endl; + std::cout << "Orbit0:" << orbit0 << " orbitL:" << orbitL << std::endl; + return; + Double_t x[n], orbit[n]; + // Double_t tcetsctoznc[n], tcetoznc[n], vchtoznc[n]; + for (int i = 0; i < n; i++) { + // x[i] = i; + x[i] = recs[i + 1].epochTime - time0; + orbit[i] = recs[i + 1].intRecord.orbit - orbit0; + } + // + gStyle->SetMarkerSize(0.5); + TGraph* gr1 = new TGraph(n, x, orbit); + // TGraph* gr2 = new TGraph(n, x, tcetsctoznc); + // TGraph* gr3 = new TGraph(n, x, tcetoznc); + // TGraph* gr4 = new TGraph(n, x, vchtoznc); + gr1->SetMarkerStyle(20); + // gr2->SetMarkerStyle(21); + // gr3->SetMarkerStyle(23); + // gr4->SetMarkerStyle(23); + std::string title = "Orbit vs EpochTime for run " + srun + " ;EpochTime[s]; Orbit"; + gr1->SetTitle(title.c_str()); + // gr2->SetTitle("R=(TSC+TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr2->GetHistogram()->SetMaximum(1.1); + // gr2->GetHistogram()->SetMinimum(0.9); + // gr3->SetTitle("R=(TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr3->GetHistogram()->SetMaximum(0.6); + // gr3->GetHistogram()->SetMinimum(0.4); + // gr4->SetTitle("R=(VCH)*TVTX*B*28/ZNC; time[sec]; R"); + // gr4->GetHistogram()->SetMaximum(0.6); + // gr4->GetHistogram()->SetMinimum(0.4); + TCanvas* c1 = new TCanvas("c1", srun.c_str(), 200, 10, 800, 500); + gr1->Draw("AP"); + std::cout << "epoch0:" << time0 << " epochL:" << timeL << " T:" << Trun << std::endl; + std::cout << "orbit0:" << orbit0 << " orbitL:" << orbitL << " T:" << (orbitL - orbit0) * 88e-6 << std::endl; +} diff --git a/Detectors/CTP/macro/PlotPbLumi.C b/Detectors/CTP/macro/PlotPbLumi.C new file mode 100644 index 0000000000000..4bda8d25e006e --- /dev/null +++ b/Detectors/CTP/macro/PlotPbLumi.C @@ -0,0 +1,265 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PlotPbLumi.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +// root "PLotPbLumi.C(519499)" +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "TGraph.h" +#include "TMath.h" +#include "TCanvas.h" +#include "TStyle.h" +#include +#include +#include +#endif +using namespace o2::ctp; +// +// sum = 0: TCE and TSC separatelly otherwise TCE and (TCE+TSC) +// qc = 0: takes scalers from CCDB (available only for finished runs) otherwise from QCCDB (available for active runs) +// t0-tlast: window in seconds counted from beginning of run +// +void PlotPbLumi(int runNumber = 567905, bool sum = 0, bool qc = 0, Double_t t0 = 0., Double_t tlast = 0.) +{ // + // PLots in one canvas + // znc rate/28 + // R = (TCE+TSC)*TVX*B*/ZNC*28 + // R = TCE*TVX*B/ZNC*28 + // R = VCH*TVX*B/ZNC*28 + std::string ccdbHost = "http://alice-ccdb.cern.ch"; + std::string mCCDBPathCTPScalers = "/CTP/Calib/Scalers"; + std::string mCCDBPathCTPScalersQC = "qc/CTP/Scalers"; + std::string mCCDBPathCTPConfig = "CTP/Config/Config"; + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + // Timestamp + auto soreor = ccdbMgr.getRunDuration(runNumber); + uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; + std::cout << "Timestamp:" << timeStamp << std::endl; + // Filling + auto lhcifdata = ccdbMgr.getForRun("GLO/Config/GRPLHCIF", runNumber); + // auto lhcifdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + if (!lhcifdata) { + throw std::runtime_error("No GRPLHCIFData for run " + std::to_string(runNumber)); + } + auto bfilling = lhcifdata->getBunchFilling(); + std::vector bcs = bfilling.getFilledBCs(); + int nbc = bcs.size(); + std::cout << "Number of interacting bc:" << nbc << std::endl; + // Scalers + std::string srun = std::to_string(runNumber); + std::map metadata; + metadata["runNumber"] = srun; + CTPRunScalers* scl = nullptr; + if (qc) { + ccdbMgr.setURL("http://ali-qcdb-gpn.cern.ch:8083"); + scl = ccdbMgr.getSpecific(mCCDBPathCTPScalersQC, timeStamp, metadata); + } else { + scl = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + } + if (scl == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timeStamp; + return; + } + scl->convertRawToO2(); + std::vector recs = scl->getScalerRecordO2(); + // + // CTPConfiguration ctpcfg; + ccdbMgr.setURL("http://alice-ccdb.cern.ch"); + auto ctpcfg = ccdbMgr.getSpecific(mCCDBPathCTPConfig, timeStamp, metadata); + if (ctpcfg == nullptr) { + LOG(info) << "CTPRunConfig not in database, timestamp:" << timeStamp; + return; + } + std::vector clslist = ctpcfg->getTriggerClassList(); + // std::vector clslist = scl->getClassIndexes(); + std::map clsIndexToScaler; + std::cout << "Classes:"; + int i = 0; + for (auto const& cls : clslist) { + std::cout << cls << " "; + clsIndexToScaler[cls] = i; + i++; + } + std::cout << std::endl; + std::vector ctpcls = ctpcfg->getCTPClasses(); + int tsc = 255; + int tce = 255; + int vch = 255; + int zncclsi = 255; + for (auto const& cls : ctpcls) { + if (cls.name.find("CMTVXTSC-B-NOPF") != std::string::npos && tsc == 255) { + int itsc = cls.getIndex(); + tsc = clsIndexToScaler[itsc]; + // tsc = scl->getScalerIndexForClass(itsc); + std::cout << cls.name << ":" << tsc << ":" << itsc << std::endl; + } + if (cls.name.find("CMTVXTCE-B-NOPF-CRU") != std::string::npos) { + int itce = cls.getIndex(); + tce = clsIndexToScaler[itce]; + // tce = scl->getScalerIndexForClass(itce); + std::cout << cls.name << ":" << tce << ":" << itce << std::endl; + } + if (cls.name.find("CMTVXVCH-B-NOPF-CRU") != std::string::npos) { + int ivch = cls.getIndex(); + vch = clsIndexToScaler[ivch]; + // vch = scl->getScalerIndexForClass(ivch); + std::cout << cls.name << ":" << vch << ":" << ivch << std::endl; + } + if (cls.name.find("C1ZNC-B-NOPF-CRU") != std::string::npos) { + int iznc = cls.getIndex(); + zncclsi = clsIndexToScaler[iznc]; + // vch = scl->getScalerIndexForClass(ivch); + std::cout << cls.name << ":" << zncclsi << ":" << iznc << std::endl; + } + } + if (tsc == 255 || tce == 255 || vch == 255) { + std::cout << " One of dcalers not available, check config to find alternative)" << std::endl; + return; + } + // + // Anal + // + // Times + double_t frev = 11245; + double_t time0 = recs[0].epochTime; + double_t timeL = recs[recs.size() - 1].epochTime; + double_t Trun = timeL - time0; + double_t orbit0 = recs[0].intRecord.orbit; + int n = recs.size() - 1; + std::cout << " Run duration:" << Trun << " Scalers size:" << n + 1 << std::endl; + // + int i0 = 0; + int ilast = 0; + if (t0 != 0. || tlast != 0.) { + for (int i = 0; i < n; i++) { + double_t ttime = recs[i].epochTime - time0; + if (!i0 && t0 < ttime) { + i0 = i; + } + if (!ilast && tlast < ttime) { + ilast = i; + } + } + } else { + ilast = n; + } + n = ilast - i0; + std::cout << "i0:" << i0 << " ilast:" << ilast << std::endl; + // Double_t x[n], znc[n], zncpp[n]; + std::vector xvec(n), zncvec(n), zncppvec(n), zncclassvec(n); + Double_t* x = xvec.data(); + Double_t* znc = zncvec.data(); + Double_t* zncpp = zncppvec.data(); + Double_t* zncclass = zncclassvec.data(); + // Double_t tcetsctoznc[n], tcetoznc[n], vchtoznc[n]; + std::vector tcetsctozncvec(n), tcetozncvec(n), vchtozncvec(n); + Double_t* tcetsctoznc = tcetsctozncvec.data(); + Double_t* tcetoznc = tcetozncvec.data(); + Double_t* vchtoznc = vchtozncvec.data(); + for (int i = i0; i < ilast; i++) { + int iv = i - i0; + x[iv] = (double_t)(recs[i + 1].intRecord.orbit + recs[i].intRecord.orbit) / 2. - orbit0; + x[iv] *= 88e-6; + // x[i] = (double_t)(recs[i+1].epochTime + recs[i].epochTime)/2.; + double_t tt = (double_t)(recs[i + 1].intRecord.orbit - recs[i].intRecord.orbit); + tt = tt * 88e-6; + // + // std::cout << recs[i+1].scalersInps[25] << std::endl; + double_t znci = (double_t)(recs[i + 1].scalersInps[25] - recs[i].scalersInps[25]); + double_t mu = -TMath::Log(1. - znci / tt / nbc / frev); + double_t zncipp = mu * nbc * frev; + zncpp[iv] = zncipp / 28.; + znc[iv] = znci / 28. / tt; + // znc class + znci = recs[i + 1].scalers[zncclsi].l1Before - recs[i].scalers[zncclsi].l1Before; + zncclass[iv] = znci / 28. / tt; + // std::cout << znc[i]/zncclass[i] << std::endl; + // + double_t had = 0; + if (sum) { + had += recs[i + 1].scalers[tce].lmBefore - recs[i].scalers[tce].lmBefore; + } + double_t mutce = -TMath::Log(1. - had / tt / nbc / frev); + // std::cout << recs[i+1].scalers[tce].lmBefore << std::endl; + had += recs[i + 1].scalers[tsc].lmBefore - recs[i].scalers[tsc].lmBefore; + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + tcetsctoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + had = recs[i + 1].scalers[tce].lmBefore - recs[i].scalers[tce].lmBefore; + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + tcetoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + had = recs[i + 1].scalers[vch].lmBefore - recs[i].scalers[vch].lmBefore; + double_t muvch = -TMath::Log(1. - had / tt / nbc / frev); + + // rat = (double_t)(had)/double_t(recs[i+1].scalersInps[25] - recs[i].scalersInps[25])*28; + vchtoznc[iv] = (double_t)(had) / zncpp[iv] / tt; + // std::cout << "muzdc:" << mu << " mu tce:" << mutce << " muvch:" << muvch << std::endl; + } + // + gStyle->SetMarkerSize(0.5); + TGraph* gr1 = new TGraph(n, x, znc); + TGraph* gr11 = new TGraph(n, x, zncpp); // PileuP corrected + TGraph* gr12 = new TGraph(n, x, zncclass); // NOT PileuP corrected + TGraph* gr2 = new TGraph(n, x, tcetsctoznc); + TGraph* gr3 = new TGraph(n, x, tcetoznc); + TGraph* gr4 = new TGraph(n, x, vchtoznc); + gr1->SetMarkerStyle(20); + gr11->SetMarkerStyle(20); + gr12->SetMarkerStyle(20); + gr11->SetMarkerColor(kRed); + gr12->SetMarkerColor(kBlue); + gr2->SetMarkerStyle(21); + gr3->SetMarkerStyle(23); + gr4->SetMarkerStyle(23); + if (sum) { + gr2->SetTitle("R=(TSC+TCE)*TVTX*B*28/ZNC; time[sec]; R"); + } else { + gr2->SetTitle("R=(TSC)*TVTX*B*28/ZNC; time[sec]; R"); + } + // gr2->GetHistogram()->SetMaximum(1.1); + // gr2->GetHistogram()->SetMinimum(0.9); + gr3->SetTitle("R=(TCE)*TVTX*B*28/ZNC; time[sec]; R"); + // gr3->GetHistogram()->SetMaximum(0.6); + // gr3->GetHistogram()->SetMinimum(0.4); + gr4->SetTitle("R=(VCH)*TVTX*B*28/ZNC; time[sec]; R"); + // gr4->GetHistogram()->SetMaximum(0.6); + // gr4->GetHistogram()->SetMinimum(0.4); + TMultiGraph* mg1 = new TMultiGraph(); + mg1->SetTitle("R=ZNC/28 rate [Hz] (red=PilUp Corrected); time[sec]; R"); + mg1->Add(gr1); + mg1->Add(gr11); + mg1->Add(gr12); + TCanvas* c1 = new TCanvas("c1", srun.c_str(), 200, 10, 800, 500); + std::string title = "RUN " + std::to_string(runNumber); + c1->SetTitle(title.c_str()); + c1->Divide(2, 2); + c1->cd(1); + mg1->Draw("AP"); + c1->cd(2); + gr2->Draw("AP"); + c1->cd(3); + gr3->Draw("AP"); + c1->cd(4); + gr4->Draw("AP"); + // getRate test: + double tt = timeStamp / 1000.; + std::pair r1 = scl->getRateGivenT(tt, 25, 7); + std::cout << "ZDC input getRateGivetT:" << r1.first / 28. << " " << r1.second / 28. << std::endl; + std::pair r2 = scl->getRateGivenT(tt, tce, 1); + std::cout << "LM before TCE class getRateGivetT:" << r2.first << " " << r2.second << std::endl; +} diff --git a/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C b/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C new file mode 100644 index 0000000000000..385a8c7e91e15 --- /dev/null +++ b/Detectors/CTP/macro/ReadCTPRunScalersFromFile.C @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TestCTPScalers.C +/// \brief create CTP scalers, test it and add to database +/// \author Roman Lietava +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsCTP/Scalers.h" +#include "DataFormatsCTP/Configuration.h" +#include "TFile.h" +#include "TString.h" +#include +#include +#include +#endif +using namespace o2::ctp; +void ReadCTPRunScalersFromFile(std::string name = "s.root") +{ + std::cout << "Reading file:" << name << std::endl; + TFile* myFile = TFile::Open(name.c_str()); + bool doscalers = 1; + bool doconfig = 0; + if (doscalers) { + // CTPRunScalers* ctpscalers = myFile->Get("ccdb_object"); + CTPRunScalers* ctpscalers = myFile->Get("CTPRunScalers"); + if (ctpscalers != nullptr) { + ctpscalers->printStream(std::cout); + ctpscalers->convertRawToO2(); + ctpscalers->printIntegrals(); + } else { + std::cout << "Scalers not there ?" << std::endl; + } + } + if (doconfig) { + CTPConfiguration* ctpconfig = myFile->Get("CTPConfig"); + if (ctpconfig != nullptr) { + ctpconfig->printStream(std::cout); + } else { + std::cout << "Config not there ?" << std::endl; + } + } +} diff --git a/Detectors/CTP/macro/SaveInputsConfig.C b/Detectors/CTP/macro/SaveInputsConfig.C index 99cae77905541..459fbf4024c95 100644 --- a/Detectors/CTP/macro/SaveInputsConfig.C +++ b/Detectors/CTP/macro/SaveInputsConfig.C @@ -41,7 +41,7 @@ void SaveInputsConfig(std::string filename = "inputs.cfg", std::string ccdbHost long tmax = timeStamp + time365days; o2::ccdb::CcdbApi api; api.init(ccdbHost); // or http://localhost:8080 for a local installation - map metadata; // can be empty + std::map metadata; // can be empty // store abitrary user object in strongly typed manner api.storeAsTFileAny(&ctpcfginps, "CTP/Calib/Inputs", metadata, tmin, tmax); } diff --git a/Detectors/CTP/macro/TestConfig.C b/Detectors/CTP/macro/TestConfig.C index 0ff91c514ee93..38da821ea4807 100644 --- a/Detectors/CTP/macro/TestConfig.C +++ b/Detectors/CTP/macro/TestConfig.C @@ -12,6 +12,7 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include +#include "CTPWorkflowScalers/ctpCCDBManager.h" #endif using namespace o2::ctp; @@ -22,8 +23,12 @@ void TestConfig(bool test = 0) } uint64_t timestamp = 1660196771632; std::string run = "523148"; - o2::ctp::CTPRunManager::setCCDBHost("https://alice-ccdb.cern.ch"); - auto ctpcfg = o2::ctp::CTPRunManager::getConfigFromCCDB(timestamp, run); + o2::ctp::ctpCCDBManager::setCCDBHost("https://alice-ccdb.cern.ch"); + bool ok; + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); + if (ok == 0) { + std::cout << "Can not get config for run:" << run << std::endl; + } CTPConfiguration ctpconfig; ctpconfig.loadConfigurationRun3(ctpcfg.getConfigString()); // ctpconfig.printStream(std::cout); diff --git a/Detectors/CTP/macro/TestFetcher.C b/Detectors/CTP/macro/TestFetcher.C new file mode 100644 index 0000000000000..b2b6912f49911 --- /dev/null +++ b/Detectors/CTP/macro/TestFetcher.C @@ -0,0 +1,46 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#endif +using namespace o2::ctp; + +void TestFetcher(int runNumber = 557251) +{ + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + // Opening run + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 0); + ccdb.setURL("http://ali-qcdb-gpn.cern.ch:8083/"); + std::string QCDBPathCTPScalers = "qc/CTP/Scalers"; + std::map metadata; // can be empty + std::string run = std::to_string(runNumber); + metadata["runNumber"] = run; + CTPRunScalers* ctpscalers = ccdb.getSpecific(QCDBPathCTPScalers, ts, metadata); + auto tt = ctpscalers->getTimeLimitFromRaw(); + std::cout << "1st scalers duration:" << tt.first << " " << tt.second << std::endl; + fetcher.updateScalers(*ctpscalers); + auto rate = fetcher.fetchNoPuCorr(&ccdb, ts, runNumber, "T0VTX"); + std::cout << "1st rate:" << rate << std::endl; + // Running on the same run + ts = ts + 5 * 1000 * 3600; + ctpscalers = ccdb.getSpecific(QCDBPathCTPScalers, ts, metadata); + std::cout << "Later scalers duration:" << tt.first << " " << tt.second << std::endl; + fetcher.updateScalers(*ctpscalers); + rate = fetcher.fetchNoPuCorr(&ccdb, ts, runNumber, "T0VTX"); + std::cout << "Later rate:" << rate << std::endl; +} diff --git a/Detectors/CTP/macro/TestGetRates.C b/Detectors/CTP/macro/TestGetRates.C new file mode 100644 index 0000000000000..19644853c568b --- /dev/null +++ b/Detectors/CTP/macro/TestGetRates.C @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#endif +using namespace o2::ctp; + +void TestGetRates(int runN = 0) +{ + std::vector runs; + std::vector codes = {"T0VTX", "T0VTX", "ZNChadronic", "ZNChadronic", "T0VTX"}; + if (runN == 0) { + runs = {529066, 539218, 544013, 544518, 557251}; + } else { + runs.push_back(runN); + } + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + int i = 0; + for (auto const& runNumber : runs) { + // Opening run + std::pair pp = ccdb.getRunDuration(runNumber); + long ts = pp.first + 60; + // std::cout << "Run duration:" << pp.first << " " << pp.second << std::endl; + std::cout << "===> RUN:" << runNumber << " duration:" << (pp.second - pp.first) / 1000. << std::endl; + + CTPRateFetcher fetcher; + fetcher.setupRun(runNumber, &ccdb, ts, 1); + fetcher.setOrbit(1); + std::array rates; + fetcher.getRates(rates, &ccdb, runNumber, codes[i]); + std::cout << "Start:" << rates[0] << " End:" << rates[1] << " Middle:" << rates[2] << " code:" << codes[i] << std::endl; + double lumi1 = fetcher.getLumi(&ccdb, runNumber, codes[i], 0); + double lumi2 = fetcher.getLumi(&ccdb, runNumber, codes[i], 1); + std::cout << " Lumi NO pile up corr:" << lumi1 << " Lumi with pile upcorr:" << lumi2 << " code:" << codes[i] << std::endl; + i++; + } +} diff --git a/Detectors/CTP/reconstruction/CMakeLists.txt b/Detectors/CTP/reconstruction/CMakeLists.txt index e06f1e8ec9909..3aba8c3c2d588 100644 --- a/Detectors/CTP/reconstruction/CMakeLists.txt +++ b/Detectors/CTP/reconstruction/CMakeLists.txt @@ -11,7 +11,10 @@ o2_add_library(CTPReconstruction SOURCES src/CTFCoder.cxx src/CTFHelper.cxx + src/RawDataDecoder.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::DPLUtils + O2::DetectorsRaw O2::DetectorsBase O2::CommonDataFormat O2::DetectorsCommonDataFormats diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 6c6d867a1a3a4..8dbc5adadbfc5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -23,8 +23,9 @@ #include "DataFormatsCTP/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "CTPReconstruction/CTFHelper.h" +#include "CTPReconstruction/RawDataDecoder.h" +#include "DataFormatsCTP/Configuration.h" class TTree; @@ -33,10 +34,10 @@ namespace o2 namespace ctp { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -47,7 +48,16 @@ class CTFCoder : public o2::ctf::CTFCoderBase template o2::ctf::CTFIOSize decode(const CTF::base& ec, VTRG& data, LumiInfo& lumi); + /// add CTP related shifts + template + bool finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); + void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; + void setDecodeInps(bool decodeinps) { mDecodeInps = decodeinps; } + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); } + bool getDecodeInps() { return mDecodeInps; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } + bool canApplyBCShiftInputs(const o2::InteractionRecord& ir) const { return canApplyBCShift(ir, mBCShiftInputs); } private: template @@ -56,6 +66,9 @@ class CTFCoder : public o2::ctf::CTFCoderBase void appendToTree(TTree& tree, CTF& ec); void readFromTree(TTree& tree, int entry, std::vector& data, LumiInfo& lumi); std::vector mDataFilt; + CTPConfiguration mCTPConfig; + int mBCShiftInputs = 0; + bool mDecodeInps = false; }; /// entropy-encode clusters to buffer with CTF @@ -80,10 +93,10 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader(lumi)); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODECTP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODECTP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODECTP(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODECTP(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -121,12 +133,12 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc; - std::vector orbitInc; + std::vector bcInc; + std::vector orbitInc; std::vector bytesInput, bytesClass; o2::ctf::CTFIOSize iosize; -#define DECODECTP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODECTP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODECTP(bcInc, CTF::BLC_bcIncTrig); iosize += DECODECTP(orbitInc, CTF::BLC_orbitIncTrig); @@ -135,14 +147,19 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l // clang-format on // data.clear(); - + std::map digitsMap; o2::InteractionRecord ir(header.firstBC, header.firstOrbit); lumi.nHBFCounted = header.lumiNHBFs; + lumi.nHBFCountedFV0 = header.lumiNHBFsFV0 ? header.lumiNHBFsFV0 : header.lumiNHBFs; lumi.counts = header.lumiCounts; + lumi.countsFV0 = header.lumiCountsFV0; lumi.orbit = header.lumiOrbit; + lumi.inp1 = int(header.inp1); + lumi.inp2 = int(header.inp2); auto itInp = bytesInput.begin(); auto itCls = bytesClass.begin(); bool checkIROK = (mBCShift == 0); // need to check if CTP offset correction does not make the local time negative ? + bool checkIROKInputs = (mBCShiftInputs == 0); // need to check if CTP offset correction does not make the local time negative ? for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { // restore TrigRecord if (orbitInc[itrig]) { // non-0 increment => new orbit @@ -151,20 +168,71 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l } else { ir.bc += bcInc[itrig]; } - if (checkIROK || canApplyBCShift(ir)) { // correction will be ok - checkIROK = true; + if (checkIROKInputs || canApplyBCShiftInputs(ir)) { // correction will be ok + auto irs = ir - mBCShiftInputs; + uint64_t CTPInputMask = 0; + for (int i = 0; i < CTFHelper::CTPInpNBytes; i++) { + CTPInputMask |= static_cast(*itInp++) << (8 * i); + } + if (CTPInputMask) { + if (digitsMap.count(irs)) { + if (digitsMap[irs].isInputEmpty()) { + digitsMap[irs].CTPInputMask = CTPInputMask; + // LOG(info) << "IR1:"; + // digitsMap[irs].printStream(std::cout); + } else { + LOG(error) << "CTPInpurMask already exist:" << irs << " dig.CTPInputMask:" << digitsMap[irs].CTPInputMask << " CTPInputMask:" << CTPInputMask; + } + } else { + CTPDigit dig = {irs, CTPInputMask, 0}; + digitsMap[irs] = dig; + // LOG(info) << "IR2:"; + // digitsMap[irs].printStream(std::cout); + } + } } else { // correction would make IR prior to mFirstTFOrbit, skip itInp += CTFHelper::CTPInpNBytes; + } + if (checkIROK || canApplyBCShift(ir)) { // correction will be ok + auto irs = ir - mBCShift; + uint64_t CTPClassMask = 0; + for (int i = 0; i < CTFHelper::CTPClsNBytes; i++) { + CTPClassMask |= static_cast(*itCls++) << (8 * i); + } + if (CTPClassMask) { + if (digitsMap.count(irs)) { + if (digitsMap[irs].isClassEmpty()) { + digitsMap[irs].CTPClassMask = CTPClassMask; + // LOG(info) << "TCM1:"; + // digitsMap[irs].printStream(std::cout); + } else { + LOG(error) << "CTPClassMask already exist:" << irs << " dig.CTPClassMask:" << digitsMap[irs].CTPClassMask << " CTPClassMask:" << CTPClassMask; + } + } else { + CTPDigit dig = {irs, 0, CTPClassMask}; + digitsMap[irs] = dig; + // LOG(info) << "TCM2:"; + // digitsMap[irs].printStream(std::cout); + } + } + } else { // correction would make IR prior to mFirstTFOrbit, skip itCls += CTFHelper::CTPClsNBytes; - continue; } - auto& dig = data.emplace_back(); - dig.intRecord = ir - mBCShift; - for (int i = 0; i < CTFHelper::CTPInpNBytes; i++) { - dig.CTPInputMask |= static_cast(*itInp++) << (8 * i); + } + if (mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMask(); } - for (int i = 0; i < CTFHelper::CTPClsNBytes; i++) { - dig.CTPClassMask |= static_cast(*itCls++) << (8 * i); + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; + o2::pmr::vector digits; + o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit, trgclassmask); + for (auto const& dig : digits) { + data.emplace_back(dig); + } + } else { + for (auto const& dig : digitsMap) { + data.emplace_back(dig.second); } } assert(itInp == bytesInput.end()); @@ -172,6 +240,15 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l iosize.rawIn = header.nTriggers * sizeof(CTPDigit); return iosize; } +///________________________________ +template +bool CTFCoder::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +{ + auto match = o2::ctf::CTFCoderBase::finaliseCCDB(matcher, obj); + mBCShiftInputs = -o2::ctp::TriggerOffsetsParam::Instance().globalInputsShift; + LOG(info) << "BCShiftInputs:" << mBCShiftInputs; + return match; +} } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h index ceb99c36a8f26..34228e3acc55f 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h @@ -38,8 +38,8 @@ class CTFHelper CTFHeader createHeader(const LumiInfo& lumi) { CTFHeader h{o2::detectors::DetID::CTP, 0, 1, 0, // dummy timestamp, version 1.0 - lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.orbit, - uint32_t(mData.size()), 0, 0}; + lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.nHBFCountedFV0, lumi.orbit, + uint32_t(mData.size()), 0, 0, uint16_t(lumi.inp1), uint16_t(lumi.inp2)}; if (mData.size()) { h.firstOrbit = mData[0].intRecord.orbit; h.firstBC = mData[0].intRecord.bc; @@ -55,7 +55,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -64,91 +64,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? M * data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I& operator--() + inline I operator++(int) { - mIndex--; - return (I&)(*this); + I res = *(static_cast(this)); + ++mIndex; + return res; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } - - difference_type operator-(size_t idx) const { return mIndex - idx; } - - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } - const I operator++(int) + inline I& operator--() noexcept { - auto res = *this; - ++mIndex; - return res; + mIndex--; + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } + + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; + + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].intRecord.orbit == mData[mIndex - 1].intRecord.orbit) { - return mData[mIndex].intRecord.bc - mData[mIndex - 1].intRecord.bc; + return value_type(mData[mIndex].intRecord.bc - mData[mIndex - 1].intRecord.bc); } else { - return mData[mIndex].intRecord.bc; + return value_type(mData[mIndex].intRecord.bc); } } return 0; @@ -158,9 +153,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].intRecord.orbit == mData[id - 1].intRecord.orbit) { - return mData[id].intRecord.bc - mData[id - 1].intRecord.bc; + return value_type(mData[id].intRecord.bc - mData[id - 1].intRecord.bc); } else { - return mData[id].intRecord.bc; + return value_type(mData[id].intRecord.bc); } } return 0; @@ -169,15 +164,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].intRecord.orbit - mData[mIndex - 1].intRecord.orbit : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].intRecord.orbit - mData[mIndex - 1].intRecord.orbit : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].intRecord.orbit - mData[id - 1].intRecord.orbit : 0; + return value_type(id ? mData[id].intRecord.orbit - mData[id - 1].intRecord.orbit : 0); } }; diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h new file mode 100644 index 0000000000000..53addf32c538f --- /dev/null +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h @@ -0,0 +1,104 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RawDataDecoder.h +/// \brief Digits tw Raw translation +/// \author Roman Lietava + +#ifndef ALICEO2_CTP_RAWDATADECODER_H_ +#define ALICEO2_CTP_RAWDATADECODER_H_ + +#include +#include +#include +#include "Framework/InputRecord.h" +#include "DataFormatsCTP/Digits.h" +#include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" + +namespace o2 +{ +namespace ctp +{ +class RawDataDecoder +{ + public: + RawDataDecoder() = default; + ~RawDataDecoder() = default; + static void makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld); + int addCTPDigit(uint32_t linkCRU, uint32_t triggerOrbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits); + int decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, o2::pmr::vector& digits, std::vector& lumiPointsHBF1); + int decodeRawFatal(o2::framework::InputRecord& inputs, std::vector& filter); + int decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, std::vector& digits, std::vector& lumiPointsHBF1); + void setDecodeInps(bool decodeinps) { mDecodeInps = decodeinps; } + void setDoLumi(bool lumi) { mDoLumi = lumi; } + void setDoDigits(bool digi) { mDoDigits = digi; } + void setVerbose(bool v) { mVerbose = v; } + void setMAXErrors(int m) { mErrorMax = m; } + int setLumiInp(int lumiinp, std::string inp); + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); }; + void setCheckConsistency(bool check) { mCheckConsistency = check; } + uint32_t getIRRejected() const { return mIRRejected; } + uint32_t getTCRRejected() const { return mTCRRejected; } + std::vector& getTFOrbits() { return mTFOrbits; } + int getErrorIR() { return mErrorIR; } + int getErrorTCR() { return mErrorTCR; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } + int init(); + static int shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit, std::bitset<48>& inpmask, int64_t shift, int level, std::map& digmap); + static int shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask = 0xffffffffffffffff); + int checkReadoutConsistentncy(o2::pmr::vector& digits, uint64_t trgclassmask = 0xffffffffffffffff, uint64_t trigclassmaskNoTrgDets = 0xffffffffffffffff); + std::array getClassErrorsA() { return mClassErrorsA; } + std::array getClassErrorsB() { return mClassErrorsB; } + std::array getClassCountersA() { return mClassCountersA; } + std::array getClassCountersB() { return mClassCountersB; } + int getLostDueToShiftCls() { return mLostDueToShiftCC; } + int getLostDueToShiftInp() { return mLostDueToShiftInps; } + + private: + static constexpr uint32_t TF_TRIGGERTYPE_MASK = 0x800; + static constexpr uint32_t HB_TRIGGERTYPE_MASK = 0x2; + // true: full inps decoding includine latency shifts here; false: latency shifts in CTF decoder + bool mDecodeInps = false; + bool mCheckConsistency = false; + // for digits + bool mDoDigits = true; + std::vector mOutputDigits; + // for lumi + bool mDoLumi = true; + // + static constexpr std::bitset LMMASKInputs = 0xfff; + static constexpr std::bitset L0MASKInputs = 0xfff000; + static constexpr std::bitset L1MASKInputs = (0xffffffull << 24); + gbtword80_t mTVXMask = 0x4; // TVX is 3rd input + gbtword80_t mVBAMask = 0x20; // VBA is 6 th input + bool mVerbose = false; + int mIRRejected = 0; + int mTCRRejected = 0; + bool mPadding = true; + uint32_t mTFOrbit = 0; + std::vector mTFOrbits; + // error verbosness + int mErrorIR = 0; + int mErrorTCR = 0; + int mErrorMax = 5; + bool mStickyError = false; + std::array mClassErrorsA{}; + std::array mClassErrorsB{}; // from inputs + std::array mClassCountersA{}; + std::array mClassCountersB{}; // from inputs + int mLostDueToShiftCC = 0; + int mLostDueToShiftInps = 0; + CTPConfiguration mCTPConfig; +}; +} // namespace ctp +} // namespace o2 +#endif diff --git a/Detectors/CTP/reconstruction/src/CTFCoder.cxx b/Detectors/CTP/reconstruction/src/CTFCoder.cxx index facbc9ebd40cf..e182032e12605 100644 --- a/Detectors/CTP/reconstruction/src/CTFCoder.cxx +++ b/Detectors/CTP/reconstruction/src/CTFCoder.cxx @@ -18,7 +18,6 @@ #include using namespace o2::ctp; - ///___________________________________________________________________________________ // Register encoded data in the tree (Fill is not called, will be done by caller) void CTFCoder::appendToTree(TTree& tree, CTF& ec) @@ -41,10 +40,10 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc = 0; - uint32_t orbitInc = 0; + int16_t bcInc = 0; + int32_t orbitInc = 0; uint8_t bytesInput = 0, bytesClass = 0; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx new file mode 100644 index 0000000000000..a062a262acf62 --- /dev/null +++ b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx @@ -0,0 +1,691 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RawDataDecoder.cxx +/// \author Roman Lietava +#include +#include "DetectorsRaw/RDHUtils.h" +#include "DPLUtils/DPLRawParser.h" +#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "CTPReconstruction/RawDataDecoder.h" +#include "DataFormatsCTP/Configuration.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include + +using namespace o2::ctp; + +// Inverse of Digits2Raw::makeGBTWord +void RawDataDecoder::makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld) +{ + gbtword80_t diglet = remnant; + uint32_t i = 0; + while (i < (NGBT - Npld)) { + std::bitset masksize = 0; + for (uint32_t j = 0; j < (Npld - size_gbt); j++) { + masksize[j] = 1; + } + diglet |= (GBTWord & masksize) << (size_gbt); + diglets.push_back(diglet); + diglet = 0; + i += Npld - size_gbt; + GBTWord = GBTWord >> (Npld - size_gbt); + size_gbt = 0; + } + size_gbt = NGBT - i; + remnant = GBTWord; +} +int RawDataDecoder::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits) +{ + int ret = 0; + gbtword80_t pld = (diglet & pldmask); + if (pld.count() == 0) { + return 0; + } + pld >>= 12; + CTPDigit digit; + const gbtword80_t bcidmask = 0xfff; + uint16_t bcid = (diglet & bcidmask).to_ulong(); + LOG(debug) << bcid << " pld:" << pld; + o2::InteractionRecord ir = {bcid, orbit}; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + int32_t BCShiftCorrectionInps = -o2::ctp::TriggerOffsetsParam::Instance().globalInputsShift; + LOG(debug) << "InputMaskCount:" << digits[ir].CTPInputMask.count(); + LOG(debug) << "ir ir ori:" << ir; + if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < BCShiftCorrectionInps)) { + // LOG(warning) << "Loosing ir:" << ir; + mIRRejected++; + return 0; + } + ir -= BCShiftCorrectionInps; + LOG(debug) << "ir ir corrected:" << ir; + digit.intRecord = ir; + if (digits.count(ir) == 0) { + digit.setInputMask(pld); + digits[ir] = digit; + LOG(debug) << bcid << " inputs case 0 bcid orbit " << orbit << " pld:" << pld; + } else if (digits.count(ir) == 1) { + if (digits[ir].CTPInputMask.count() == 0) { + digits[ir].setInputMask(pld); + LOG(debug) << bcid << " inputs bcid case 1 orbit " << orbit << " pld:" << pld; + } else { + if (mErrorIR < mErrorMax) { + LOG(error) << "Two CTP IRs with the same timestamp:" << ir.bc << " " << ir.orbit << " pld:" << pld << " dig:" << digits[ir]; + } + ret = 4; + mErrorIR++; + mStickyError = true; + } + } else { + LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; + ret = 8; + } + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + int32_t BCShiftCorrection = -o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; + int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; + LOG(debug) << "tcr ir ori:" << ir; + if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < offset)) { + // LOG(warning) << "Loosing tclass:" << ir; + mTCRRejected++; + return 0; + } + ir -= offset; + LOG(debug) << "tcr ir corrected:" << ir; + digit.intRecord = ir; + if (digits.count(ir) == 0) { + digit.setClassMask(pld); + digits[ir] = digit; + LOG(debug) << bcid << " class bcid case 0 orbit " << orbit << " pld:" << pld; + } else if (digits.count(ir) == 1) { + if (digits[ir].CTPClassMask.count() == 0) { + digits[ir].setClassMask(pld); + LOG(debug) << bcid << " class bcid case 1 orbit " << orbit << " pld:" << pld; + } else { + if (mErrorTCR < mErrorMax) { + LOG(error) << "Two CTP Class masks for same timestamp"; + mStickyError = true; + } + mErrorTCR++; + ret = 16; + } + } else { + LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; + ret = 32; + } + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + return ret; +} +// +// Decodes one page +// It is NOT assumed that CTP HBF has never more than one page. +// 1 HBF/page <= 8000kB = 8*1024*8/120 = 546 GBT words = 546 IRs/page = 5.5 MHz +int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, o2::pmr::vector& digits, std::vector& lumiPointsHBF1) +{ + int ret = 0; + static int nwrites = 0; + uint64_t countsMBT = 0; + uint64_t countsMBV = 0; + std::map digitsMap; + // + // using InputSpec = o2::framework::InputSpec; + // using ConcreteDataTypeMatcher = o2::framework::ConcreteDataTypeMatcher; + // using Lifetime = o2::framework::Lifetime; + o2::framework::DPLRawParser parser(inputs, filter); + uint32_t payloadCTP = 0; + gbtword80_t remnant = 0; + uint32_t size_gbt = 0; + mTFOrbit = 0; + uint32_t orbit0 = 0; + for (auto it = parser.begin(); it != parser.end(); ++it) { + const o2::header::RDHAny* rdh = nullptr; + try { + rdh = reinterpret_cast(it.raw()); + mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); + } catch (std::exception& e) { + LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); + // dummyOutput(); + return 1; + } + // auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); + uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); + uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); + uint32_t version = o2::raw::RDHUtils::getVersion(rdh); + uint32_t rdhOrbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); + uint32_t triggerType = o2::raw::RDHUtils::getTriggerType(rdh); + // std::cout << "diff orbits:" << triggerOrbit - rdhOrbit << std::endl; + bool tf = (triggerType & TF_TRIGGERTYPE_MASK) && (packetCounter == 0); + bool hb = (triggerType & HB_TRIGGERTYPE_MASK) && (packetCounter == 0); + if (tf) { + mTFOrbit = rdhOrbit; + // std::cout << "tforbit==================>" << mTFOrbit << " " << std::hex << mTFOrbit << std::endl; + mTFOrbits.push_back(mTFOrbit); + } + static bool prt = true; + if (prt) { + LOG(info) << "RDH version:" << version << " Padding:" << mPadding; + prt = false; + } + auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR + auto linkCRU = (feeID & 0xf00) >> 8; + // LOG(info) << "CRU link:" << linkCRU; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + payloadCTP = o2::ctp::NIntRecPayload; + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + payloadCTP = o2::ctp::NClassPayload; + if (!mDoDigits) { // Do not do TCR if only lumi + continue; + } + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << " triggerType:" << triggerType; + // LOG(info) << "remnant :" << remnant.count(); + gbtword80_t pldmask = 0; + for (uint32_t i = 0; i < payloadCTP; i++) { + pldmask[12 + i] = 1; + } + // std::cout << (orbit0 != rdhOrbit) << " comp " << (mTFOrbit==rdhOrbit) << std::endl; + // if(orbit0 != rdhOrbit) { + if (hb) { + if ((mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) && !tf) { // create lumi per HB + lumiPointsHBF1.emplace_back(LumiInfo{rdhOrbit, 0, 0, countsMBT, countsMBV}); + // std::cout << "hb:" << nhb << " countsMBT:" << countsMBT << std::endl; + countsMBT = 0; + countsMBV = 0; + // nhb++; + } + remnant = 0; + size_gbt = 0; + orbit0 = rdhOrbit; + // std::cout << "orbit0============>" << std::dec << orbit0 << " " << std::hex << orbit0 << std::endl; + } + // Create 80 bit words + gsl::span payload(it.data(), it.size()); + gbtword80_t gbtWord80; + gbtWord80.set(); + int wordCount = 0; + int wordSize = 10; + std::vector gbtwords80; + // mPadding = 0; + if (mPadding == 1) { + wordSize = 16; + } + // LOG(info) << ii << " payload size:" << payload.size(); + /* if (payload.size()) { + //LOG(info) << "payload size:" << payload.size(); + // LOG(info) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << triggerOrbit << " stopbit:" << stopBit << " packet:" << packetCounter; + // LOGP(info, "RDH FEEid: {} CRU link: {}, Orbit: {}", feeID, linkCRU, triggerOrbit); + std::cout << std::hex << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << std::endl; + } */ + gbtword80_t bcmask = std::bitset<80>("111111111111"); + for (auto payloadWord : payload) { + int wc = wordCount % wordSize; + // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); + if ((wc == 0) && (wordCount != 0)) { + if (gbtWord80.count() != 80) { + gbtwords80.push_back(gbtWord80); + } + gbtWord80.set(); + } + if (wc < 10) { + for (int i = 0; i < 8; i++) { + gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); + } + } + wordCount++; + } + if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { + gbtwords80.push_back(gbtWord80); + } + // decode 80 bits payload + for (auto word : gbtwords80) { + std::vector diglets; + gbtword80_t gbtWord = word; + makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); + for (auto diglet : diglets) { + if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { + gbtword80_t pld = (diglet >> 12) & mTVXMask; + if (pld.count() != 0) { + countsMBT++; + } + pld = (diglet >> 12) & mVBAMask; + if (pld.count() != 0) { + countsMBV++; + } + } + if (!mDoDigits) { + continue; + } + LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); + ret = addCTPDigit(linkCRU, rdhOrbit, diglet, pldmask, digitsMap); + } + } + if ((remnant.count() > 0) && stopBit) { + if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { + gbtword80_t pld = (remnant >> 12) & mTVXMask; + if (pld.count() != 0) { + countsMBT++; + } + pld = (remnant >> 12) & mVBAMask; + if (pld.count() != 0) { + countsMBV++; + } + } + if (!mDoDigits) { + continue; + } + ret = addCTPDigit(linkCRU, rdhOrbit, remnant, pldmask, digitsMap); + LOG(debug) << "diglet:" << remnant << " " << (remnant & bcmask).to_ullong(); + remnant = 0; + } + } + if (mDoLumi) { + lumiPointsHBF1.emplace_back(LumiInfo{orbit0, 0, 0, countsMBT, countsMBV}); + // std::cout << "last lumi:" << nhb << std::endl; + } + if (mDoDigits & mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + uint64_t trgclassmaskNOTRGDet = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMaskWInputs(); // classes triggered by internal ctp generators not here + trgclassmaskNOTRGDet = mCTPConfig.getTriggerClassMaskWInputsNoTrgDets(); + // mCTPConfig.printStream(std::cout); + } + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; + mLostDueToShiftInps += shiftInputs(digitsMap, digits, mTFOrbit); + if (mCheckConsistency) { + ret = checkReadoutConsistentncy(digits, trgclassmask, trgclassmaskNOTRGDet); + } + } + if (mDoDigits && !mDecodeInps) { + for (auto const& dig : digitsMap) { + digits.push_back(dig.second); + } + } + // ret = 1; + if (mStickyError) { + if (nwrites < mErrorMax) { + std::string file = "dumpCTP" + std::to_string(nwrites) + ".bin"; + std::ofstream dumpctp(file.c_str(), std::ios::out | std::ios::binary); + if (!dumpctp.good()) { + LOGP(error, "Failed to open file {}", file); + } else { + LOGP(info, "CTP dump file open {}", file); + for (auto it = parser.begin(); it != parser.end(); ++it) { + char* dataout = (char*)(it.raw()); + dumpctp.write(dataout, it.sizeTotal()); + } + dumpctp.close(); + } + nwrites++; + } + // LOG(error) << "CTP decoding IR errors:" << mErrorIR << " TCR errors:" << mErrorTCR; + } + return ret; +} +// +int RawDataDecoder::decodeRawFatal(o2::framework::InputRecord& inputs, std::vector& filter) +{ + o2::framework::DPLRawParser parser(inputs, filter); + uint32_t payloadCTP = 0; + gbtword80_t remnant = 0; + uint32_t size_gbt = 0; + mTFOrbit = 0; + uint32_t orbit0 = 0; + std::array rates{}; + std::array ratesC{}; + for (auto it = parser.begin(); it != parser.end(); ++it) { + const o2::header::RDHAny* rdh = nullptr; + try { + rdh = reinterpret_cast(it.raw()); + mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); + } catch (std::exception& e) { + LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); + // dummyOutput(); + return 1; + } + // auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); + uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); + uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); + uint32_t version = o2::raw::RDHUtils::getVersion(rdh); + uint32_t rdhOrbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); + uint32_t triggerType = o2::raw::RDHUtils::getTriggerType(rdh); + // std::cout << "diff orbits:" << triggerOrbit - rdhOrbit << std::endl; + bool tf = (triggerType & TF_TRIGGERTYPE_MASK) && (packetCounter == 0); + bool hb = (triggerType & HB_TRIGGERTYPE_MASK) && (packetCounter == 0); + if (tf) { + mTFOrbit = rdhOrbit; + // std::cout << "tforbit==================>" << mTFOrbit << " " << std::hex << mTFOrbit << std::endl; + mTFOrbits.push_back(mTFOrbit); + } + static bool prt = true; + if (prt) { + LOG(info) << "RDH version:" << version << " Padding:" << mPadding; + prt = false; + } + auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR + auto linkCRU = (feeID & 0xf00) >> 8; + // LOG(info) << "CRU link:" << linkCRU; + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + payloadCTP = o2::ctp::NIntRecPayload; + } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { + payloadCTP = o2::ctp::NClassPayload; + } else { + LOG(error) << "Unxpected CTP CRU link:" << linkCRU; + } + LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << " triggerType:" << triggerType; + // LOG(info) << "remnant :" << remnant.count(); + gbtword80_t pldmask = 0; + for (uint32_t i = 0; i < payloadCTP; i++) { + pldmask[12 + i] = 1; + } + // std::cout << (orbit0 != rdhOrbit) << " comp " << (mTFOrbit==rdhOrbit) << std::endl; + // if(orbit0 != rdhOrbit) { + if (hb) { + remnant = 0; + size_gbt = 0; + orbit0 = rdhOrbit; + // std::cout << "orbit0============>" << std::dec << orbit0 << " " << std::hex << orbit0 << std::endl; + } + // Create 80 bit words + gsl::span payload(it.data(), it.size()); + gbtword80_t gbtWord80; + gbtWord80.set(); + int wordCount = 0; + int wordSize = 10; + std::vector gbtwords80; + // mPadding = 0; + if (mPadding == 1) { + wordSize = 16; + } + // LOG(info) << ii << " payload size:" << payload.size(); + gbtword80_t bcmask = std::bitset<80>("111111111111"); + for (auto payloadWord : payload) { + int wc = wordCount % wordSize; + // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); + if ((wc == 0) && (wordCount != 0)) { + if (gbtWord80.count() != 80) { + gbtwords80.push_back(gbtWord80); + } + gbtWord80.set(); + } + if (wc < 10) { + for (int i = 0; i < 8; i++) { + gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); + } + } + wordCount++; + } + if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { + gbtwords80.push_back(gbtWord80); + } + // decode 80 bits payload + for (auto word : gbtwords80) { + std::vector diglets; + gbtword80_t gbtWord = word; + makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); + for (auto diglet : diglets) { + int nbits = payloadCTP - 12; + for (int i = 0; i < nbits; i++) { + gbtword80_t mask = 1ull << i; + gbtword80_t pld = (diglet >> 12) & mask; + // LOG(info) << "diglet:" << diglet << " pld:" << pld; + if (pld.count() != 0) { + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + rates[i]++; + } else { + ratesC[i]++; + } + } + } + // LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); + } + } + if ((remnant.count() > 0) && stopBit) { + int nbits = payloadCTP - 12; + for (int i = 0; i < nbits; i++) { + gbtword80_t mask = 1ull << i; + gbtword80_t pld = (remnant >> 12) & mask; + // LOG(info) << "diglet:" << remnant << " pld:" << pld; + if (pld.count() != 0) { + if (linkCRU == o2::ctp::GBTLinkIDIntRec) { + rates[i]++; + } else { + ratesC[i]++; + } + } + } + remnant = 0; + } + } + // print max rates + std::map ratesmap; + std::map ratesmapC; + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + if (rates[i]) { + ratesmap[rates[i]] = i; + } + if (ratesC[i]) { + ratesmapC[ratesC[i]] = i; + } + } + auto nhb = o2::base::GRPGeomHelper::getNHBFPerTF(); + std::string message = "Ringing inputs [MHz]:"; + for (auto const& r : boost::adaptors::reverse(ratesmap)) { + // LOG(error) << r.second; + message += " " + o2::ctp::CTPInputsConfiguration::getInputNameFromIndex(r.second + 1) + ":" + std::to_string(r.first / 32. / o2::constants::lhc::LHCOrbitMUS); + } + std::string messageC = "Ringing classes [MHz]:"; + for (auto const& r : boost::adaptors::reverse(ratesmapC)) { + messageC += " class" + std::to_string(r.second) + ":" + std::to_string(r.first / 32. / o2::constants::lhc::LHCOrbitMUS); + } + LOG(error) << messageC; + LOG(fatal) << message; + return 0; +} +// +int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector& filter, std::vector& digits, std::vector& lumiPointsHBF1) +{ + o2::pmr::vector pmrdigits; + int ret = decodeRaw(inputs, filter, pmrdigits, lumiPointsHBF1); + for (auto const d : pmrdigits) { + digits.push_back(d); + } + return ret; +} +// +// Not to be called with level LM +// Keeping shift in params if needed to be generalised +int RawDataDecoder::shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit, std::bitset<48>& inpmask, int64_t shift, int level, std::map& digmap) +{ + // + if (irin.orbit > TFOrbit || irin.bc >= shift) { + auto lxmask = L0MASKInputs; + if (level == 1) { + lxmask = L1MASKInputs; + } + auto ir = irin - shift; // add L0 to prev digit + if (digmap.count(ir)) { + if ((digmap[ir].CTPInputMask & lxmask).count()) { + LOG(error) << " Overwriting LX ? X:" << level; + } + digmap[ir].CTPInputMask = digmap[ir].CTPInputMask | (inpmask & lxmask); + } else { + CTPDigit digit = {ir, inpmask & lxmask, 0}; + digmap[ir] = digit; + } + } else { + // LOG(info) << "LOST:" << irin << " shift:" << shift; + return 1; + ; + } + return 0; +} +// + +int RawDataDecoder::shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask) +{ + // int nClasswoInp = 0; // counting classes without input which should never happen + int lost = 0; + std::map digitsMapShifted; + auto L0shift = o2::ctp::TriggerOffsetsParam::Instance().LM_L0; + auto L1shift = L0shift + o2::ctp::TriggerOffsetsParam::Instance().L0_L1; + for (auto const& dig : digitsMap) { + auto inpmask = dig.second.CTPInputMask; + auto inpmaskLM = inpmask & LMMASKInputs; + auto inpmaskL0 = inpmask & L0MASKInputs; + auto inpmaskL1 = inpmask & L1MASKInputs; + int lm = inpmaskLM.count() > 0; + int l0 = inpmaskL0.count() > 0; + int l1 = inpmaskL1.count() > 0; + int lut = lm + (l0 << 1) + (l1 << 2); + // std::cout << "L0mask:" << L0MASKInputs << std::endl; + // std::cout << "L0:" << inpmaskL0 << std::endl; + // std::cout << "L1:" << inpmaskL1 << std::endl; + if (lut == 0 || lut == 1) { // no inps or LM + digitsMapShifted[dig.first] = dig.second; + } else if (lut == 2) { // L0 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + // LOG(error) << "Adding class mask without input ?"; + // This is not needed as it can happen; Full checj done below - see next LOG(error) + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 4) { // L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 6) { // L0 and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + if (dig.second.CTPClassMask.count()) { + CTPDigit digi = {dig.first, 0, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } + } else if (lut == 3) { // LM and L0 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + CTPDigit digi = {dig.first, inpmask & (~L0MASKInputs), dig.second.CTPClassMask}; + // if LM level do not need to add class as LM is not shifted; + digitsMapShifted[dig.first] = digi; + } else if (lut == 5) { // LM and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + CTPDigit digi = {dig.first, inpmask & (~L1MASKInputs), dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } else if (lut == 7) { // LM and L0 and L1 + lost += shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + lost += shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + CTPDigit digi = {dig.first, inpmaskLM, dig.second.CTPClassMask}; + digitsMapShifted[dig.first] = digi; + } else { + LOG(fatal) << "lut = " << lut; + } + } + for (auto const& dig : digitsMapShifted) { + digits.push_back(dig.second); + } + return lost; +} +// +int RawDataDecoder::checkReadoutConsistentncy(o2::pmr::vector& digits, uint64_t trgclassmask, uint64_t trgclassmaskNoTrgDet) +{ + LOG(debug) << "Checking readout"; + int ret = 0; + static int nerror = 0; + for (auto const& digit : digits) { + // if class mask => inps + for (int i = 0; i < digit.CTPClassMask.size(); i++) { + bool trgcls = trgclassmask & (1ull << i); + if (digit.CTPClassMask[i] & trgcls) { + const CTPClass* cls = mCTPConfig.getCTPClassFromHWIndex(i); + if (cls == nullptr) { + if (nerror < mErrorMax) { + LOG(error) << "Class mask index not found in CTP config:" << i; + nerror++; + } + ret = 128; + continue; + } + mClassCountersA[i]++; + if (cls->descriptor == nullptr) { + continue; + } + uint64_t clsinpmask = cls->descriptor->getInputsMask(); + uint64_t diginpmask = digit.CTPInputMask.to_ullong(); + if (!((clsinpmask & diginpmask) == clsinpmask)) { + if (nerror < mErrorMax) { + LOG(error) << "Cls=>Inps: CTP class:" << cls->name << " inpmask:" << clsinpmask << " not compatible with inputs mask:" << diginpmask; + nerror++; + } + mClassErrorsA[i]++; + ret = 128; + } + } + } + // if inps => class mask + for (auto const& cls : mCTPConfig.getCTPClasses()) { + // cls.printStream(std::cout); + if (cls.descriptor == nullptr) { + continue; + } + uint64_t clsinpmask = cls.descriptor->getInputsMask(); // class definition + uint64_t diginpmask = digit.CTPInputMask.to_ullong(); + uint64_t digclsmask = digit.CTPClassMask.to_ullong(); + if ((clsinpmask & diginpmask) == clsinpmask) { + if (cls.classMask & trgclassmask) { + mClassCountersB[cls.getIndex()]++; + if ((cls.classMask & digclsmask) == 0) { + int32_t BCShiftCorrection = -o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; + int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; + offset = o2::constants::lhc::LHCMaxBunches - offset; + if (digit.intRecord.bc < offset) { + if ((nerror < mErrorMax) && (cls.classMask & ~trgclassmaskNoTrgDet)) { + LOG(info) << "Inp=>Cls: CTP class:" << cls.name << " inpmask:" << clsinpmask << " cls mask:" << cls.classMask << " not found in digit:" << digit; + nerror++; + } + mClassErrorsB[cls.getIndex()]++; + ret = 256; + } else { + mLostDueToShiftCC++; + } + } + } + } + } + } + return ret; +} +// +int RawDataDecoder::setLumiInp(int lumiinp, std::string inp) +{ + // check if valid input + int index = o2::ctp::CTPInputsConfiguration::getInputIndexFromName(inp); + if (index == 0xff) { + LOG(fatal) << "CTP raw decoder: input index not found:" << inp; + return 0xff; + } + if (lumiinp == 1) { + mTVXMask.reset(); + mTVXMask[index - 1] = true; + } else { + mVBAMask.reset(); + mVBAMask[index - 1] = true; + } + return index; +} +// +int RawDataDecoder::init() +{ + return 0; +} diff --git a/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h b/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h index 68202c8d22996..7d29f2144c2b3 100644 --- a/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h +++ b/Detectors/CTP/simulation/include/CTPSimulation/Digitizer.h @@ -34,7 +34,10 @@ class Digitizer void setCCDBServer(const std::string& server) { mCCDBServer = server; } std::vector process(const gsl::span detinputs); void calculateClassMask(const std::bitset ctpinpmask, std::bitset& classmask); + void setCTPConfiguration(o2::ctp::CTPConfiguration* config); + o2::ctp::CTPConfiguration* getDefaultCTPConfiguration(); void init(); + private: // CTP configuration std::string mCCDBServer = o2::base::NameConf::getCCDBServer(); @@ -43,4 +46,4 @@ class Digitizer }; } // namespace ctp } // namespace o2 -#endif //ALICEO2_CTP_DIGITIZER_H +#endif // ALICEO2_CTP_DIGITIZER_H diff --git a/Detectors/CTP/simulation/src/Digitizer.cxx b/Detectors/CTP/simulation/src/Digitizer.cxx index dc84e1bee1982..b1d4ef40b7b0e 100644 --- a/Detectors/CTP/simulation/src/Digitizer.cxx +++ b/Detectors/CTP/simulation/src/Digitizer.cxx @@ -18,67 +18,97 @@ #include #include +#include + using namespace o2::ctp; ClassImp(Digitizer); // Trigger detector config needed here. std::vector Digitizer::process(const gsl::span detinputs) { + if (!mCTPConfiguration) { + setCTPConfiguration(getDefaultCTPConfiguration()); + } std::map> det2ctpinp = mCTPConfiguration->getDet2InputMap(); // To be taken from config database ? std::map detInputName2Mask = - {{"MVBA", 1}, {"MVIR", 0x10}, {"MVOR", 2}, {"MVNC", 4}, {"MVCH", 8}, {"MT0A", 1}, {"MT0C", 2}, {"MTVX", 0x10}, {"MTCE", 8}, {"MTSC", 0x4}, {"0U0A", 1}, {"0U0C", 2}, {"0UVX", 0x10}, {"0UCE", 8}, {"0USC", 0x4}}; + {{"MVBA", 1}, {"MVOR", 2}, {"MVNC", 4}, {"MVCH", 8}, {"MVIR", 0x10}, {"MT0A", 1}, {"MT0C", 2}, {"MTSC", 4}, {"MTCE", 8}, {"MTVX", 0x10}, {"0U0A", 1}, {"0U0C", 2}, {"0USC", 4}, {"0UCE", 8}, {"0UVX", 0x10}, {"EMBA", 0x1}, {"0EMC", 0x2}, {"0DMC", 0x4}}; + + // pre-sorting detector inputs per interaction record std::map> predigits; for (auto const& inp : detinputs) { predigits[inp.intRecord].push_back(&inp); } + std::vector digits; for (auto const& hits : predigits) { - CTPDigit data; - data.intRecord = hits.first; std::bitset inpmaskcoll = 0; + auto currentIR = hits.first; for (auto const inp : hits.second) { switch (inp->detector) { case o2::detectors::DetID::FT0: { // see dummy database above for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FT0]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::FV0: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FV0]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::FDD: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::FDD]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::EMC: { + // uint64_t inpmaskdebug = 1; + uint64_t inpmaskdebug = (inp->inputsMask).to_ullong(); + if (inpmaskdebug & detInputName2Mask["EMBA"]) { + // MB-accept must be treated separately, as it is not a CTP input + std::bitset emcMBaccept; + emcMBaccept.set(CTP_NINPUTS - 1, 1); + inpmaskcoll |= emcMBaccept; + } // else { // needs to be done always, remove else for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::EMC]) { - uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + uint64_t mask = inpmaskdebug & detInputName2Mask[ctpinp.name]; + // uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } + // } + // LOG(info) << "EMC input mask:" << inpmaskcoll << " with IR = " << currentIR.bc << ", orbit = " << currentIR.orbit; break; } case o2::detectors::DetID::PHS: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::PHS]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } case o2::detectors::DetID::ZDC: { for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::ZDC]) { uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - inpmaskcoll |= std::bitset(ctpinp.inputMask); + if (mask) { + inpmaskcoll |= std::bitset(ctpinp.inputMask); + } } break; } @@ -87,11 +117,25 @@ std::vector Digitizer::process(const gsl::span LOG(error) << "CTP Digitizer: unknown detector:" << inp->detector; break; } + // inpmaskcoll.reset(); // debug + // inpmaskcoll[47] = 1; // debug + } // end loop over trigger input for this interaction + if (inpmaskcoll.to_ullong()) { + // we put the trigger only when non-trivial + std::bitset<64> classmask; + calculateClassMask(inpmaskcoll, classmask); + if (classmask.to_ulong() == 0) { + // No class accepted + continue; + } + CTPDigit data; + data.intRecord = hits.first; + data.CTPInputMask = inpmaskcoll; + data.CTPClassMask = classmask; + digits.emplace_back(data); + LOG(info) << "Trigger-Event " << data.intRecord.bc << " " << data.intRecord.orbit << " Input mask:" << inpmaskcoll << " with IR = " << data.intRecord.bc << ", orbit = " << data.intRecord.orbit; + // LOG(info) << "Trigger-Event " << data.intRecord.bc << " " << data.intRecord.orbit << " Class mask:" << data.CTPClassMask << " with IR = " << data.intRecord.bc << ", orbit = " << data.intRecord.orbit; } - LOG(info) << data.intRecord.bc << " " << data.intRecord.orbit << " Input mask:" << inpmaskcoll; - data.CTPInputMask = inpmaskcoll; - calculateClassMask(inpmaskcoll, data.CTPClassMask); - digits.emplace_back(data); } return std::move(digits); } @@ -99,12 +143,48 @@ void Digitizer::calculateClassMask(const std::bitset ctpinpmask, st { classmask = 0; for (auto const& tcl : mCTPConfiguration->getCTPClasses()) { - if (tcl.descriptor->getInputsMask() & ctpinpmask.to_ullong()) { - classmask |= (1 << tcl.classMask); + auto clustername = boost::algorithm::to_lower_copy(tcl.cluster->name); // make case-insensitive for string comparison + if (clustername == "emc") { + // check if Min Bias EMC class + bool tvxMBemc = tcl.name.find("C0TVX-B-NOPF-EMC") != std::string::npos; // 2023 + tvxMBemc |= tcl.name.find("C0TVX-A-NOPF-EMC") != std::string::npos; + tvxMBemc |= tcl.name.find("C0TVX-C-NOPF-EMC") != std::string::npos; + tvxMBemc |= tcl.name.find("C0TVX-E-NOPF-EMC") != std::string::npos; + if (clustername == "emc") { + tvxMBemc |= tcl.name.find("minbias_TVX_L0") != std::string::npos; // 2022 + } + // require real physics input in any case + if (tvxMBemc) { + // if the class is a min. bias class accept it only if the MB-accept bit is set in addition + // (fake trigger input) + if (ctpinpmask[CTP_NINPUTS - 1]) { + classmask |= tcl.classMask; + LOG(info) << "adding MBA:" << tcl.name; + } + } else if ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask()) { + // EMCAL rare triggers - physical trigger input + // class identification can be handled like in the case of the other + // classes as EMCAL trigger input is required + LOG(info) << "adding EMCal rare trigger:" << tcl.name; + classmask |= tcl.classMask; + } + } else { + if (tcl.descriptor && ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask())) { + classmask |= tcl.classMask; + } } } + LOG(info) << "input mask:" << ctpinpmask; + LOG(info) << "class mask:" << classmask; } -void Digitizer::init() + +void Digitizer::setCTPConfiguration(o2::ctp::CTPConfiguration* config) +{ + mCTPConfiguration = config; + LOG(info) << *mCTPConfiguration; +} + +o2::ctp::CTPConfiguration* Digitizer::getDefaultCTPConfiguration() { // CTP Configuration if (mCCDBServer.empty()) { @@ -114,9 +194,14 @@ void Digitizer::init() } auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBServer); - map metadata = {}; + std::map metadata = {}; long timestamp = 1546300800000; - mCTPConfiguration = mgr.getSpecific(o2::ctp::CCDBPathCTPConfig, timestamp, metadata); - mCTPConfiguration->printStream(std::cout); + + auto config = mgr.getSpecific(o2::ctp::CCDBPathCTPConfig, timestamp, metadata); LOG(info) << " @@@ CTP Digitizer:: CCDB connected " << std::endl; + return config; +} + +void Digitizer::init() +{ } diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h index 4596fe12cb31d..dda45c9f11a34 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h @@ -28,12 +28,13 @@ namespace ctp class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + void updateTimeDependentParams(framework::ProcessingContext& pc); private: o2::ctp::CTFCoder mCTFCoder; @@ -41,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h index 3a023ce2022dc..a63119264e071 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ctp class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR, bool noLumi); + EntropyEncoderSpec(bool selIR, bool noLumi, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, bool noLumiInput = false, const std::string& ctfdictOpt = "none"); } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h index a899453c0a6a1..3198e5c33e219 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h @@ -14,12 +14,12 @@ #include #include - #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/WorkflowSpec.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" -#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "CTPReconstruction/RawDataDecoder.h" namespace o2 { @@ -51,21 +51,18 @@ class RawDecoderSpec : public framework::Task /// Input RawData: {"ROUT", "RAWDATA", 0, Lifetime::Timeframe} /// Output HW errors: {"CTP", "RAWHWERRORS", 0, Lifetime::Timeframe} -later void run(framework::ProcessingContext& ctx) final; - static void makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld); - int addCTPDigit(uint32_t linkCRU, uint32_t triggerOrbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits); + void updateTimeDependentParams(framework::ProcessingContext& pc); protected: private: - static constexpr uint32_t TF_TRIGGERTYPE_MASK = 0x800; - static constexpr uint32_t HB_TRIGGERTYPE_MASK = 0x2; // for digits bool mDoDigits = true; - std::vector mOutputDigits; + o2::pmr::vector mOutputDigits; + int mMaxInputSize = 0; + bool mMaxInputSizeFatal = 0; // for lumi bool mDoLumi = true; // - gbtword80_t mTVXMask = 0x4; // TVX is 3rd input - gbtword80_t mVBAMask = 0x20; // VBA is 6 th input LumiInfo mOutputLumiInfo; bool mVerbose = false; uint64_t mCountsT = 0; @@ -73,13 +70,21 @@ class RawDecoderSpec : public framework::Task uint32_t mNTFToIntegrate = 1; uint32_t mNHBIntegratedT = 0; uint32_t mNHBIntegratedV = 0; - uint32_t mIRRejected = 0; - uint32_t mTCRRejected = 0; - bool mPadding = true; - uint32_t mTFOrbit = 0; + bool mDecodeinputs = 0; std::deque mHistoryT; std::deque mHistoryV; - std::vector mTFOrbits; + RawDataDecoder mDecoder; + // Errors + int mLostDueToShiftInps = 0; + int mErrorIR = 0; + int mErrorTCR = 0; + int mIRRejected = 0; + int mTCRRejected = 0; + std::array mClsEA{}; + std::array mClsEB{}; // from inputs + std::array mClsA{}; + std::array mClsB{}; // from inputs + bool mCheckConsistency = false; }; /// \brief Creating DataProcessorSpec for the CTP diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index edce577599acf..0fa8fb0004e4c 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -24,13 +24,13 @@ namespace o2 { namespace ctp { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_CTP"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -43,6 +43,9 @@ void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matche void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { mCTFCoder.init(ic); + bool decodeinps = !ic.options().get("ignore-ctpinputs-decoding-ctf"); + mCTFCoder.setDecodeInps(decodeinps); + LOG(info) << "Decode inputs:" << decodeinps; } void EntropyDecoderSpec::run(ProcessingContext& pc) @@ -51,11 +54,10 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc, true); - auto buff = pc.inputs().get>("ctf"); - + updateTimeDependentParams(pc); + auto buff = pc.inputs().get>("ctf_CTP"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); - auto& lumi = pc.outputs().make(OutputRef{"lumi"}); + auto& lumi = pc.outputs().make(OutputRef{"CTPLumi"}); // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object if (buff.size()) { @@ -72,26 +74,43 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) LOGF(info, "CTP Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +void EntropyDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + mCTFCoder.updateTimeDependentParams(pc, true); + if (pc.services().get().globalRunNumberChanged) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (mCTFCoder.getDecodeInps()) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mCTFCoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mCTFCoder.getCTPConfig().getRunNumber(); + } + } + } +} -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "CTP", "DIGITS", 0, Lifetime::Timeframe}, - OutputSpec{{"lumi"}, "CTP", "LUMI", 0, Lifetime::Timeframe}, + OutputSpec{{"CTPLumi"}, "CTP", "LUMI", 0, Lifetime::Timeframe}, OutputSpec{{"ctfrep"}, "CTP", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); - inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("ctf_CTP", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_CTP", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + } + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", 1)); return DataProcessorSpec{ "ctp-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx index ba091891fbd5c..902fe22dadcc9 100644 --- a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ctp { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR), mNoLumi(nolumi) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR), mNoLumi(nolumi) { mTimer.Stop(); mTimer.Reset(); @@ -50,11 +49,21 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); + static LumiInfo lumiPrev; + const int maxDumRep = 5; + static int dumRep = 0; LumiInfo lumi{}; if (!mNoLumi) { - lumi = pc.inputs().get("lumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(LumiInfo)) { + lumiPrev = lumi = pc.inputs().get("CTPLumi"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumi = lumiPrev; + } } - auto& buffer = pc.outputs().make>(Output{"CTP", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"CTP", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, lumi); pc.outputs().snapshot({"ctfrep", 0}, iosize); mTimer.Stop(); @@ -67,14 +76,17 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "CTP", "DIGITS", 0, Lifetime::Timeframe); if (!nolumi) { - inputs.emplace_back("lumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + } + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); } - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -82,12 +94,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR, bool nolumi) "ctp-entropy-encoder", inputs, Outputs{{"CTP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "CTP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR, nolumi)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, nolumi, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx index dfdd9353e3629..041e6cb472ebb 100644 --- a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx @@ -13,64 +13,100 @@ #include #include "Framework/InputRecordWalker.h" #include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" #include "DetectorsRaw/RDHUtils.h" -#include "DPLUtils/DPLRawParser.h" #include "CTPWorkflow/RawDecoderSpec.h" #include "CommonUtils/VerbosityConfig.h" +#include "Framework/InputRecord.h" +#include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsCTP/Configuration.h" using namespace o2::ctp::reco_workflow; void RawDecoderSpec::init(framework::InitContext& ctx) { + mCheckConsistency = ctx.options().get("check-consistency"); + mDecoder.setCheckConsistency(mCheckConsistency); + mDecodeinputs = ctx.options().get("ctpinputs-decoding"); + mDecoder.setDecodeInps(mDecodeinputs); mNTFToIntegrate = ctx.options().get("ntf-to-average"); mVerbose = ctx.options().get("use-verbose-mode"); - LOG(info) << "CTP reco init done"; + int maxerrors = ctx.options().get("print-errors-num"); + mDecoder.setVerbose(mVerbose); + mDecoder.setDoLumi(mDoLumi); + mDecoder.setDoDigits(mDoDigits); + mDecoder.setMAXErrors(maxerrors); + std::string lumiinp1 = ctx.options().get("lumi-inp1"); + std::string lumiinp2 = ctx.options().get("lumi-inp2"); + int inp1 = mDecoder.setLumiInp(1, lumiinp1); + int inp2 = mDecoder.setLumiInp(2, lumiinp2); + mOutputLumiInfo.inp1 = inp1; + mOutputLumiInfo.inp2 = inp2; + mMaxInputSize = ctx.options().get("max-input-size"); + mMaxInputSizeFatal = ctx.options().get("max-input-size-fatal"); + LOG(info) << "CTP reco init done. Inputs decoding here:" << mDecodeinputs << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal << " CheckConsistency:" << mCheckConsistency; + // mOutputLumiInfo.printInputs(); } void RawDecoderSpec::endOfStream(framework::EndOfStreamContext& ec) { - std::sort(mTFOrbits.begin(), mTFOrbits.end()); - size_t l = mTFOrbits.size(); + auto& TFOrbits = mDecoder.getTFOrbits(); + std::sort(TFOrbits.begin(), TFOrbits.end()); + size_t l = TFOrbits.size(); uint32_t o0 = 0; if (l) { - o0 = mTFOrbits[0]; + o0 = TFOrbits[0]; } int nmiss = 0; int nprt = 0; std::cout << "Missing orbits:"; for (int i = 1; i < l; i++) { - if ((mTFOrbits[i] - o0) > 0x20) { + if ((TFOrbits[i] - o0) > 0x20) { if (nprt < 20) { - std::cout << " " << o0 << "-" << mTFOrbits[i]; + std::cout << " " << o0 << "-" << TFOrbits[i]; } - nmiss += (mTFOrbits[i] - o0) / 0x20; + nmiss += (TFOrbits[i] - o0) / 0x20; nprt++; } - o0 = mTFOrbits[i]; + o0 = TFOrbits[i]; } std::cout << std::endl; - std::cout << "Number of missing TF:" << nmiss << std::endl; + LOG(info) << "Number of non continous TF:" << nmiss << std::endl; + LOG(info) << "Lost in shiftInputs:" << mLostDueToShiftInps; + LOG(info) << "Lost in addDigit Inputs:" << mIRRejected << " Classes:" << mTCRRejected; + if (mErrorIR || mErrorTCR) { + LOG(error) << "# of IR errors:" << mErrorIR << " TCR errors:" << mErrorTCR << std::endl; + } + if (mCheckConsistency) { + LOG(info) << "Lost due to the shift Consistency Checker:" << mDecoder.getLostDueToShiftCls(); + auto ctpcfg = mDecoder.getCTPConfig(); + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + std::string name = ctpcfg.getClassNameFromIndex(i); + if (mClsEA[i]) { + LOG(error) << " Class without inputs:"; + } + LOG(important) << "CLASS:" << name << ":" << i << " Cls=>Inp:" << mClsA[i] << " Inp=>Cls:" << mClsB[i] << " ErrorsCls=>Inps:" << mClsEA[i] << " MissingInps=>Cls:" << mClsEB[i]; + } + } } void RawDecoderSpec::run(framework::ProcessingContext& ctx) { + updateTimeDependentParams(ctx); mOutputDigits.clear(); std::map digits; using InputSpec = o2::framework::InputSpec; using ConcreteDataTypeMatcher = o2::framework::ConcreteDataTypeMatcher; using Lifetime = o2::framework::Lifetime; - // setUpDummyLink auto& inputs = ctx.inputs(); auto dummyOutput = [&ctx, this]() { if (this->mDoDigits) { - ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, this->mOutputDigits); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, this->mOutputDigits); } if (this->mDoLumi) { - ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0, o2::framework::Lifetime::Timeframe}, this->mOutputLumiInfo); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, this->mOutputLumiInfo); } }; - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow { @@ -93,180 +129,57 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) contDeadBeef = 0; // if good data, reset the counter } // - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, Lifetime::Timeframe}}; - o2::framework::DPLRawParser parser(ctx.inputs(), filter); std::vector lumiPointsHBF1; - uint64_t countsMBT = 0; - uint64_t countsMBV = 0; - uint32_t payloadCTP; - gbtword80_t remnant = 0; - uint32_t size_gbt = 0; - mTFOrbit = 0; - uint32_t orbit0 = 0; - for (auto it = parser.begin(); it != parser.end(); ++it) { - const o2::header::RDHAny* rdh = nullptr; - try { - rdh = reinterpret_cast(it.raw()); - mPadding = (o2::raw::RDHUtils::getDataFormat(rdh) == 0); - } catch (std::exception& e) { - LOG(error) << "Failed to extract RDH, abandoning TF sending dummy output, exception was: " << e.what(); - dummyOutput(); - return; - } - // auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - uint32_t stopBit = o2::raw::RDHUtils::getStop(rdh); - uint32_t packetCounter = o2::raw::RDHUtils::getPageCounter(rdh); - uint32_t version = o2::raw::RDHUtils::getVersion(rdh); - uint32_t rdhOrbit = o2::raw::RDHUtils::getHeartBeatOrbit(rdh); - uint32_t triggerType = o2::raw::RDHUtils::getTriggerType(rdh); - // std::cout << "diff orbits:" << triggerOrbit - rdhOrbit << std::endl; - bool tf = (triggerType & TF_TRIGGERTYPE_MASK) && (packetCounter == 0); - bool hb = (triggerType & HB_TRIGGERTYPE_MASK) && (packetCounter == 0); - if (tf) { - mTFOrbit = rdhOrbit; - // std::cout << "tforbit==================>" << mTFOrbit << " " << std::hex << mTFOrbit << std::endl; - mTFOrbits.push_back(mTFOrbit); - } - static bool prt = true; - if (prt) { - LOG(info) << "RDH version:" << version << " Padding:" << mPadding; - prt = false; - } - auto feeID = o2::raw::RDHUtils::getFEEID(rdh); // 0 = IR, 1 = TCR - auto linkCRU = (feeID & 0xf00) >> 8; - if (linkCRU == o2::ctp::GBTLinkIDIntRec) { - payloadCTP = o2::ctp::NIntRecPayload; - } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { - payloadCTP = o2::ctp::NClassPayload; - if (!mDoDigits) { // Do not do TCR if only lumi - continue; - } - } else { - LOG(error) << "Unxpected CTP CRU link:" << linkCRU; - } - LOG(debug) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << " triggerType:" << triggerType; - // LOG(info) << "remnant :" << remnant.count(); - gbtword80_t pldmask = 0; - for (uint32_t i = 0; i < payloadCTP; i++) { - pldmask[12 + i] = 1; - } - // std::cout << (orbit0 != rdhOrbit) << " comp " << (mTFOrbit==rdhOrbit) << std::endl; - // if(orbit0 != rdhOrbit) { - if (hb) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { // create lumi per HB - lumiPointsHBF1.emplace_back(LumiInfo{rdhOrbit, 0, 0, countsMBT, countsMBV}); - countsMBT = 0; - countsMBV = 0; - } - remnant = 0; - size_gbt = 0; - orbit0 = rdhOrbit; - // std::cout << "orbit0============>" << std::dec << orbit0 << " " << std::hex << orbit0 << std::endl; - } - // Create 80 bit words - gsl::span payload(it.data(), it.size()); - gbtword80_t gbtWord80; - gbtWord80.set(); - int wordCount = 0; - int wordSize = 10; - std::vector gbtwords80; - // mPadding = 0; - if (mPadding == 1) { - wordSize = 16; - } - /* if (payload.size()) { - //LOG(info) << "payload size:" << payload.size(); - // LOG(info) << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << triggerOrbit << " stopbit:" << stopBit << " packet:" << packetCounter; - // LOGP(info, "RDH FEEid: {} CRU link: {}, Orbit: {}", feeID, linkCRU, triggerOrbit); - std::cout << std::hex << "RDH FEEid: " << feeID << " CTP CRU link:" << linkCRU << " Orbit:" << rdhOrbit << std::endl; - } */ - gbtword80_t bcmask = std::bitset<80>("111111111111"); - for (auto payloadWord : payload) { - int wc = wordCount % wordSize; - // LOG(info) << wordCount << ":" << wc << " payload:" << int(payloadWord); - if ((wc == 0) && (wordCount != 0)) { - if (gbtWord80.count() != 80) { - gbtwords80.push_back(gbtWord80); - /* uint64_t bcid = (gbtWord80 & bcmask).to_ullong(); - if (bcid < 279) - bcid += 3564 - 279; - else - bcid += -279; - std::string ss = fmt::format("{:x}", bcid); - LOG(info) << "w80:" << gbtWord80 << " " << ss; - // LOGP(info,"w80: {} bcid:{%x}", gbtWord80,bcid); */ - } - gbtWord80.set(); - } - if (wc < 10) { - for (int i = 0; i < 8; i++) { - gbtWord80[wc * 8 + i] = bool(int(payloadWord) & (1 << i)); - } - } - wordCount++; - } - if ((gbtWord80.count() != 80) && (gbtWord80.count() > 0)) { - gbtwords80.push_back(gbtWord80); - /*uint64_t bcid = (gbtWord80 & bcmask).to_ullong(); - if (bcid < 279) - bcid += 3564 - 279; - else - bcid += -279; - std::string ss = fmt::format("{:x}", bcid); - LOG(info) << "w80l:" << gbtWord80 << " " << ss; */ - } - // decode 80 bits payload - for (auto word : gbtwords80) { - std::vector diglets; - gbtword80_t gbtWord = word; - makeGBTWordInverse(diglets, gbtWord, remnant, size_gbt, payloadCTP); - for (auto diglet : diglets) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { - gbtword80_t pld = (diglet >> 12) & mTVXMask; - if (pld.count() != 0) { - countsMBT++; - } - pld = (diglet >> 12) & mVBAMask; - if (pld.count() != 0) { - countsMBV++; - } - } - if (!mDoDigits) { - continue; - } - LOG(debug) << "diglet:" << diglet << " " << (diglet & bcmask).to_ullong(); - addCTPDigit(linkCRU, rdhOrbit, diglet, pldmask, digits); - } + std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, Lifetime::Timeframe}}; + bool fatal_flag = 0; + if (mMaxInputSize > 0) { + size_t payloadSize = 0; + for (const auto& ref : o2::framework::InputRecordWalker(inputs, filter)) { + const auto dh = o2::framework::DataRefUtils::getHeader(ref); + payloadSize += o2::framework::DataRefUtils::getPayloadSize(ref); } - // if ((remnant.count() > 0) && stopBit) { - if (remnant.count() > 0) { - if (mDoLumi && payloadCTP == o2::ctp::NIntRecPayload) { - gbtword80_t pld = (remnant >> 12) & mTVXMask; - if (pld.count() != 0) { - countsMBT++; - } - pld = (remnant >> 12) & mVBAMask; - if (pld.count() != 0) { - countsMBV++; - } - } - if (!mDoDigits) { - continue; + if (payloadSize > (size_t)mMaxInputSize) { + if (mMaxInputSizeFatal) { + fatal_flag = 1; + LOG(error) << "Input data size bigger than threshold: " << mMaxInputSize << " < " << payloadSize << " decoding TF and exiting."; + // LOG(fatal) << "Input data size:" << payloadSize; - fatal issued in decoder + } else { + LOG(error) << "Input data size:" << payloadSize << " sending dummy output"; + dummyOutput(); + return; } - addCTPDigit(linkCRU, rdhOrbit, remnant, pldmask, digits); - LOG(debug) << "diglet:" << remnant << " " << (remnant & bcmask).to_ullong(); - remnant = 0; } } + int ret = 0; + if (fatal_flag) { + ret = mDecoder.decodeRawFatal(inputs, filter); + } else { + ret = mDecoder.decodeRaw(inputs, filter, mOutputDigits, lumiPointsHBF1); + } + if (ret == 1) { + dummyOutput(); + return; + } if (mDoDigits) { - for (auto const digmap : digits) { - mOutputDigits.push_back(digmap.second); + LOG(info) << "[CTPRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits. IR rejected:" << mDecoder.getIRRejected() << " TCR rejected:" << mDecoder.getTCRRejected(); + ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, mOutputDigits); + mLostDueToShiftInps += mDecoder.getLostDueToShiftInp(); + mErrorIR += mDecoder.getErrorIR(); + mErrorTCR += mDecoder.getErrorTCR(); + mIRRejected += mDecoder.getIRRejected(); + mTCRRejected += mDecoder.getTCRRejected(); + auto clsEA = mDecoder.getClassErrorsA(); + auto clsEB = mDecoder.getClassErrorsB(); + auto cntCA = mDecoder.getClassCountersA(); + auto cntCB = mDecoder.getClassCountersB(); + for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) { + mClsEA[i] += clsEA[i]; + mClsEB[i] += clsEB[i]; + mClsA[i] += cntCA[i]; + mClsB[i] += cntCB[i]; } - LOG(info) << "[CTPRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits. IR rejected:" << mIRRejected << " TCR rejected:" << mTCRRejected; - ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mOutputDigits); } if (mDoLumi) { - lumiPointsHBF1.emplace_back(LumiInfo{orbit0, 0, 0, countsMBT, countsMBV}); uint32_t tfCountsT = 0; uint32_t tfCountsV = 0; for (auto const& lp : lumiPointsHBF1) { @@ -297,105 +210,16 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) mOutputLumiInfo.orbit = lumiPointsHBF1[0].orbit; } mOutputLumiInfo.counts = mCountsT; + mOutputLumiInfo.countsFV0 = mCountsV; mOutputLumiInfo.nHBFCounted = mNHBIntegratedT; mOutputLumiInfo.nHBFCountedFV0 = mNHBIntegratedV; if (mVerbose) { - LOGP(info, "Orbit {}: {}/{} counts T/V in {}/{} HBFs -> lumiT = {:.3e}+-{:.3e} lumiV = {:.3e}+-{:.3e}", mOutputLumiInfo.orbit, mCountsT, mCountsV, mNHBIntegratedT, mNHBIntegratedV, mOutputLumiInfo.getLumi(), mOutputLumiInfo.getLumiError(), mOutputLumiInfo.getLumiFV0(), mOutputLumiInfo.getLumiFV0Error()); - } - ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0, o2::framework::Lifetime::Timeframe}, mOutputLumiInfo); - } -} - -// Inverse of Digits2Raw::makeGBTWord -void RawDecoderSpec::makeGBTWordInverse(std::vector& diglets, gbtword80_t& GBTWord, gbtword80_t& remnant, uint32_t& size_gbt, uint32_t Npld) -{ - gbtword80_t diglet = remnant; - uint32_t i = 0; - while (i < (NGBT - Npld)) { - std::bitset masksize = 0; - for (uint32_t j = 0; j < (Npld - size_gbt); j++) { - masksize[j] = 1; + mOutputLumiInfo.printInputs(); + LOGP(info, "Orbit {}: {}/{} counts inp1/inp2 in {}/{} HBFs -> lumi_inp1 = {:.3e}+-{:.3e} lumi_inp2 = {:.3e}+-{:.3e}", mOutputLumiInfo.orbit, mCountsT, mCountsV, mNHBIntegratedT, mNHBIntegratedV, mOutputLumiInfo.getLumi(), mOutputLumiInfo.getLumiError(), mOutputLumiInfo.getLumiFV0(), mOutputLumiInfo.getLumiFV0Error()); } - diglet |= (GBTWord & masksize) << (size_gbt); - diglets.push_back(diglet); - diglet = 0; - i += Npld - size_gbt; - GBTWord = GBTWord >> (Npld - size_gbt); - size_gbt = 0; + ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, mOutputLumiInfo); } - size_gbt = NGBT - i; - remnant = GBTWord; -} -int RawDecoderSpec::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& diglet, gbtword80_t& pldmask, std::map& digits) -{ - gbtword80_t pld = (diglet & pldmask); - if (pld.count() == 0) { - return 0; - } - pld >>= 12; - CTPDigit digit; - const gbtword80_t bcidmask = 0xfff; - uint16_t bcid = (diglet & bcidmask).to_ulong(); - LOG(debug) << bcid << " pld:" << pld; - o2::InteractionRecord ir = {bcid, orbit}; - int32_t BCShiftCorrection = o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; - if (linkCRU == o2::ctp::GBTLinkIDIntRec) { - LOG(debug) << "InputMaskCount:" << digits[ir].CTPInputMask.count(); - LOG(debug) << "ir ir ori:" << ir; - // if ((int32_t)ir.bc < BCShiftCorrection) { - if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < BCShiftCorrection)) { - // LOG(warning) << "Loosing ir:" << ir; - mIRRejected++; - return 0; - } - ir -= BCShiftCorrection; - LOG(debug) << "ir ir corrected:" << ir; - digit.intRecord = ir; - if (digits.count(ir) == 0) { - digit.setInputMask(pld); - digits[ir] = digit; - LOG(debug) << bcid << " inputs case 0 bcid orbit " << orbit << " pld:" << pld; - } else if (digits.count(ir) == 1) { - if (digits[ir].CTPInputMask.count() == 0) { - digits[ir].setInputMask(pld); - LOG(debug) << bcid << " inputs bcid vase 1 orbit " << orbit << " pld:" << pld; - } else { - LOG(error) << "Two CTP IRs with the same timestamp:" << ir.bc << " " << ir.orbit; - } - } else { - LOG(error) << "Two digits with the same rimestamp:" << ir.bc << " " << ir.orbit; - } - } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { - int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1 - 1; - LOG(debug) << "tcr ir ori:" << ir; - // if ((int32_t)ir.bc < offset) { - if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < offset)) { - // if (0) { - LOG(warning) << "Loosing tclass:" << ir; - mTCRRejected++; - return 0; - } - ir -= offset; - LOG(debug) << "tcr ir corrected:" << ir; - digit.intRecord = ir; - if (digits.count(ir) == 0) { - digit.setClassMask(pld); - digits[ir] = digit; - LOG(debug) << bcid << " class bcid case 0 orbit " << orbit << " pld:" << pld; - } else if (digits.count(ir) == 1) { - if (digits[ir].CTPClassMask.count() == 0) { - digits[ir].setClassMask(pld); - LOG(debug) << bcid << " class bcid case 1 orbit " << orbit << " pld:" << pld; - } else { - LOG(error) << "Two CTP Class masks for same timestamp"; - } - } else { - } - } else { - LOG(error) << "Unxpected CTP CRU link:" << linkCRU; - } - return 0; } o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool askDISTSTF, bool digits, bool lumi) { @@ -403,12 +227,14 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool throw std::runtime_error("all outputs were disabled"); } std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); } std::vector outputs; + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/Config", 1)); + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/TriggerOffsets")); if (digits) { outputs.emplace_back("CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe); } @@ -422,5 +248,25 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(digits, lumi)}, o2::framework::Options{ {"ntf-to-average", o2::framework::VariantType::Int, 90, {"Time interval for averaging luminosity in units of TF"}}, - {"use-verbose-mode", o2::framework::VariantType::Bool, false, {"Verbose logging"}}}}; + {"print-errors-num", o2::framework::VariantType::Int, 3, {"Max number of errors to print"}}, + {"lumi-inp1", o2::framework::VariantType::String, "TVX", {"The first input used for online lumi. Name in capital."}}, + {"lumi-inp2", o2::framework::VariantType::String, "VBA", {"The second input used for online lumi. Name in capital."}}, + {"use-verbose-mode", o2::framework::VariantType::Bool, false, {"Verbose logging"}}, + {"max-input-size", o2::framework::VariantType::Int, 0, {"Do not process input if bigger than max size, 0 - do not check"}}, + {"max-input-size-fatal", o2::framework::VariantType::Bool, false, {"If true issue fatal error otherwise error only"}}, + {"check-consistency", o2::framework::VariantType::Bool, false, {"If true checks digits consistency using ctp config"}}, + {"ctpinputs-decoding", o2::framework::VariantType::Bool, false, {"Inputs alignment: true - raw decoder - has to be compatible with CTF decoder: allowed options: 10,01,00"}}}}; +} +void RawDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + if (pc.services().get().globalRunNumberChanged) { + pc.inputs().get("trigoffset"); + const auto& trigOffsParam = o2::ctp::TriggerOffsetsParam::Instance(); + LOG(info) << "updateing TroggerOffsetsParam: inputs L0_L1:" << trigOffsParam.L0_L1 << " classes L0_L1:" << trigOffsParam.L0_L1_classes; + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mDecoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mDecoder.getCTPConfig().getRunNumber(); + } + } } diff --git a/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx b/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx index c024ac1c954f8..31b9647972c79 100644 --- a/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx +++ b/Detectors/CTP/workflow/src/ctp-raw-decoder.cxx @@ -50,7 +50,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co !cfgc.options().get("no-digits"), !cfgc.options().get("no-lumi"))); if (!cfgc.options().get("disable-root-output")) { - specs.emplace_back(o2::ctp::getDigitWriterSpec(true)); + specs.emplace_back(o2::ctp::getDigitWriterSpec(!cfgc.options().get("no-lumi"))); } return specs; } diff --git a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 1fcaa89be9888..c2b324a4b3bfa 100644 --- a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"no-lumi-input", VariantType::Bool, false, {"Lumi info not available"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; @@ -38,6 +39,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"))); + wf.emplace_back(o2::ctp::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("no-lumi-input"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/CTP/workflowIO/CMakeLists.txt b/Detectors/CTP/workflowIO/CMakeLists.txt index 1fc171d536d55..e1db635515cac 100644 --- a/Detectors/CTP/workflowIO/CMakeLists.txt +++ b/Detectors/CTP/workflowIO/CMakeLists.txt @@ -1,23 +1,29 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_library(CTPWorkflowIO - SOURCES src/DigitReaderSpec.cxx - src/DigitWriterSpec.cxx - PUBLIC_LINK_LIBRARIES O2::Framework - O2::DataFormatsCTP - O2::DPLUtils - O2::DetectorsRaw - O2::Algorithm) + SOURCES src/DigitReaderSpec.cxx + src/DigitWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCTP + O2::DPLUtils + O2::DetectorsRaw + O2::Algorithm) o2_add_executable(digit-writer - COMPONENT_NAME ctp - SOURCES src/digits-writer-workflow.cxx - PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) + COMPONENT_NAME ctp + SOURCES src/digits-writer-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) + +o2_add_executable(digit-reader + COMPONENT_NAME ctp + SOURCES src/digits-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CTPWorkflowIO) \ No newline at end of file diff --git a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx index 59f608bef2efb..81e6f53f42dcc 100644 --- a/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx @@ -17,7 +17,10 @@ #include "DataFormatsCTP/LumiInfo.h" #include "Headers/DataHeader.h" #include "DetectorsCommonDataFormats/DetID.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" #include "CommonUtils/NameConf.h" +#include "CommonUtils/IRFrameSelector.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Framework/ControlService.h" @@ -50,6 +53,7 @@ class DigitReader : public Task std::unique_ptr mTree; bool mUseMC = false; // use MC truth + bool mUseIRFrames = false; // selected IRFrames mode std::string mDigTreeName = "o2sim"; std::string mDigitBranchName = "CTPDigits"; std::string mLumiBranchName = "CTPLumi"; @@ -58,7 +62,7 @@ class DigitReader : public Task DigitReader::DigitReader(bool useMC) { if (useMC) { - LOG(info) << "CTP does not support MC truth at the moment"; + LOG(info) << "CTP : truth = data as CTP inputs are already digital"; } } @@ -66,21 +70,70 @@ void DigitReader::init(InitContext& ic) { auto filename = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get("ctp-digit-infile")); + if (ic.options().hasOption("ignore-irframes") && !ic.options().get("ignore-irframes")) { + mUseIRFrames = true; + } connectTree(filename); } void DigitReader::run(ProcessingContext& pc) { - auto ent = mTree->GetReadEntry() + 1; - assert(ent < mTree->GetEntries()); // this should not happen - - mTree->GetEntry(ent); - LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; - pc.outputs().snapshot(Output{"CTP", "DIGITS", 0, Lifetime::Timeframe}, mDigits); - pc.outputs().snapshot(Output{"CTP", "LUMI", 0, Lifetime::Timeframe}, mLumi); - if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { - pc.services().get().endOfStream(); - pc.services().get().readyToQuit(QuitRequest::Me); + gsl::span irFrames{}; + // LOG(info) << "Using IRs:" << mUseIRFrames; + if (mUseIRFrames) { + irFrames = pc.inputs().get>("driverInfo"); + } + auto ent = mTree->GetReadEntry(); + if (!mUseIRFrames) { + ent++; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "DigitReader pushes " << mDigits.size() << " digits at entry " << ent; + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + } else { + std::vector digitSel; + if (irFrames.size()) { // we assume the IRFrames are in the increasing order + if (ent < 0) { + ent++; + } + o2::utils::IRFrameSelector irfSel; + // MC digits are already aligned + irfSel.setSelectedIRFrames(irFrames, 0, 0, 0, true); + const auto irMin = irfSel.getIRFrames().front().getMin(); // use processed IRframes for rough comparisons (possible shift!) + const auto irMax = irfSel.getIRFrames().back().getMax(); + LOGP(info, "Selecting IRFrame {}-{}", irMin.asString(), irMax.asString()); + while (ent < mTree->GetEntries()) { + if (ent > mTree->GetReadEntry()) { + mTree->GetEntry(ent); + } + if (mDigits.front().intRecord <= irMax && mDigits.back().intRecord >= irMin) { // THere is overlap + for (int i = 0; i < (int)mDigits.size(); i++) { + const auto& dig = mDigits[i]; + // if(irfSel.check(dig.intRecord)) { // adding selected digit + if (dig.intRecord >= irMin && dig.intRecord <= irMax) { + digitSel.push_back(dig); + LOG(info) << "adding:" << dig.intRecord << " ent:" << ent; + } + } + } + if (mDigits.back().intRecord < irMax) { // need to check the next entry + ent++; + continue; + } + break; // push collected data + } + } + pc.outputs().snapshot(Output{"CTP", "DIGITS", 0}, digitSel); + pc.outputs().snapshot(Output{"CTP", "LUMI", 0}, mLumi); // add full lumi for this TF + if (!irFrames.size() || irFrames.back().isLast()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } } } diff --git a/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx b/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx index 10318698309c7..a65b94d16f43c 100644 --- a/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx +++ b/Detectors/CTP/workflowIO/src/DigitWriterSpec.cxx @@ -37,7 +37,7 @@ framework::DataProcessorSpec getDigitWriterSpec(bool raw) return MakeRootTreeWriterSpec("ctp-digit-writer-dec", "ctpdigits.root", MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with CTP digits/Lumi"}, BranchDefinition>{InputSpec{"digit", "CTP", "DIGITS", 0}, "CTPDigits", logger}, - BranchDefinition{InputSpec{"lumi", "CTP", "LUMI", 0}, "CTPLumi"})(); + BranchDefinition{InputSpec{"CTPLumi", "CTP", "LUMI", 0}, "CTPLumi"})(); } // MC digits case, no lumi available return MakeRootTreeWriterSpec("ctp-digit-writer", "ctpdigits.root", diff --git a/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx b/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx new file mode 100644 index 0000000000000..fe9104fcf9978 --- /dev/null +++ b/Detectors/CTP/workflowIO/src/digits-reader-workflow.cxx @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"inputfile", o2::framework::VariantType::String, "ctpdigits.root", {"File with CTP digits"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"Do not reed MC truth branch"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/WorkflowSpec.h" +#include "CTPWorkflowIO/DigitReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + WorkflowSpec specs; + specs.emplace_back(o2::ctp::getDigitsReaderSpec(!configcontext.options().get("disable-mc"), configcontext.options().get("inputfile"))); + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + return std::move(specs); +} diff --git a/Detectors/CTP/workflowScalers/CMakeLists.txt b/Detectors/CTP/workflowScalers/CMakeLists.txt index 716da7e596482..f02a7f33e2abd 100644 --- a/Detectors/CTP/workflowScalers/CMakeLists.txt +++ b/Detectors/CTP/workflowScalers/CMakeLists.txt @@ -1,12 +1,44 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. +o2_add_library(CTPWorkflowScalers + SOURCES src/ctpCCDBManager.cxx + SOURCES src/RunManager.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCTP + AliceO2::BookkeepingApi) +o2_target_root_dictionary(CTPWorkflowScalers HEADERS + include/CTPWorkflowScalers/ctpCCDBManager.h) o2_add_executable( proxy COMPONENT_NAME ctp SOURCES src/ctp-proxy.cxx PUBLIC_LINK_LIBRARIES O2::DCStestWorkflow - O2::DataFormatsCTP) + O2::CTPWorkflowScalers) o2_add_executable( qc-proxy COMPONENT_NAME ctp SOURCES src/ctp-qc-proxy.cxx PUBLIC_LINK_LIBRARIES O2::DCStestWorkflow O2::DataFormatsCTP) +o2_add_executable( + ccdb-orbit + COMPONENT_NAME ctp + SOURCES src/ctp-ccdb-orbit.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + Boost::program_options) +o2_add_executable( + bk-write + COMPONENT_NAME ctp + SOURCES src/ctp-bk-write.cxx + PUBLIC_LINK_LIBRARIES O2::DataFormatsCTP + O2::CTPWorkflowScalers + AliceO2::BookkeepingApi + Boost::program_options) diff --git a/Detectors/CTP/workflowScalers/README.md b/Detectors/CTP/workflowScalers/README.md index 4b109f779dc1d..df8df2c55cc44 100644 --- a/Detectors/CTP/workflowScalers/README.md +++ b/Detectors/CTP/workflowScalers/README.md @@ -1,3 +1,7 @@ + + How to generate c++ proto files ? On the computer where project https://gitlab.cern.ch/aliceCTP3/ctp3-ipbus/-/tree/master diff --git a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h new file mode 100644 index 0000000000000..6d2172e3da165 --- /dev/null +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/RunManager.h @@ -0,0 +1,79 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.h +/// \brief Managing runs for config and scalers +/// \author Roman Lietava +#ifndef _CTP_RUNMANAGER_H_ +#define _CTP_RUNMANAGER_H_ +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include "BookkeepingApi/BkpClientFactory.h" +#include "BookkeepingApi/BkpClient.h" + +using namespace o2::bkp::api; +namespace o2 +{ +namespace ctp +{ +typedef std::map> counters_t; +typedef std::map> counters64_t; +struct CTPActiveRun { + CTPActiveRun() = default; + long timeStart; + long timeStop; + CTPConfiguration cfg; + CTPRunScalers scalers; + void initBK(); + int send2BK(std::unique_ptr& BKClient, size_t ts, bool start); + // + counters_t cnts0; // first counters in run + counters_t cntslast0; // last minus one read counters needed for overflow correction + counters_t cntslast; // last read counters + counters64_t overflows; + // QC + int qcwpcount = 0; +}; +class CTPRunManager : public ctpCCDBManager +{ + public: + CTPRunManager() = default; + void init(); + int loadRun(const std::string& cfg); + int setRunConfigBK(uint32_t runNumber, const std::string& cfg); + int stopRun(uint32_t irun, long timeStamp); + int addScalers(uint32_t irun, std::time_t time, bool start = 0); + int processMessage(std::string& topic, const std::string& message); + void printActiveRuns() const; + int loadScalerNames(); + int getNRuns(); + void setBKHost(std::string host) { mBKHost = host; }; + void setQCWritePeriod(int period) { mQCWritePeriod = period; }; + uint64_t checkOverflow(uint32_t lcnt0, uint32_t lcnt1, uint64_t lcntcor); + void printCounters(); + + private: + /// Database constants + std::string mBKHost = ""; + std::array mActiveRuns; + std::array mActiveRunNumbers; + std::array mCounters; + std::map mScalerName2Position; + std::map mRunsLoaded; + std::unique_ptr mBKClient; + int mEOX = 0; // redundancy check + int mNew = 1; // 1 - no CCDB: used for QC + int mQCWritePeriod = 3; // Time in 10secs between two writes to QCCD + ClassDefNV(CTPRunManager, 8); +}; +} // namespace ctp +} // namespace o2 +#endif //_CTP_RUNMANAGER_H_ diff --git a/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h new file mode 100644 index 0000000000000..df2aa79d18697 --- /dev/null +++ b/Detectors/CTP/workflowScalers/include/CTPWorkflowScalers/ctpCCDBManager.h @@ -0,0 +1,59 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.h +/// \brief Managing runs for config and scalers +/// \author Roman Lietava +#ifndef _CTP_CTPCCDB_H_ +#define _CTP_CTPCCDB_H_ +#include "DataFormatsCTP/Configuration.h" + +namespace o2 +{ +namespace ctp +{ +class ctpCCDBManager +{ + public: + ctpCCDBManager() = default; + int saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, long timeStop); + int saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, long timeStop); + int saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart); + int saveSoxOrbit(uint32_t runNumber, uint32_t soxOrbit, long timeStart); + int saveOrbitReset(long timeStamp); + int saveCtpCfg(uint32_t runNumber, long timeStamp); + static CTPConfiguration getConfigFromCCDB(long timestamp, std::string run, bool& ok); + CTPConfiguration getConfigFromCCDB(long timestamp, std::string run); + CTPRunScalers getScalersFromCCDB(long timestamp, std::string run, bool& ok); + static CTPRunScalers getScalersFromCCDB(long timestamp, std::string, std::string path, bool& ok); + static void setCCDBHost(std::string host) { mCCDBHost = host; }; + static void setQCDBHost(std::string host) { mQCDBHost = host; }; + void setCtpCfgDir(std::string& ctpcfgdir) { mCtpCfgDir = ctpcfgdir; }; + + protected: + /// Database constants + // std::string mCCDBHost = "http://ccdb-test.cern.ch:8080"; + // std::string mQCDBHost = "http://ali-qcdb.cern.ch:8083"; + static std::string mCCDBHost; + static std::string mQCDBHost; + const std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + // std::string mCCDBPathCTPConfig = "CTP/Config/Config"; - in Configuration.h + const std::string mQCDBPathCTPScalers = "qc/CTP/Scalers"; + const std::string mCCDBPathSoxOrbit = "CTP/Calib/FirstRunOrbit"; + const std::string mCCDBPathOrbitReset = "CTP/Calib/OrbitReset"; + const std::string mCCDBPathCtpCfg = "CTP/Config/CtpCfg"; + std::string mCtpCfgDir; + + ClassDefNV(ctpCCDBManager, 2); +}; +} // namespace ctp +} // namespace o2 +#endif //_CTP_CTPCCDB_H_ diff --git a/Detectors/CTP/workflowScalers/py/createCnts.py b/Detectors/CTP/workflowScalers/py/createCnts.py index 11d35a1591ba5..4773065cfca43 100644 --- a/Detectors/CTP/workflowScalers/py/createCnts.py +++ b/Detectors/CTP/workflowScalers/py/createCnts.py @@ -8,9 +8,21 @@ socket.bind("tcp://*:%s" % port) time.sleep(1) # +path = "/home/rl/countersLHCo/" +# run = "527345" -path = "/home/rl/counters/" filename = path + "20221014.cc" +# +#run = "528543" +#run = "528537" +#filename = path + "20221102.cc" +# +#run = "527963" +#filename = path + "20221024.cc" +# +#run = "527349" +#filename = path + "20221015.cc" +# filecfg = path + run + ".rcfg" # if len(sys.argv) == 2: @@ -42,7 +54,8 @@ def senddata(header, messagedata): print("Sending:",header, data) data = str(messagedata).encode('UTF-8') header = str(header).encode('UTF-8') - msg = [header, data] + test = str("test").encode('UTF-8') + msg = [header, data,test] socket.send_multipart(msg) time.sleep(1) ########################## diff --git a/Detectors/CTP/workflowScalers/py/pub_server.py b/Detectors/CTP/workflowScalers/py/pub_server.py index 088835ff62a41..fb26d2e3929f1 100644 --- a/Detectors/CTP/workflowScalers/py/pub_server.py +++ b/Detectors/CTP/workflowScalers/py/pub_server.py @@ -3,7 +3,7 @@ import sys import time -port = "500901" +port = "50090" if len(sys.argv) > 1: port = sys.argv[1] int(port) @@ -16,19 +16,24 @@ topic = random.randrange(0,2**32) header = "CTP" messagedata = str(topic)+"1111111111111111st" +tag = "tag" print("Sending:",header, messagedata) data = str(messagedata).encode() header = str(header).encode() +tag = str(tag).encode() socket.send(memoryview(header),zmq.SNDMORE) -socket.send(memoryview(data)) +socket.send(memoryview(data),zmq.SNDMORE) +socket.send(memoryview(tag)) time.sleep(1) while True: topic = random.randrange(0,2**32) header = "CTP" - messagedata = str(topic)+"1 2 3 4 5" - print("Sending:",header, messagedata) + #messagedata = str(topic)+"1 2 3 4 5+6 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuukkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk" + messagedata = "x "*1072 + #print("Sending:",header, messagedata) data = str(messagedata).encode() header = str(header).encode() socket.send(memoryview(header),zmq.SNDMORE) - socket.send(memoryview(data)) + socket.send(memoryview(data),zmq.SNDMORE) + socket.send(memoryview(tag)) time.sleep(1) diff --git a/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt b/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt new file mode 100644 index 0000000000000..ccad42af3f38d --- /dev/null +++ b/Detectors/CTP/workflowScalers/py/scalersCTP_v3.txt @@ -0,0 +1,1085 @@ +names: "runn0" +names: "runn1" +names: "runn2" +names: "runn3" +names: "runn4" +names: "runn5" +names: "runn6" +names: "runn7" +names: "runn8" +names: "runn9" +names: "runn10" +names: "runn11" +names: "runn12" +names: "runn13" +names: "runn14" +names: "runn15" +names: "ltg1_ORB" +names: "ltg1_HB" +names: "ltg1_HBr" +names: "ltg1_HC" +names: "ltg1_PH" +names: "ltg1_PP" +names: "ltg1_CAL" +names: "ltg1_SOT" +names: "ltg1_EOT" +names: "ltg1_SOC" +names: "ltg1_EOC" +names: "ltg1_TF" +names: "ltg1_FERST" +names: "ltg1_RT" +names: "ltg1_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg1_GAP1" +names: "ltg1_GAP2" +names: "ltg1_TPC_sync" +names: "ltg1_TPC_rst" +names: "ltg1_TOF" +names: "ltg2_ORB" +names: "ltg2_HB" +names: "ltg2_HBr" +names: "ltg2_HC" +names: "ltg2_PH" +names: "ltg2_PP" +names: "ltg2_CAL" +names: "ltg2_SOT" +names: "ltg2_EOT" +names: "ltg2_SOC" +names: "ltg2_EOC" +names: "ltg2_TF" +names: "ltg2_FERST" +names: "ltg2_RT" +names: "ltg2_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg2_GAP1" +names: "ltg2_GAP2" +names: "ltg2_TPC_sync" +names: "ltg2_TPC_rst" +names: "ltg2_TOF" +names: "ltg3_ORB" +names: "ltg3_HB" +names: "ltg3_HBr" +names: "ltg3_HC" +names: "ltg3_PH" +names: "ltg3_PP" +names: "ltg3_CAL" +names: "ltg3_SOT" +names: "ltg3_EOT" +names: "ltg3_SOC" +names: "ltg3_EOC" +names: "ltg3_TF" +names: "ltg3_FERST" +names: "ltg3_RT" +names: "ltg3_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg3_GAP1" +names: "ltg3_GAP2" +names: "ltg3_TPC_sync" +names: "ltg3_TPC_rst" +names: "ltg3_TOF" +names: "ltg4_ORB" +names: "ltg4_HB" +names: "ltg4_HBr" +names: "ltg4_HC" +names: "ltg4_PH" +names: "ltg4_PP" +names: "ltg4_CAL" +names: "ltg4_SOT" +names: "ltg4_EOT" +names: "ltg4_SOC" +names: "ltg4_EOC" +names: "ltg4_TF" +names: "ltg4_FERST" +names: "ltg4_RT" +names: "ltg4_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg4_GAP1" +names: "ltg4_GAP2" +names: "ltg4_TPC_sync" +names: "ltg4_TPC_rst" +names: "ltg4_TOF" +names: "ltg5_ORB" +names: "ltg5_HB" +names: "ltg5_HBr" +names: "ltg5_HC" +names: "ltg5_PH" +names: "ltg5_PP" +names: "ltg5_CAL" +names: "ltg5_SOT" +names: "ltg5_EOT" +names: "ltg5_SOC" +names: "ltg5_EOC" +names: "ltg5_TF" +names: "ltg5_FERST" +names: "ltg5_RT" +names: "ltg5_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg5_GAP1" +names: "ltg5_GAP2" +names: "ltg5_TPC_sync" +names: "ltg5_TPC_rst" +names: "ltg5_TOF" +names: "ltg6_ORB" +names: "ltg6_HB" +names: "ltg6_HBr" +names: "ltg6_HC" +names: "ltg6_PH" +names: "ltg6_PP" +names: "ltg6_CAL" +names: "ltg6_SOT" +names: "ltg6_EOT" +names: "ltg6_SOC" +names: "ltg6_EOC" +names: "ltg6_TF" +names: "ltg6_FERST" +names: "ltg6_RT" +names: "ltg6_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg6_GAP1" +names: "ltg6_GAP2" +names: "ltg6_TPC_sync" +names: "ltg6_TPC_rst" +names: "ltg6_TOF" +names: "ltg7_ORB" +names: "ltg7_HB" +names: "ltg7_HBr" +names: "ltg7_HC" +names: "ltg7_PH" +names: "ltg7_PP" +names: "ltg7_CAL" +names: "ltg7_SOT" +names: "ltg7_EOT" +names: "ltg7_SOC" +names: "ltg7_EOC" +names: "ltg7_TF" +names: "ltg7_FERST" +names: "ltg7_RT" +names: "ltg7_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg7_GAP1" +names: "ltg7_GAP2" +names: "ltg7_TPC_sync" +names: "ltg7_TPC_rst" +names: "ltg7_TOF" +names: "ltg8_ORB" +names: "ltg8_HB" +names: "ltg8_HBr" +names: "ltg8_HC" +names: "ltg8_PH" +names: "ltg8_PP" +names: "ltg8_CAL" +names: "ltg8_SOT" +names: "ltg8_EOT" +names: "ltg8_SOC" +names: "ltg8_EOC" +names: "ltg8_TF" +names: "ltg8_FERST" +names: "ltg8_RT" +names: "ltg8_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg8_GAP1" +names: "ltg8_GAP2" +names: "ltg8_TPC_sync" +names: "ltg8_TPC_rst" +names: "ltg8_TOF" +names: "ltg9_ORB" +names: "ltg9_HB" +names: "ltg9_HBr" +names: "ltg9_HC" +names: "ltg9_PH" +names: "ltg9_PP" +names: "ltg9_CAL" +names: "ltg9_SOT" +names: "ltg9_EOT" +names: "ltg9_SOC" +names: "ltg9_EOC" +names: "ltg9_TF" +names: "ltg9_FERST" +names: "ltg9_RT" +names: "ltg9_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg9_GAP1" +names: "ltg9_GAP2" +names: "ltg9_TPC_sync" +names: "ltg9_TPC_rst" +names: "ltg9_TOF" +names: "ltg10_ORB" +names: "ltg10_HB" +names: "ltg10_HBr" +names: "ltg10_HC" +names: "ltg10_PH" +names: "ltg10_PP" +names: "ltg10_CAL" +names: "ltg10_SOT" +names: "ltg10_EOT" +names: "ltg10_SOC" +names: "ltg10_EOC" +names: "ltg10_TF" +names: "ltg10_FERST" +names: "ltg10_RT" +names: "ltg10_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg10_GAP1" +names: "ltg10_GAP2" +names: "ltg10_TPC_sync" +names: "ltg10_TPC_rst" +names: "ltg10_TOF" +names: "ltg11_ORB" +names: "ltg11_HB" +names: "ltg11_HBr" +names: "ltg11_HC" +names: "ltg11_PH" +names: "ltg11_PP" +names: "ltg11_CAL" +names: "ltg11_SOT" +names: "ltg11_EOT" +names: "ltg11_SOC" +names: "ltg11_EOC" +names: "ltg11_TF" +names: "ltg11_FERST" +names: "ltg11_RT" +names: "ltg11_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg11_GAP1" +names: "ltg11_GAP2" +names: "ltg11_TPC_sync" +names: "ltg11_TPC_rst" +names: "ltg11_TOF" +names: "ltg12_ORB" +names: "ltg12_HB" +names: "ltg12_HBr" +names: "ltg12_HC" +names: "ltg12_PH" +names: "ltg12_PP" +names: "ltg12_CAL" +names: "ltg12_SOT" +names: "ltg12_EOT" +names: "ltg12_SOC" +names: "ltg12_EOC" +names: "ltg12_TF" +names: "ltg12_FERST" +names: "ltg12_RT" +names: "ltg12_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg12_GAP1" +names: "ltg12_GAP2" +names: "ltg12_TPC_sync" +names: "ltg12_TPC_rst" +names: "ltg12_TOF" +names: "ltg13_ORB" +names: "ltg13_HB" +names: "ltg13_HBr" +names: "ltg13_HC" +names: "ltg13_PH" +names: "ltg13_PP" +names: "ltg13_CAL" +names: "ltg13_SOT" +names: "ltg13_EOT" +names: "ltg13_SOC" +names: "ltg13_EOC" +names: "ltg13_TF" +names: "ltg13_FERST" +names: "ltg13_RT" +names: "ltg13_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg13_GAP1" +names: "ltg13_GAP2" +names: "ltg13_TPC_sync" +names: "ltg13_TPC_rst" +names: "ltg13_TOF" +names: "ltg14_ORB" +names: "ltg14_HB" +names: "ltg14_HBr" +names: "ltg14_HC" +names: "ltg14_PH" +names: "ltg14_PP" +names: "ltg14_CAL" +names: "ltg14_SOT" +names: "ltg14_EOT" +names: "ltg14_SOC" +names: "ltg14_EOC" +names: "ltg14_TF" +names: "ltg14_FERST" +names: "ltg14_RT" +names: "ltg14_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg14_GAP1" +names: "ltg14_GAP2" +names: "ltg14_TPC_sync" +names: "ltg14_TPC_rst" +names: "ltg14_TOF" +names: "ltg15_ORB" +names: "ltg15_HB" +names: "ltg15_HBr" +names: "ltg15_HC" +names: "ltg15_PH" +names: "ltg15_PP" +names: "ltg15_CAL" +names: "ltg15_SOT" +names: "ltg15_EOT" +names: "ltg15_SOC" +names: "ltg15_EOC" +names: "ltg15_TF" +names: "ltg15_FERST" +names: "ltg15_RT" +names: "ltg15_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg15_GAP1" +names: "ltg15_GAP2" +names: "ltg15_TPC_sync" +names: "ltg15_TPC_rst" +names: "ltg15_TOF" +names: "ltg16_ORB" +names: "ltg16_HB" +names: "ltg16_HBr" +names: "ltg16_HC" +names: "ltg16_PH" +names: "ltg16_PP" +names: "ltg16_CAL" +names: "ltg16_SOT" +names: "ltg16_EOT" +names: "ltg16_SOC" +names: "ltg16_EOC" +names: "ltg16_TF" +names: "ltg16_FERST" +names: "ltg16_RT" +names: "ltg16_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg16_GAP1" +names: "ltg16_GAP2" +names: "ltg16_TPC_sync" +names: "ltg16_TPC_rst" +names: "ltg16_TOF" +names: "ltg17_ORB" +names: "ltg17_HB" +names: "ltg17_HBr" +names: "ltg17_HC" +names: "ltg17_PH" +names: "ltg17_PP" +names: "ltg17_CAL" +names: "ltg17_SOT" +names: "ltg17_EOT" +names: "ltg17_SOC" +names: "ltg17_EOC" +names: "ltg17_TF" +names: "ltg17_FERST" +names: "ltg17_RT" +names: "ltg17_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg17_GAP1" +names: "ltg17_GAP2" +names: "ltg17_TPC_sync" +names: "ltg17_TPC_rst" +names: "ltg17_TOF" +names: "ltg18_ORB" +names: "ltg18_HB" +names: "ltg18_HBr" +names: "ltg18_HC" +names: "ltg18_PH" +names: "ltg18_PP" +names: "ltg18_CAL" +names: "ltg18_SOT" +names: "ltg18_EOT" +names: "ltg18_SOC" +names: "ltg18_EOC" +names: "ltg18_TF" +names: "ltg18_FERST" +names: "ltg18_RT" +names: "ltg18_RS" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "" +names: "ltg18_GAP1" +names: "ltg18_GAP2" +names: "ltg18_TPC_sync" +names: "ltg18_TPC_rst" +names: "ltg18_TOF" +names: "bc40" +names: "clk240" +names: "extorb" +names: "PLSRin" +names: "FastLMin" +names: "BUSYin" +names: "SPAREin" +names: "inp1" +names: "inp2" +names: "inp3" +names: "inp4" +names: "inp5" +names: "inp6" +names: "inp7" +names: "inp8" +names: "inp9" +names: "inp10" +names: "inp11" +names: "inp12" +names: "inp13" +names: "inp14" +names: "inp15" +names: "inp16" +names: "inp17" +names: "inp18" +names: "inp19" +names: "inp20" +names: "inp21" +names: "inp22" +names: "inp23" +names: "inp24" +names: "inp25" +names: "inp26" +names: "inp27" +names: "inp28" +names: "inp29" +names: "inp30" +names: "inp31" +names: "inp32" +names: "inp33" +names: "inp34" +names: "inp35" +names: "inp36" +names: "inp37" +names: "inp38" +names: "inp39" +names: "inp40" +names: "inp41" +names: "inp42" +names: "inp43" +names: "inp44" +names: "inp45" +names: "inp46" +names: "inp47" +names: "inp48" +names: "clamb1" +names: "clamb2" +names: "clamb3" +names: "clamb4" +names: "clamb5" +names: "clamb6" +names: "clamb7" +names: "clamb8" +names: "clamb9" +names: "clamb10" +names: "clamb11" +names: "clamb12" +names: "clamb13" +names: "clamb14" +names: "clamb15" +names: "clamb16" +names: "clamb17" +names: "clamb18" +names: "clamb19" +names: "clamb20" +names: "clamb21" +names: "clamb22" +names: "clamb23" +names: "clamb24" +names: "clamb25" +names: "clamb26" +names: "clamb27" +names: "clamb28" +names: "clamb29" +names: "clamb30" +names: "clamb31" +names: "clamb32" +names: "clamb33" +names: "clamb34" +names: "clamb35" +names: "clamb36" +names: "clamb37" +names: "clamb38" +names: "clamb39" +names: "clamb40" +names: "clamb41" +names: "clamb42" +names: "clamb43" +names: "clamb44" +names: "clamb45" +names: "clamb46" +names: "clamb47" +names: "clamb48" +names: "clamb49" +names: "clamb50" +names: "clamb51" +names: "clamb52" +names: "clamb53" +names: "clamb54" +names: "clamb55" +names: "clamb56" +names: "clamb57" +names: "clamb58" +names: "clamb59" +names: "clamb60" +names: "clamb61" +names: "clamb62" +names: "clamb63" +names: "clamb64" +names: "clama1" +names: "clama2" +names: "clama3" +names: "clama4" +names: "clama5" +names: "clama6" +names: "clama7" +names: "clama8" +names: "clama9" +names: "clama10" +names: "clama11" +names: "clama12" +names: "clama13" +names: "clama14" +names: "clama15" +names: "clama16" +names: "clama17" +names: "clama18" +names: "clama19" +names: "clama20" +names: "clama21" +names: "clama22" +names: "clama23" +names: "clama24" +names: "clama25" +names: "clama26" +names: "clama27" +names: "clama28" +names: "clama29" +names: "clama30" +names: "clama31" +names: "clama32" +names: "clama33" +names: "clama34" +names: "clama35" +names: "clama36" +names: "clama37" +names: "clama38" +names: "clama39" +names: "clama40" +names: "clama41" +names: "clama42" +names: "clama43" +names: "clama44" +names: "clama45" +names: "clama46" +names: "clama47" +names: "clama48" +names: "clama49" +names: "clama50" +names: "clama51" +names: "clama52" +names: "clama53" +names: "clama54" +names: "clama55" +names: "clama56" +names: "clama57" +names: "clama58" +names: "clama59" +names: "clama60" +names: "clama61" +names: "clama62" +names: "clama63" +names: "clama64" +names: "cla0b1" +names: "cla0b2" +names: "cla0b3" +names: "cla0b4" +names: "cla0b5" +names: "cla0b6" +names: "cla0b7" +names: "cla0b8" +names: "cla0b9" +names: "cla0b10" +names: "cla0b11" +names: "cla0b12" +names: "cla0b13" +names: "cla0b14" +names: "cla0b15" +names: "cla0b16" +names: "cla0b17" +names: "cla0b18" +names: "cla0b19" +names: "cla0b20" +names: "cla0b21" +names: "cla0b22" +names: "cla0b23" +names: "cla0b24" +names: "cla0b25" +names: "cla0b26" +names: "cla0b27" +names: "cla0b28" +names: "cla0b29" +names: "cla0b30" +names: "cla0b31" +names: "cla0b32" +names: "cla0b33" +names: "cla0b34" +names: "cla0b35" +names: "cla0b36" +names: "cla0b37" +names: "cla0b38" +names: "cla0b39" +names: "cla0b40" +names: "cla0b41" +names: "cla0b42" +names: "cla0b43" +names: "cla0b44" +names: "cla0b45" +names: "cla0b46" +names: "cla0b47" +names: "cla0b48" +names: "cla0b49" +names: "cla0b50" +names: "cla0b51" +names: "cla0b52" +names: "cla0b53" +names: "cla0b54" +names: "cla0b55" +names: "cla0b56" +names: "cla0b57" +names: "cla0b58" +names: "cla0b59" +names: "cla0b60" +names: "cla0b61" +names: "cla0b62" +names: "cla0b63" +names: "cla0b64" +names: "cla0a1" +names: "cla0a2" +names: "cla0a3" +names: "cla0a4" +names: "cla0a5" +names: "cla0a6" +names: "cla0a7" +names: "cla0a8" +names: "cla0a9" +names: "cla0a10" +names: "cla0a11" +names: "cla0a12" +names: "cla0a13" +names: "cla0a14" +names: "cla0a15" +names: "cla0a16" +names: "cla0a17" +names: "cla0a18" +names: "cla0a19" +names: "cla0a20" +names: "cla0a21" +names: "cla0a22" +names: "cla0a23" +names: "cla0a24" +names: "cla0a25" +names: "cla0a26" +names: "cla0a27" +names: "cla0a28" +names: "cla0a29" +names: "cla0a30" +names: "cla0a31" +names: "cla0a32" +names: "cla0a33" +names: "cla0a34" +names: "cla0a35" +names: "cla0a36" +names: "cla0a37" +names: "cla0a38" +names: "cla0a39" +names: "cla0a40" +names: "cla0a41" +names: "cla0a42" +names: "cla0a43" +names: "cla0a44" +names: "cla0a45" +names: "cla0a46" +names: "cla0a47" +names: "cla0a48" +names: "cla0a49" +names: "cla0a50" +names: "cla0a51" +names: "cla0a52" +names: "cla0a53" +names: "cla0a54" +names: "cla0a55" +names: "cla0a56" +names: "cla0a57" +names: "cla0a58" +names: "cla0a59" +names: "cla0a60" +names: "cla0a61" +names: "cla0a62" +names: "cla0a63" +names: "cla0a64" +names: "cla1b1" +names: "cla1b2" +names: "cla1b3" +names: "cla1b4" +names: "cla1b5" +names: "cla1b6" +names: "cla1b7" +names: "cla1b8" +names: "cla1b9" +names: "cla1b10" +names: "cla1b11" +names: "cla1b12" +names: "cla1b13" +names: "cla1b14" +names: "cla1b15" +names: "cla1b16" +names: "cla1b17" +names: "cla1b18" +names: "cla1b19" +names: "cla1b20" +names: "cla1b21" +names: "cla1b22" +names: "cla1b23" +names: "cla1b24" +names: "cla1b25" +names: "cla1b26" +names: "cla1b27" +names: "cla1b28" +names: "cla1b29" +names: "cla1b30" +names: "cla1b31" +names: "cla1b32" +names: "cla1b33" +names: "cla1b34" +names: "cla1b35" +names: "cla1b36" +names: "cla1b37" +names: "cla1b38" +names: "cla1b39" +names: "cla1b40" +names: "cla1b41" +names: "cla1b42" +names: "cla1b43" +names: "cla1b44" +names: "cla1b45" +names: "cla1b46" +names: "cla1b47" +names: "cla1b48" +names: "cla1b49" +names: "cla1b50" +names: "cla1b51" +names: "cla1b52" +names: "cla1b53" +names: "cla1b54" +names: "cla1b55" +names: "cla1b56" +names: "cla1b57" +names: "cla1b58" +names: "cla1b59" +names: "cla1b60" +names: "cla1b61" +names: "cla1b62" +names: "cla1b63" +names: "cla1b64" +names: "cla1a1" +names: "cla1a2" +names: "cla1a3" +names: "cla1a4" +names: "cla1a5" +names: "cla1a6" +names: "cla1a7" +names: "cla1a8" +names: "cla1a9" +names: "cla1a10" +names: "cla1a11" +names: "cla1a12" +names: "cla1a13" +names: "cla1a14" +names: "cla1a15" +names: "cla1a16" +names: "cla1a17" +names: "cla1a18" +names: "cla1a19" +names: "cla1a20" +names: "cla1a21" +names: "cla1a22" +names: "cla1a23" +names: "cla1a24" +names: "cla1a25" +names: "cla1a26" +names: "cla1a27" +names: "cla1a28" +names: "cla1a29" +names: "cla1a30" +names: "cla1a31" +names: "cla1a32" +names: "cla1a33" +names: "cla1a34" +names: "cla1a35" +names: "cla1a36" +names: "cla1a37" +names: "cla1a38" +names: "cla1a39" +names: "cla1a40" +names: "cla1a41" +names: "cla1a42" +names: "cla1a43" +names: "cla1a44" +names: "cla1a45" +names: "cla1a46" +names: "cla1a47" +names: "cla1a48" +names: "cla1a49" +names: "cla1a50" +names: "cla1a51" +names: "cla1a52" +names: "cla1a53" +names: "cla1a54" +names: "cla1a55" +names: "cla1a56" +names: "cla1a57" +names: "cla1a58" +names: "cla1a59" +names: "cla1a60" +names: "cla1a61" +names: "cla1a62" +names: "cla1a63" +names: "cla1a64" +names: "l0_trigger" +names: "l1_trigger" +names: "l2_trigger" +names: "clum1" +names: "clum2" +names: "clum3" +names: "clum4" +names: "clum5" +names: "clum6" +names: "clu01" +names: "clu02" +names: "clu03" +names: "clu04" +names: "clu05" +names: "clu06" +names: "clu11" +names: "clu12" +names: "clu13" +names: "clu14" +names: "clu15" +names: "clu16" +names: "ltg1_busy" +names: "ltg2_busy" +names: "ltg3_busy" +names: "ltg4_busy" +names: "ltg5_busy" +names: "ltg6_busy" +names: "ltg7_busy" +names: "ltg8_busy" +names: "ltg9_busy" +names: "ltg10_busy" +names: "ltg11_busy" +names: "ltg12_busy" +names: "ltg13_busy" +names: "ltg14_busy" +names: "ltg15_busy" +names: "ltg16_busy" +names: "ltg17_busy" +names: "ltg18_busy" +names: "orbitid" +names: "clum7" +names: "clum8" +names: "clu07" +names: "clu08" +names: "clu17" +names: "clu18" +names: "clu_busy1" +names: "clu_busy2" +names: "clu_busy3" +names: "clu_busy4" +names: "clu_busy5" +names: "clu_busy6" +names: "clu_busy7" +names: "clu_busy8" diff --git a/Detectors/CTP/workflowScalers/py/watchProcess.py b/Detectors/CTP/workflowScalers/py/watchProcess.py new file mode 100755 index 0000000000000..5ac9a98833706 --- /dev/null +++ b/Detectors/CTP/workflowScalers/py/watchProcess.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +import requests,re +import os,sys,time,math +import time +from subprocess import Popen, PIPE +def send2(text = "ahoj"): + headers = { 'Content-Type':'application/json',} + data = { + 'text':text, + 'username':'alicetrg', + } + response = requests.post('https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh',headers=headers,json=data) + print(response) +# +def send(): + """ + does not work always return can not parse data + """ + Hpar = '\\"Content-Type:application/json\\"' + texth = '\\"text\\"' + text = '\\"lalal\\"' + usernameh = '\\"username\\"' + username = '\\"alicetrg\\"' + dpar = '{}:{},{}:{}'.format(texth,text,usernameh,username) + dpar = "{"+dpar+"}" + dpar = '"'+dpar+'"' + #print(dpar) + cmd = 'curl -g -i -X POST -H {} -d {} https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh'.format(Hpar,dpar) + #cmd = 'curl -i -X POST -H {} https://mattermost.web.cern.ch/hooks/75949oimoinr9b47uhp8c1oomh'.format(Hpar) + #cmd='curl -s -o /dev/null {} -F blob=@datafile.root {}/CTP/Calib/{}/{}/{}'.format(wpar, ccdb, actid, tval_s, tval_e) + process = Popen(cmd.split(), stdout = PIPE, stderr = PIPE) + stdo, stde = process.communicate() + print(cmd) + print(stdo) + print("====") + print(stde) +def getLog(service): + #cmd = "journalctl --no-hostname --user-unit "+service + cmd = 'journalctl --no-hostname --user-unit --since "1 day ago" '+service + print(cmd,cmd.split()) + process = Popen(cmd.split(), stdout = PIPE, stderr = PIPE) + stdo, stde= process.communicate() + stdo_str = stdo.decode("utf-8") + if stdo_str.find("No entries") != -1: + print("No entries for service:",service) + return None + #print(stdo) + return stdo_str +NMAX = 3 +def parseLog(log): + nsent = 0 + lines = log.split('\n') + print("# lines:",len(lines)) + print(lines[len(lines)-2]) + for line in lines: + if line.find("ERROR") != -1: + print(line) + if nsent < NMAX: + send2(line) + nsent += 1 + if line.find("ALARM") != -1: + print(line) + if nsent < NMAX: + send2(line) + nsent += 1 +# +nWarn = 0 +nAlarm = 0 +nError = 0 +def printNew(list,n,send = 0): + print(n,len(list)) + sendnow = 0 + if len(list) > n : + sendnow = 1 + for i in list[n:]: + print(i) + n = len(list) + if sendnow and send: + print("sending to mm:",line) + send2(line) + return n +def getLogFile(): + global nWarn, nAlarm,nError + nWarnList = [] + nAlarmList = [] + nErrorList = [] + MAX = 3 + file = "/home/rl/WORK/ctpproxy110424.log" + f = open(file,"r") + nsent = 0 + for line in f: + if line.find("ERROR") != -1: + print(line) + if line.find("ALARM") != -1: + #print(line) + nAlarmList.append(line) + #send2(line) + #nsent += 1 + if line.find("WARN") != -1: + #print(line) + #items = line.split("\]\[") + #items = re.split('\[|\]',line) + nWarnList.append(line) + #print(nWarn) + nWarn = printNew(nWarnList,nWarn) + nAlarm = printNew(nAlarmList,nAlarm,1) + f.close() +if __name__ =="__main__": + #send2("uj0"); + while 1: + now = time.localtime() + current_time = time.strftime("%H:%M:%S",now) + print("===> Time =", current_time) + log = getLogFile() + time.sleep(15) + + #parseLog(log) diff --git a/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h b/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h new file mode 100644 index 0000000000000..284e64a5222b1 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/CTPWorkflowScalersLinkDef.h @@ -0,0 +1,18 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::ctp::ctpCCDBManager + ; +#endif diff --git a/Detectors/CTP/workflowScalers/src/RunManager.cxx b/Detectors/CTP/workflowScalers/src/RunManager.cxx new file mode 100644 index 0000000000000..778677bec2ec9 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/RunManager.cxx @@ -0,0 +1,488 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RunManager.cxx +/// \author Roman Lietava + +#include "CTPWorkflowScalers/RunManager.h" +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include + +using namespace o2::ctp; +/// +/// Active run to keep cfg and saclers of active runs +/// Also used for Bookkeeping counters managment; +/// +void CTPActiveRun::initBK() +{ + std::vector clslist = cfg.getTriggerClassList(); + for (auto const& cls : clslist) { + cntslast[cls] = {0, 0, 0, 0, 0, 0}; + cntslast0[cls] = {0, 0, 0, 0, 0, 0}; + overflows[cls] = {0, 0, 0, 0, 0, 0}; + } +} +int CTPActiveRun::send2BK(std::unique_ptr& BKClient, size_t ts, bool start) +{ + int runNumber = cfg.getRunNumber(); + // LOG(info) << "BK Filling run:" << runNumber; + // int runOri = runNumber; + // runNumber = 123; + if (start) { + for (auto const& cls : cntslast) { + for (int i = 0; i < 6; i++) { + cnts0[cls.first][i] = cls.second[i]; + } + } + } + std::array cntsbk{0}; + for (auto const& cls : cntslast) { + for (int i = 0; i < 6; i++) { + if (cls.second[i] < cntslast0[cls.first][i]) { + overflows[cls.first][i]++; + } + cntslast0[cls.first][i] = cls.second[i]; + cntsbk[i] = (uint64_t)cls.second[i] + 0xffffffffull * overflows[cls.first][i] - (uint64_t)cnts0[cls.first][i]; + } + std::string clsname = cfg.getClassNameFromHWIndex(cls.first); + // clsname = std::to_string(runOri) + "_" + clsname; + try { + BKClient->ctpTriggerCounters()->createOrUpdateForRun(runNumber, clsname, ts, cntsbk[0], cntsbk[1], cntsbk[2], cntsbk[3], cntsbk[4], cntsbk[5]); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + return 1; + } + LOG(debug) << "Run BK:" << runNumber << " class:" << clsname << " cls.first" << cls.first << " ts:" << ts << " cnts:" << cntsbk[0] << " " << cntsbk[1] << " " << cntsbk[2] << " " << cntsbk[3] << " " << cntsbk[4] << " " << cntsbk[5]; + } + return 0; +} +/// +/// Run Manager to manage Config and Scalers +/// +void CTPRunManager::init() +{ + for (uint32_t i = 0; i < NRUNS; i++) { + mActiveRuns[i] = nullptr; + } + loadScalerNames(); + if (mBKHost != "none") { + mBKClient = BkpClientFactory::create(mBKHost); + LOG(info) << "BK Client created with:" << mBKHost; + } else { + LOG(info) << "BK not sent"; + } + setQCDBHost(mQCDBHost); + LOG(info) << "QC host:" << mQCDBHost; + LOG(info) << "QCDB writing every:" << mQCWritePeriod << " 10 secs"; + LOG(info) << "CCDB host:" << mCCDBHost; + LOG(info) << "CTP vNew cfg:" << mNew; + LOG(info) << "ctp.cfg dir:" << mCtpCfgDir; + LOG(info) << "CTPRunManager initialised."; +} +int CTPRunManager::loadRun(const std::string& cfg) +{ + LOG(info) << "Loading run: " << cfg; + auto now = std::chrono::system_clock::now(); + long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + LOG(info) << "Timestamp real time:" << timeStamp; + size_t pos = cfg.find(" "); + std::string cfgmod = cfg; + if (pos == std::string::npos) { + LOG(warning) << "Space not found in CTP config"; + } else { + std::string f = cfg.substr(0, pos); + if (f.find("run") == std::string::npos) { + double_t tt = std::stold(f); + timeStamp = (tt * 1000.); + LOG(info) << "Timestamp file:" << timeStamp; + cfgmod = cfg.substr(pos, cfg.size()); + LOG(info) << "ctpconfig: using ctp time"; + } + } + CTPActiveRun* activerun = new CTPActiveRun; + activerun->timeStart = timeStamp; + activerun->cfg.loadConfigurationRun3(cfgmod); + activerun->cfg.printStream(std::cout); + // + uint32_t runnumber = activerun->cfg.getRunNumber(); + activerun->scalers.setRunNumber(runnumber); + activerun->scalers.setClassMask(activerun->cfg.getTriggerClassMask()); + o2::detectors::DetID::mask_t detmask = activerun->cfg.getDetectorMask(); + activerun->scalers.setDetectorMask(detmask); + // + mRunsLoaded[runnumber] = activerun; + saveRunConfigToCCDB(&activerun->cfg, timeStamp); + if (mCtpCfgDir != "none") { + saveCtpCfg(runnumber, timeStamp); + } + return 0; +} +int CTPRunManager::setRunConfigBK(uint32_t runNumber, const std::string& cfg) +{ + std::cout << "Printing run:" << runNumber << " cfg:" << cfg << std::endl; + if (mBKClient) { + try { + mBKClient->run()->setRawCtpTriggerConfiguration(runNumber, cfg); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + return 1; + } + LOG(info) << "Run BK:" << runNumber << " CFG:" << cfg; + } + return 0; +} +int CTPRunManager::stopRun(uint32_t irun, long timeStamp) +{ + LOG(info) << "Stopping run index: " << irun; + if (mActiveRuns[irun] == nullptr) { + LOG(error) << "No config for run index:" << irun; + return 1; + } + // const auto now = std::chrono::system_clock::now(); + // const long timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + mActiveRuns[irun]->timeStop = timeStamp * 1000.; + saveRunScalersToCCDB(mActiveRuns[irun]->scalers, mActiveRuns[irun]->timeStart, mActiveRuns[irun]->timeStop); + saveRunScalersToQCDB(mActiveRuns[irun]->scalers, mActiveRuns[irun]->timeStart, mActiveRuns[irun]->timeStop); + delete mActiveRuns[irun]; + mActiveRuns[irun] = nullptr; + return 0; +} +int CTPRunManager::addScalers(uint32_t irun, std::time_t time, bool start) +{ + if (mActiveRuns[irun] == nullptr) { + LOG(error) << "No config for run index:" << irun; + return 1; + } + std::string orb = "extorb"; + std::string orbitid = "orbitid"; + CTPScalerRecordRaw scalrec; + scalrec.epochTime = time; + std::vector clslist = mActiveRuns[irun]->cfg.getTriggerClassList(); + for (auto const& cls : clslist) { + std::string cmb = "clamb" + std::to_string(cls + 1); + std::string cma = "clama" + std::to_string(cls + 1); + std::string c0b = "cla0b" + std::to_string(cls + 1); + std::string c0a = "cla0a" + std::to_string(cls + 1); + std::string c1b = "cla1b" + std::to_string(cls + 1); + std::string c1a = "cla1a" + std::to_string(cls + 1); + CTPScalerRaw scalraw; + scalraw.classIndex = (uint32_t)cls; + // std::cout << "cls:" << cls << " " << scalraw.classIndex << std::endl; + scalraw.lmBefore = mCounters[mScalerName2Position[cmb]]; + scalraw.lmAfter = mCounters[mScalerName2Position[cma]]; + scalraw.l0Before = mCounters[mScalerName2Position[c0b]]; + scalraw.l0After = mCounters[mScalerName2Position[c0a]]; + scalraw.l1Before = mCounters[mScalerName2Position[c1b]]; + scalraw.l1After = mCounters[mScalerName2Position[c1a]]; + // std::cout << "positions:" << cmb << " " << mScalerName2Position[cmb] << std::endl; + // std::cout << "positions:" << cma << " " << mScalerName2Position[cma] << std::endl; + scalrec.scalers.push_back(scalraw); + // BK scalers to be corrected for overflow + if (mBKClient) { + CTPActiveRun* ar = mActiveRuns[irun]; + ar->cntslast[cls][0] = scalraw.lmBefore; + ar->cntslast[cls][1] = scalraw.lmAfter; + ar->cntslast[cls][2] = scalraw.l0Before; + ar->cntslast[cls][3] = scalraw.l0After; + ar->cntslast[cls][4] = scalraw.l1Before; + ar->cntslast[cls][5] = scalraw.l1After; + } + } + mActiveRuns[irun]->send2BK(mBKClient, time, start); + // + uint32_t NINPS = 48; + int offset = 599; + for (uint32_t i = 0; i < NINPS; i++) { + uint32_t inpcount = mCounters[offset + i]; + scalrec.scalersInps.push_back(inpcount); + // LOG(info) << "Scaler for input:" << CTPRunScalers::scalerNames[offset+i] << ":" << inpcount; + } + // + if (mNew == 0) { + scalrec.intRecord.orbit = mCounters[mScalerName2Position[orb]]; + } else { + scalrec.intRecord.orbit = mCounters[mScalerName2Position[orbitid]]; + } + scalrec.intRecord.bc = 0; + mActiveRuns[irun]->scalers.addScalerRacordRaw(scalrec); + LOG(debug) << "Adding scalers for orbit:" << scalrec.intRecord.orbit; + // scalrec.printStream(std::cout); + // printCounters(); + return 0; +} +int CTPRunManager::processMessage(std::string& topic, const std::string& message) +{ + LOG(debug) << "Processing message with topic:" << topic; + std::string firstcounters; + if (topic.find("clear") != std::string::npos) { + mRunsLoaded.clear(); + LOG(info) << "Loaded runs cleared."; + return 0; + } + if (topic.find("ctpconfig") != std::string::npos) { + LOG(info) << "ctpconfig received"; + loadRun(message); + return 0; + } + if (topic.find("soxorbit") != std::string::npos) { + std::vector tokens = o2::utils::Str::tokenize(message, ' '); + int ret = 0; + if (tokens.size() == 3) { + long timestamp = std::stol(tokens[0]); + uint32_t runnumber = std::stoul(tokens[1]); + uint32_t orbit = std::stoul(tokens[2]); + ret = saveSoxOrbit(runnumber, orbit, timestamp); + std::string logmessage; + if (ret) { + logmessage = "Failed to update CCDB with SOX orbit."; + } else { + logmessage = "CCDB updated with SOX orbit."; + } + LOG(important) << logmessage << " run:" << runnumber << " sox orbit:" << orbit << " ts:" << timestamp; + } else { + LOG(error) << "Topic soxorbit dize !=3: " << message << " token size:" << tokens.size(); + ret = 1; + } + return ret; + } + if (topic.find("orbitreset") != std::string::npos) { + std::vector tokens = o2::utils::Str::tokenize(message, ' '); + int ret = 0; + if (tokens.size() == 1) { + long timestamp = std::stol(tokens[0]); + ret = saveOrbitReset(timestamp); + std::string logmessage; + if (ret) { + logmessage = "Failed to update CCDB with orbitreset. "; + } else { + logmessage = "CCDB updated with orbitreset. "; + } + LOG(important) << logmessage << timestamp; + } else { + LOG(error) << "Topic orbit reset != 2: " << message << " token size:" << tokens.size(); + ret = 1; + } + return ret; + } + if (topic.find("rocnts") != std::string::npos) { + return 0; + } + static int nerror = 0; + if (topic.find("sox") != std::string::npos) { + // get config + size_t irun = message.find("run"); + if (irun == std::string::npos) { + LOG(debug) << "run keyword not found in SOX"; + irun = message.size(); + } + LOG(info) << "SOX received, Run keyword position:" << irun; + std::string cfg = message.substr(irun, message.size() - irun); + firstcounters = message.substr(0, irun); + } else if (topic.find("eox") != std::string::npos) { + LOG(info) << "EOX received"; + mEOX = 1; + } else if (topic.find("cnts") != std::string::npos) { + // just continue + } else { + if (nerror < 10) { + LOG(warning) << "Skipping topic:" << topic; + nerror++; + } + return 0; + } + // + std::vector tokens; + if (firstcounters.size() > 0) { + tokens = o2::utils::Str::tokenize(firstcounters, ' '); + } else { + tokens = o2::utils::Str::tokenize(message, ' '); + } + if (tokens.size() != (CTPRunScalers::NCOUNTERS + 1)) { + if (tokens.size() == (CTPRunScalers::NCOUNTERSv2 + 1)) { + mNew = 0; + LOG(warning) << "v2 scaler size"; + } else { + LOG(warning) << "Scalers size wrong:" << tokens.size() << " expected:" << CTPRunScalers::NCOUNTERS + 1 << " or " << CTPRunScalers::NCOUNTERSv2 + 1; + return 1; + } + } + double timeStamp = std::stold(tokens.at(0)); + std::time_t tt = timeStamp; + LOG(debug) << "Processing scalers, all good, time:" << tokens.at(0) << " " << std::asctime(std::localtime(&tt)); + for (uint32_t i = 1; i < tokens.size(); i++) { + mCounters[i - 1] = std::stoull(tokens.at(i)); + if (i < (NRUNS + 1)) { + // std::cout << mCounters[i - 1] << " "; + } + } + // std::cout << std::endl; + LOG(debug) << "Counter size:" << tokens.size(); + // + for (uint32_t i = 0; i < NRUNS; i++) { + if ((mCounters[i] == 0) && (mActiveRunNumbers[i] == 0)) { + // not active + } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == mCounters[i])) { + // active , do scalers + LOG(debug) << "Run continue:" << mCounters[i]; + addScalers(i, tt); + // LOG(info) << " QC period:" << mActiveRunNumbers[i] << " " << mActiveRuns[i]->qcwpcount << " " << mQCWritePeriod; + if (mActiveRuns[i]->qcwpcount > mQCWritePeriod) { + saveRunScalersToQCDB(mActiveRuns[i]->scalers, tt * 1000, tt * 1000); + mActiveRuns[i]->qcwpcount = 0; + } + mActiveRuns[i]->qcwpcount++; + } else if ((mCounters[i] != 0) && (mActiveRunNumbers[i] == 0)) { + LOG(info) << "Run started:" << mCounters[i]; + auto run = mRunsLoaded.find(mCounters[i]); + if (run != mRunsLoaded.end()) { + mActiveRunNumbers[i] = mCounters[i]; + mActiveRuns[i] = run->second; + mRunsLoaded.erase(run); + setRunConfigBK(mActiveRuns[i]->cfg.getRunNumber(), mActiveRuns[i]->cfg.getConfigString()); + addScalers(i, tt, 1); + saveRunScalersToQCDB(mActiveRuns[i]->scalers, tt * 1000, tt * 1000); + } else { + LOG(error) << "Trying to start run which is not loaded:" << mCounters[i]; + } + } else if ((mCounters[i] == 0) && (mActiveRunNumbers[i] != 0)) { + if (mEOX != 1) { + LOG(error) << "Internal error in processMessage: mEOX != 1 expected 0: mEOX:" << mEOX; + } + LOG(info) << "Run stopped:" << mActiveRunNumbers[i]; + addScalers(i, tt); + mActiveRunNumbers[i] = 0; + mEOX = 0; + stopRun(i, tt); + } + } + mEOX = 0; + // printActiveRuns(); + return 0; +} +void CTPRunManager::printActiveRuns() const +{ + std::cout << "Active runs:"; + for (auto const& arun : mActiveRunNumbers) { + std::cout << arun << " "; + } + std::cout << " #loaded runs:" << mRunsLoaded.size(); + for (auto const& lrun : mRunsLoaded) { + std::cout << " " << lrun.second->cfg.getRunNumber(); + } + std::cout << std::endl; +} +int CTPRunManager::loadScalerNames() +{ + if (CTPRunScalers::NCOUNTERS != CTPRunScalers::scalerNames.size()) { + LOG(fatal) << "NCOUNTERS:" << CTPRunScalers::NCOUNTERS << " different from names vector:" << CTPRunScalers::scalerNames.size(); + return 1; + } + // try to open files of no success use default + for (uint32_t i = 0; i < CTPRunScalers::scalerNames.size(); i++) { + mScalerName2Position[CTPRunScalers::scalerNames[i]] = i; + } + return 0; +} +int CTPRunManager::getNRuns() +{ + int n = 0; + for (int i = 0; i < NRUNS; i++) { + if (mActiveRuns[i] != nullptr) { + n++; + } + } + return n; +} +void CTPRunManager::printCounters() +{ + int NDET = 18; + int NINPS = 48; + // int NCLKFP = 7; + int NLTG_start = NRUNS; + int NCLKFP_start = NLTG_start + NDET * 32; + int NINPS_start = NCLKFP_start + 7; + int NCLS_start = NINPS_start + NINPS; + std::cout << "====> CTP counters:" << std::endl; + std::cout << "RUNS:" << std::endl; + int ipos = 0; + for (uint32_t i = 0; i < NRUNS; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + for (int i = 0; i < NDET; i++) { + std::cout << "LTG" << i + 1 << std::endl; + for (int j = NLTG_start + i * 32; j < NLTG_start + (i + 1) * 32; j++) { + std::cout << ipos << ":" << mCounters[j] << " "; + ipos++; + } + std::cout << std::endl; + } + std::cout << "BC40,BC240,Orbit,pulser, fastlm, busy,spare" << std::endl; + for (int i = NCLKFP_start; i < NCLKFP_start + 7; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "INPUTS:" << std::endl; + for (int i = NINPS_start; i < NINPS_start + NINPS; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS M Before" << std::endl; + for (int i = NCLS_start; i < NCLS_start + 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS M After" << std::endl; + for (int i = NCLS_start + 64; i < NCLS_start + 2 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 0 Before" << std::endl; + for (int i = NCLS_start + 2 * 64; i < NCLS_start + 3 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 0 After" << std::endl; + for (int i = NCLS_start + 3 * 64; i < NCLS_start + 4 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 1 Before" << std::endl; + for (int i = NCLS_start + 4 * 64; i < NCLS_start + 5 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << "CLASS 1 After" << std::endl; + for (int i = NCLS_start + 5 * 64; i < NCLS_start + 6 * 64; i++) { + std::cout << ipos << ":" << mCounters[i] << " "; + ipos++; + } + std::cout << std::endl; + std::cout << " REST:" << std::endl; + for (uint32_t i = NCLS_start + 6 * 64; i < mCounters.size(); i++) { + if ((ipos % 10) == 0) { + std::cout << std::endl; + std::cout << ipos << ":"; + } + std::cout << mCounters[i] << " "; + } +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx new file mode 100644 index 0000000000000..8460c07dcc896 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx @@ -0,0 +1,170 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// example to run: +// +#include +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "BookkeepingApi/BkpClientFactory.h" +#include "BookkeepingApi/BkpClient.h" +#include +#include +#include +#include +namespace bpo = boost::program_options; +// +// Test in the lab +// o2-ctp-bk-write -r 37 -s 1 -c 1 --ccdb='http://acsl-ccdb.cern.ch:8083' -b 'acsl-aliecs.cern.ch:4001' -t 1753185071753 +// +int main(int argc, char** argv) +{ + const std::string testCCDB = "http://ccdb-test.cern.ch:8080"; + // std::string prodCCDB = "http://o2-ccdb.internal"; + const std::string aliceCCDB = "http://alice-ccdb.cern.ch"; + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " Write ctp config or scalers to BK\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("input-file,f", bpo::value()->default_value("none"), "input file name, none - do not read file"); + add_option("bkhost,b", bpo::value()->default_value("none"), "bk web address"); + add_option("ccdb", bpo::value()->default_value("alice"), "choose databse: test- test ccdb; prod - production ccdb; alice - alice ccdb; else ccdb parameter"); + add_option("run-number,r", bpo::value()->default_value(0), "run number"); + add_option("timestamp,t", bpo::value()->default_value(0), "timestamp; if 0 timestamp is calulated inside this code"); + add_option("cfg,c", bpo::value()->default_value(0), "Do cfg"); + add_option("scalers,s", bpo::value()->default_value(0), "Do scalers"); + // + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + uint64_t timestamp = vm["timestamp"].as(); + // + int ret = 0; + std::vector runs; + int32_t run = vm["run-number"].as(); + std::cout << "run:" << run << std::endl; + if (run) { + std::cout << "pushing" << std::endl; + runs.push_back(std::to_string(run)); + } + // read input file + std::string filename = vm["input-file"].as(); + if (filename != "none") { + std::ifstream file(filename); + if (!file.is_open()) { + LOG(fatal) << "Cannot open file:" << filename << std::endl; + } else { + std::string line; + while (std::getline(file, line)) { + std::cout << line << "\n"; + std::vector tokens = o2::utils::Str::tokenize(line, ' '); + // int run = std::stoi(tokens[0]); + runs.push_back(tokens[0]); + } + } + } + bool cfg = vm["cfg"].as(); + bool scalers = vm["scalers"].as(); + std::cout << "Doing: cfg:" << cfg << " scal:" << scalers << std::endl; + if (cfg || scalers) { + std::string bkhost = vm["bkhost"].as(); + std::unique_ptr mBKClient = o2::bkp::api::BkpClientFactory::create(bkhost); + // get from ccdb + std::string ccdbAddress; + if (vm["ccdb"].as() == "prod") { + // ccdbAddress = prodCCDB; + } else if (vm["ccdb"].as() == "test") { + ccdbAddress = testCCDB; + } else if (vm["ccdb"].as() == "alice") { + ccdbAddress = aliceCCDB; + } else { + ccdbAddress = vm["ccdb"].as(); + } + o2::ctp::ctpCCDBManager::setCCDBHost(ccdbAddress); + std::cout << "CCDB: " << vm["ccdb"].as() << " " << ccdbAddress << std::endl; + std::map metadata; + for (auto const& run : runs) { + metadata["runNumber"] = run; + bool ok; + int runNumber = std::stoi(run); + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); + + if (cfg) { + std::string ctpcfgstr = ctpcfg.getConfigString(); + try { + mBKClient->run()->setRawCtpTriggerConfiguration(runNumber, ctpcfgstr); + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(info) << "Run BK:" << run << " CFG:" << cfg; + } + if (scalers) { + auto ctpcnts = o2::ctp::ctpCCDBManager::getScalersFromCCDB(timestamp, run, "CTP/Calib/Scalers", ok); + ctpcnts.convertRawToO2(); + std::vector clsinds = ctpcnts.getClassIndexes(); + long ts = ctpcnts.getTimeLimit().second; + int i = 0; + for (auto const& ind : clsinds) { + std::array cntsbk = ctpcnts.getIntegralForClass(i); + std::string clsname = ctpcfg.getClassNameFromHWIndex(cntsbk[0]); + try { + mBKClient->ctpTriggerCounters()->createOrUpdateForRun(runNumber, clsname, ts, cntsbk[1], cntsbk[2], cntsbk[3], cntsbk[4], cntsbk[5], cntsbk[6]); + std::cout << runNumber << " clsname: " << cntsbk[0] << " " << clsname << " t:" << ts << " cnts:" << cntsbk[1] << " " << cntsbk[2] << " " << cntsbk[3] << " " << cntsbk[4] << " " << cntsbk[5] << " " << cntsbk[6] << std::endl; + ; + + } catch (std::runtime_error& error) { + std::cerr << "An error occurred: " << error.what() << std::endl; + // return 1; + } + LOG(debug) << "Run BK scalers ok"; + i++; + } + } + } + // add to bk + } + std::cout << "o2-ctp-bk-write done" << std::endl; + return ret; +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx new file mode 100644 index 0000000000000..aa953e89264ef --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx @@ -0,0 +1,151 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// example to run: +// +#include +#include +#include +#include +#include "CommonUtils/StringUtils.h" +#include +#include +#include +#include +namespace bpo = boost::program_options; +// +// get object from ccdb +// auto & cc = o2::ccdb::BasicCCDNManager::instance(); +// auto pp = ccdbMgr.getSpecific>("CTP/Calib/OrbitResetTest") +// std::cout << (*pp)[0] << std::endl; +int main(int argc, char** argv) +{ + const std::string testCCDB = "http://ccdb-test.cern.ch:8080"; + const std::string prodCCDB = "http://o2-ccdb.internal"; + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + " Write orbit staff to ccdb\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("output-file,o", bpo::value()->default_value("none"), "output file name, none - file not created"); + add_option("output-dir,d", bpo::value()->default_value("./"), "output dir"); + add_option("ccdb", bpo::value()->default_value("test"), "choose databse: test- test ccdb; prod - production ccdb; else ccdb parameter"); + add_option("action,a", bpo::value()->default_value(""), "sox - first orbit otherwise orbit reset"); + add_option("run-number,r", bpo::value()->default_value(123), "run number"); + add_option("testReset,t", bpo::value()->default_value(0), "0 = CTP/Calib/OrbitReset; 1 = CTP/Calib/OrbitResetTest"); + add_option("sox-orbit,x", bpo::value()->default_value(0), "SOX orbit"); + add_option("timestamp,s", bpo::value()->default_value(0), "timestamp of SOX/orbit reading; if 0 timestamp is calulated inside this code"); + + // + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + int ret = 0; + std::string action = vm["action"].as(); + std::vector vect; + std::string ccdbPath; + long tt = vm["timestamp"].as(); + if (tt == 0) { + auto now = std::chrono::system_clock::now(); + tt = std::chrono::duration_cast(now.time_since_epoch()).count(); + } + vect.push_back(tt); + if (action == "sox") { + // write to CTP/Calib/FirstRunOrbit + std::cout << "===> FirsRunOrbit" << std::endl; + vect.push_back(vm["run-number"].as()); + vect.push_back(vm["sox-orbit"].as()); + ccdbPath = "CTP/Calib/FirstRunOrbit"; + } else { + // write to CTP/Calib/OrbitReset + std::cout << "===> ResetOrbit" << std::endl; + ccdbPath = "CTP/Calib/OrbitReset"; + if (vm["testReset"].as()) { + ccdbPath += "Test"; + } + } + // + std::string ccdbAddress; + if (vm["ccdb"].as() == "prod") { + ccdbAddress = prodCCDB; + } else if (vm["ccdb"].as() == "test") { + ccdbAddress = testCCDB; + } else { + ccdbAddress = vm["ccdb"].as(); + } + std::cout << " Writing to db:" << ccdbAddress << std::endl; + if (ccdbAddress != "none") { + // auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + o2::ccdb::CcdbApi api; + api.init(ccdbAddress.c_str()); + std::map metadata; + long tmin = tt / 1000; + long tmax = tmin + 381928219; + if (action == "sox") { + int64_t runnum = vm["run-number"].as(); + metadata["runNumber"] = std::to_string(runnum); + std::cout << "Storing:" << ccdbPath << " " << metadata["runNumber"] << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; + ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + } else { + std::cout << "Storing:" << ccdbPath << " tmin:" << tmin << " tmax:" << tmax << " ts:" << tt << std::endl; + std::string filename = "orbitReset.root"; + auto classname = "std::vector"; + metadata["adjustableEOV"] = "true"; + ret = api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + o2::ccdb::CcdbObjectInfo oi(ccdbPath, classname, filename, metadata, tmin, tmax); + adjustOverriddenEOV(api, oi); + } + } + // + if (vm["output-file"].as() != "none") { + std::string file = vm["output-dir"].as() + vm["output-file"].as(); + TFile* f = TFile::Open(file.c_str(), "RECREATE"); + if (f == nullptr) { + std::cout << "Error: File" << file << " could not be open for writing !!!" << std::endl; + ret++; + } else { + std::cout << "File" << file << " being writen." << std::endl; + f->WriteObject(&vect, "ccdb_object"); + f->Close(); + } + } else { + std::cout << "No file created" << std::endl; + } + return ret; +} diff --git a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx index a94d633ddbbfa..391d1b5ccf58b 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx @@ -29,6 +29,7 @@ #include "Framework/WorkflowSpec.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" +#include "Framework/RawDeviceService.h" #include "Framework/ControlService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" @@ -39,30 +40,44 @@ #include #include #include "CommonUtils/StringUtils.h" -#include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/RunManager.h" #include #include - +#include "BookkeepingApi/BkpClient.h" using namespace o2::framework; using DetID = o2::detectors::DetID; -InjectorFunction dcs2dpl(std::string& ccdbhost) -// InjectorFunction dcs2dpl() +InjectorFunction dcs2dpl(std::string& ccdbhost, std::string& bkhost, std::string& qchost, int qcwriteperiod, std::string& ctpcfgdir) { auto runMgr = std::make_shared(); runMgr->setCCDBHost(ccdbhost); + runMgr->setBKHost(bkhost); + runMgr->setQCDBHost(qchost); + runMgr->setQCWritePeriod(qcwriteperiod); + runMgr->setCtpCfgDir(ctpcfgdir); runMgr->init(); - return [runMgr](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + // runMgr->setClient(client); + static int nprint = 0; + return [runMgr](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { // FIXME: Why isn't this function using the timeslice index? // make sure just 2 messages received - if (parts.Size() != 2) { - LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - return; - } + // if (parts.Size() != 2) { + // LOG(error) << "received " << parts.Size() << " instead of 2 expected"; + // return; + //} std::string messageHeader{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t dataSize = parts.At(1)->GetSize(); std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; - LOG(info) << "received message " << messageHeader << " of size " << dataSize; // << " Payload:" << messageData; + nprint++; + int nlimit = 60; + int nrange = 8; + if (nprint > nlimit && nprint < (nlimit + nrange + 1)) { + LOG(info) << "received message " << messageHeader << " of size " << dataSize << " # parts:" << parts.Size(); // << " Payload:" << messageData; + if (nprint == (nlimit + nrange)) { + nprint = 0; + } + } runMgr->processMessage(messageHeader, messageData); + return true; }; } @@ -71,6 +86,10 @@ void customize(std::vector& workflowOptions) { workflowOptions.push_back(ConfigParamSpec{"subscribe-to", VariantType::String, "type=sub,method=connect,address=tcp://188.184.30.57:5556,rateLogging=10,transport=zeromq", {"channel subscribe to"}}); workflowOptions.push_back(ConfigParamSpec{"ccdb-host", VariantType::String, "http://o2-ccdb.internal:8080", {"ccdb host"}}); + workflowOptions.push_back(ConfigParamSpec{"bk-host", VariantType::String, "none", {"bk host"}}); + workflowOptions.push_back(ConfigParamSpec{"qc-host", VariantType::String, "none", {"qc host"}}); + workflowOptions.push_back(ConfigParamSpec{"ctpcfg-dir", VariantType::String, "none", {"ctp.cfg file directory"}}); + workflowOptions.push_back(ConfigParamSpec{"qc-writeperiod", VariantType::Int, 30, {"Period of writing to QCDB in units of 10secs, default = 30 (5 mins)"}}); } #include "Framework/runDataProcessing.h" @@ -93,6 +112,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const std::string devName = "ctp-proxy"; auto chan = config.options().get("subscribe-to"); std::string ccdbhost = config.options().get("ccdb-host"); + std::string bkhost = config.options().get("bk-host"); + std::string qchost = config.options().get("qc-host"); + int qcwriteperiod = config.options().get("qc-writeperiod"); + std::string ctpcfgdir = config.options().get("ctpcfg-dir"); if (chan.empty()) { throw std::runtime_error("input channel is not provided"); } @@ -107,7 +130,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) std::move(ctpCountersOutputs), // this is just default, can be overriden by --ctp-config-proxy '--channel-config..' chan.c_str(), - dcs2dpl(ccdbhost)); + dcs2dpl(ccdbhost, bkhost, qchost, qcwriteperiod, ctpcfgdir)); + ctpProxy.labels.emplace_back(DataProcessorLabel{"input-proxy"}); LOG(info) << "===> Proxy done"; WorkflowSpec workflow; workflow.emplace_back(ctpProxy); diff --git a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx index a7491b489b5e1..0a4eeb64cf3af 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx @@ -28,6 +28,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ControlService.h" +#include "Framework/RawDeviceService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" #include "Framework/ConfigParamSpec.h" @@ -45,22 +46,18 @@ using DetID = o2::detectors::DetID; InjectorFunction dcs2dpl() // InjectorFunction dcs2dpl() { - return [](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { - // make sure just 2 messages received - if (parts.Size() != 2) { - LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - return; - } + return [](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto* device = services.get().device(); std::string messageHeader{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t dataSize = parts.At(1)->GetSize(); std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; - LOG(info) << "received message " << messageHeader << " of size " << dataSize; // << " Payload:" << messageData; + LOG(info) << "received message " << messageHeader << " of size " << dataSize << "#parts:" << parts.Size(); // << " Payload:" << messageData; o2::header::DataHeader hdrF("CTP_COUNTERS", o2::header::gDataOriginCTP, 0); OutputSpec outsp{hdrF.dataOrigin, hdrF.dataDescription, hdrF.subSpecification}; auto channel = channelRetriever(outsp, newTimesliceId); if (channel.empty()) { LOG(error) << "No output channel found for OutputSpec " << outsp; - return; + return false; } hdrF.tfCounter = newTimesliceId; // this also hdrF.payloadSerializationMethod = o2::header::gSerializationMethodNone; @@ -69,7 +66,7 @@ InjectorFunction dcs2dpl() hdrF.payloadSize = parts.At(0)->GetSize() + parts.At(1)->GetSize() + 1; hdrF.firstTForbit = 0; // this should be irrelevant for Counters ? Orbit is in payload - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); o2::header::Stack headerStackF{hdrF, DataProcessingHeader{newTimesliceId, 1}}; auto hdMessageF = fmqFactory->CreateMessage(headerStackF.size(), fair::mq::Alignment{64}); @@ -92,8 +89,9 @@ InjectorFunction dcs2dpl() fair::mq::Parts outParts; outParts.AddPart(std::move(hdMessageF)); outParts.AddPart(std::move(plMessageF)); - sendOnChannel(device, outParts, channel, newTimesliceId); + sendOnChannel(*device, outParts, channel, newTimesliceId); LOG(info) << "Sent CTP counters DPL message" << std::flush; + return true; }; } @@ -137,6 +135,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) // this is just default, can be overriden by --ctp-config-proxy '--channel-config..' chan.c_str(), dcs2dpl()); + ctpProxy.labels.emplace_back(DataProcessorLabel{"input-proxy"}); LOG(info) << "===> Proxy done"; WorkflowSpec workflow; workflow.emplace_back(ctpProxy); diff --git a/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx new file mode 100644 index 0000000000000..74d4a905c93e2 --- /dev/null +++ b/Detectors/CTP/workflowScalers/src/ctpCCDBManager.cxx @@ -0,0 +1,274 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ctpCCDBManager.cxx +/// \author Roman Lietava +#include "CTPWorkflowScalers/ctpCCDBManager.h" +#include "DataFormatsCTP/Configuration.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include +#include +#include "CommonUtils/StringUtils.h" +#include +using namespace o2::ctp; +std::string ctpCCDBManager::mCCDBHost = "http://o2-ccdb.internal"; +std::string ctpCCDBManager::mQCDBHost = "http://ali-qcdb.cern.ch:8083"; +// std::string ctpCCDBManager::mQCDBHost = "none"; +// +int ctpCCDBManager::saveRunScalersToCCDB(CTPRunScalers& scalers, long timeStart, long timeStop) +{ + // data base + if (mCCDBHost == "none") { + LOG(debug) << "Scalers not written to CCDB none"; + return 0; + } + // CTPActiveRun* run = mActiveRuns[i]; + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStop + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(scalers.getRunNumber()); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&(scalers), mCCDBPathCTPScalers, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP scalers saved in ccdb:" << mCCDBHost << " run:" << scalers.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "Problem writing to database ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveRunScalersToQCDB(CTPRunScalers& scalers, long timeStart, long timeStop) +{ + // data base + if (mQCDBHost == "none") { + LOG(debug) << "Scalers not written to QCDB none"; + return 0; + } + // CTPActiveRun* run = mActiveRuns[i];q + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStop + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(scalers.getRunNumber()); + api.init(mQCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&(scalers), mQCDBPathCTPScalers, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP scalers saved in qcdb:" << mQCDBHost << " run:" << scalers.getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "CTP scalers Problem writing to database qcdb ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveRunConfigToCCDB(CTPConfiguration* cfg, long timeStart) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "CTP config not written to CCDB none"; + return 0; + } + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStart + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(cfg->getRunNumber()); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(cfg, CCDBPathCTPConfig, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CTP config saved in ccdb:" << mCCDBHost << " run:" << cfg->getRunNumber() << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "CTPConfig: Problem writing to database ret:" << ret; + } + return ret; +} +int ctpCCDBManager::saveSoxOrbit(uint32_t runNumber, uint32_t soxOrbit, long timestamp) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "SOX Orbit not written to CCDB none"; + return 0; + } + std::vector vect; + if (timestamp == 0) { + auto now = std::chrono::system_clock::now(); + timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + } + vect.push_back(timestamp); + vect.push_back((uint64_t)runNumber); + vect.push_back((uint64_t)soxOrbit); + long tmin = timestamp / 1000; + long tmax = tmin + 381928219; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(runNumber); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + + // store abitrary user object in strongly typed manner + int ret = api.storeAsTFileAny(&vect, mCCDBPathSoxOrbit, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "SOX orbit saved in ccdb:" << mCCDBHost << " run:" << runNumber << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "SOX orbit Problem writing to database ret:" << ret; + } + return 0; +} +int ctpCCDBManager::saveOrbitReset(long timeStamp) +{ + // data base + if (mCCDBHost == "none") { + LOG(info) << "Orbit Reset not written to CCDB none"; + return 0; + } + std::vector vect; + if (timeStamp == 0) { + auto now = std::chrono::system_clock::now(); + timeStamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + LOG(warn) << "Received timestamp = 0 , using current time:" << timeStamp; + } + vect.push_back(timeStamp); + long tmin = timeStamp / 1000; + long tmax = tmin + 381928219; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // int ret = api.storeAsTFileAny(&vect, mCCDBPathOrbitReset, metadata, tmin, tmax); + std::cout << "Storing:" << mCCDBPathOrbitReset << " tmin:" << tmin << " tmax:" << tmax << " ts:" << timeStamp << std::endl; + std::string filename = "orbitReset.root"; + auto classname = "std::vector"; + metadata["adjustableEOV"] = "true"; + int ret = api.storeAsTFileAny(&(vect), mCCDBPathOrbitReset, metadata, tmin, tmax); + o2::ccdb::CcdbObjectInfo oi(mCCDBPathOrbitReset, classname, filename, metadata, tmin, tmax); + adjustOverriddenEOV(api, oi); + if (ret == 0) { + LOG(info) << "Orbit reset saved in ccdb:" << mCCDBHost << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(fatal) << "Orbit reset Problem writing to database ret:" << ret; + } + return 0; +} +int ctpCCDBManager::saveCtpCfg(uint32_t runNumber, long timeStart) +{ + if (mCCDBHost == "none") { + LOG(info) << "CtpCfg not written to CCDB none"; + return 0; + } + CtpCfg ctpcfg; + int ret = ctpcfg.readAndSave(mCtpCfgDir); + if (ret == 0) { + using namespace std::chrono_literals; + std::chrono::seconds days3 = 259200s; + std::chrono::seconds min10 = 600s; + long time3days = std::chrono::duration_cast(days3).count(); + long time10min = std::chrono::duration_cast(min10).count(); + long tmin = timeStart - time10min; + long tmax = timeStart + time3days; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + metadata["runNumber"] = std::to_string(runNumber); + api.init(mCCDBHost.c_str()); // or http://localhost:8080 for a local installation + // store abitrary user object in strongly typed manner + ret = api.storeAsTFileAny(&ctpcfg, mCCDBPathCtpCfg, metadata, tmin, tmax); + if (ret == 0) { + LOG(info) << "CtpCfg saved in ccdb:" << mCCDBHost << " tmin:" << tmin << " tmax:" << tmax; + } else { + LOG(error) << "CtpCfg Problem writing to database ret:" << ret; + } + } + return ret; +} +CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run, bool& ok) +{ + + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } + auto ctpconfigdb = mgr.getSpecific(CCDBPathCTPConfig, timestamp, metadata); + if (ctpconfigdb == nullptr) { + LOG(info) << "CTP config not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpconfigdb->printStream(std::cout); + LOG(info) << "CTP config found. Run:" << run; + ok = 1; + } + return *ctpconfigdb; +} +CTPConfiguration ctpCCDBManager::getConfigFromCCDB(long timestamp, std::string run) +{ + bool ok; + auto ctpconfig = getConfigFromCCDB(timestamp, run, ok); + if (ok == 0) { + LOG(error) << "CTP config not in CCDB"; + return CTPConfiguration(); + } + return ctpconfig; +} +CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run, bool& ok) +{ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + auto ctpscalers = mgr.getSpecific(mCCDBPathCTPScalers, timestamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpscalers->printStream(std::cout); + ok = 1; + } + return *ctpscalers; +} +CTPRunScalers ctpCCDBManager::getScalersFromCCDB(long timestamp, std::string run, std::string path, bool& ok) +{ + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL(mCCDBHost); + std::map metadata; // can be empty + metadata["runNumber"] = run; + if (timestamp == 0) { + // Timestamp + auto soreor = mgr.getRunDuration(std::stoi(run)); + timestamp = (soreor.second - soreor.first) / 2 + soreor.first; + } + auto ctpscalers = mgr.getSpecific(path, timestamp, metadata); + if (ctpscalers == nullptr) { + LOG(info) << "CTPRunScalers not in database, timestamp:" << timestamp; + ok = 0; + } else { + // ctpscalers->printStream(std::cout); + ok = 1; + } + return *ctpscalers; +} \ No newline at end of file diff --git a/Detectors/Calibration/README.md b/Detectors/Calibration/README.md index 88bd201dab975..be519be405c07 100644 --- a/Detectors/Calibration/README.md +++ b/Detectors/Calibration/README.md @@ -42,6 +42,13 @@ In order to prepare only one CCDB object at the end of the run you can use `setU See e.g. LHCClockCalibrator.h/cxx in AliceO2/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h and AliceO2/Detectors/TOF/calibration/srcLHCClockCalibrator.cxx +Sometimes it might be useful to define the 1st slot of the run shorter than the nominal slot length, e.g. to not rely to long on the previous run or default calibration. In this case one can impose to the +calibration class an offset using method `setStartOffsetFrac(float offset)`, where `offset` is a fractional of the nominal slot length to be subtracted from the nominal boundaries of all slots (except the +very 1st one, whose startTF is set to 0). The fractional offset should be in `[0:0.95)` range, any value outside this range will be overridden to the nearest boundary. +This feature is supported only for the finite slot-length calibrations. E.g. if the nominal slot length is 10 minutes `(==~210000 TFs)`, setting `setStartOffsetFrac(float 0.7)` will lead to 1st slot +finalized after the first 3 minutes, while the rest of the slots will be defined with nominal 10 minutes coverage. If statistics of this 1st short slot is insufficient, it will be merged as usual +with the next slot (note this if this happens, in the example above the 1st calibration will be available in 13 minutes...). + ## TimeSlot The TimeSlot is a templated class which takes as input type the Container that will hold the calibration data needed to produce the calibration objects (histograms, vectors, array...). Each calibration device could implement its own Container, according to its needs. @@ -154,7 +161,11 @@ o2-calibration-ccdb-populator-workflow --sspec-min 0 --sspec-max 1 -b then the `ObjA` will be uploaded only to the default server (`http://alice-ccdb.cern.ch`), `ObjB` will be uploaded to both default and `local` server and `ObjC` will be uploaded to the `local` server only. -By default the ccdb-populator-workflow will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. +By default the `ccdb-populator-workflow` will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. + +By default the `ccdb-populator-workflow` uploads objects as it gets them. In case there is a danger that objects of the same URL will arrive in the order not sorted in SOV +(which may lead to screaning of the object with later SOV by other object if earlier ROF) one can use an option `--ordering-latency ` of the `ccdb-populator-workflow`. +Then every incoming object will be buffered and uploaded only if no object with the same CCDB path and earlier start of validity was received in preceding N milliseconds. All remaining cached objects are uploaded at EOR (or stop() method call). we are in 22c, d, e, or f, so IR is < 100 kHz, writing 0.f"); - LOGP(info, "In addition, the duration for these runs is O2end - O2start: if the run was short, this might overestimate the duration"); - // In these runs, sometimes the CCDB does not contain correct scalers, so we use 0 as a placeholder - writeIRtoFile(ir); - writeDurationToFile(run_O2duration); - return 0; + // getting the detector list + LOGP(info, "Getting detector participating in the run"); + std::map metadataRun; + metadataRun["runNumber"] = std::to_string(run); + o2::parameters::GRPECSObject* ecsObj = ccdb_inst.getSpecific("GLO/Config/GRPECS", tsSOR, metadataRun); + std::string dets = ""; + for (int i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) { + if (ecsObj->isDetReadOut(i)) { + dets = dets + o2::detectors::DetID::getName(i) + " "; + } } + LOGP(info, "run {}: detectors in readout = {}", run, dets); + writeDetListToFile(dets); + + LOGP(info, "Checking IR and duration"); // Extract CTP info ccdb_inst.setFatalWhenNull(false); diff --git a/Detectors/Calibration/workflow/CCDBPopulatorSpec.h b/Detectors/Calibration/workflow/CCDBPopulatorSpec.h index 40d426e8a3369..990d70e17e050 100644 --- a/Detectors/Calibration/workflow/CCDBPopulatorSpec.h +++ b/Detectors/Calibration/workflow/CCDBPopulatorSpec.h @@ -31,6 +31,9 @@ #include "CommonUtils/NameConf.h" #include #include +#include +#include +#include namespace o2 { @@ -39,132 +42,215 @@ namespace calibration class CCDBPopulator : public o2::framework::Task { + public: using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; using CcdbApi = o2::ccdb::CcdbApi; - public: - void init(o2::framework::InitContext& ic) final - { - mCCDBpath = ic.options().get("ccdb-path"); - mSSpecMin = ic.options().get("sspec-min"); - mSSpecMax = ic.options().get("sspec-max"); - mFatalOnFailure = ic.options().get("fatal-on-failure"); - mValidateUpload = ic.options().get("validate-upload"); - mThrottlingDelayMS = ic.options().get("throttling-delay"); - mAPI.init(mCCDBpath); - } + using BLOB = std::vector; + using TBLOB = std::pair; // pair of creation time and object to upload + using OBJCACHE = std::map; + + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + void stop() final; + + void checkCache(long delay); + void doUpload(const CcdbObjectInfo& wrp, const gsl::span& pld, bool cached = false); + void logAsNeeded(long nowMS, const std::string& path, std::string& msg); - void run(o2::framework::ProcessingContext& pc) final - { - int nSlots = pc.inputs().getNofParts(0); - if (nSlots != pc.inputs().getNofParts(1)) { - LOGP(alarm, "Number of slots={} in part0 is different from that ({}) in part1", nSlots, pc.inputs().getNofParts(1)); - return; - } else if (nSlots == 0) { - LOG(alarm) << "0 slots received"; - return; + private: + CcdbApi mAPI; + long mThrottlingDelayMS = 0; // LOG(important) at most once per this period for given path + int mOrderingLatencyMS = -1; // if >0, bufferize and upload if no object with smaller SOV was received in this time interval in ms + bool mFatalOnFailure = true; // produce fatal on failed upload + bool mValidateUpload = false; // validate upload by querying its headers + bool mEnded = false; + std::unordered_map> mThrottling; + std::unordered_map mOrdCache; + std::int64_t mSSpecMin = -1; // min subspec to accept + std::int64_t mSSpecMax = -1; // max subspec to accept + std::string mCCDBpath = "http://ccdb-test.cern.ch:8080"; // CCDB path + int mRunNoFromDH = 0; + std::string mRunNoStr = {}; +}; + +void CCDBPopulator::init(o2::framework::InitContext& ic) +{ + mCCDBpath = ic.options().get("ccdb-path"); + mSSpecMin = ic.options().get("sspec-min"); + mSSpecMax = ic.options().get("sspec-max"); + mFatalOnFailure = ic.options().get("fatal-on-failure"); + mValidateUpload = ic.options().get("validate-upload"); + mThrottlingDelayMS = ic.options().get("throttling-delay"); + mOrderingLatencyMS = ic.options().get("ordering-latency"); + mAPI.init(mCCDBpath); +} + +void CCDBPopulator::run(o2::framework::ProcessingContext& pc) +{ + int nSlots = pc.inputs().getNofParts(0); + if (nSlots != pc.inputs().getNofParts(1)) { + LOGP(alarm, "Number of slots={} in part0 is different from that ({}) in part1", nSlots, pc.inputs().getNofParts(1)); + return; + } else if (nSlots == 0) { + LOG(alarm) << "0 slots received"; + return; + } + mRunNoFromDH = pc.services().get().runNumber; + if (mRunNoFromDH > 0) { + mRunNoStr = std::to_string(mRunNoFromDH); + } + auto nowMS = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + for (int isl = 0; isl < nSlots; isl++) { + auto refWrp = pc.inputs().get("clbWrapper", isl); + auto refPld = pc.inputs().get("clbPayload", isl); + if (!o2::framework::DataRefUtils::isValid(refWrp)) { + LOGP(alarm, "Wrapper is not valid for slot {}", isl); + continue; } - auto runNoFromDH = pc.services().get().runNumber; - std::string runNoStr; - if (runNoFromDH > 0) { - runNoStr = std::to_string(runNoFromDH); + if (!o2::framework::DataRefUtils::isValid(refPld)) { + LOGP(alarm, "Payload is not valid for slot {}", isl); + continue; } - auto nowMS = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - std::map metadata; - for (int isl = 0; isl < nSlots; isl++) { - auto refWrp = pc.inputs().get("clbWrapper", isl); - auto refPld = pc.inputs().get("clbPayload", isl); - if (!o2::framework::DataRefUtils::isValid(refWrp)) { - LOGP(alarm, "Wrapper is not valid for slot {}", isl); - continue; - } - if (!o2::framework::DataRefUtils::isValid(refPld)) { - LOGP(alarm, "Payload is not valid for slot {}", isl); + if (mSSpecMin >= 0 && mSSpecMin <= mSSpecMax) { // there is a selection + auto ss = std::int64_t(o2::framework::DataRefUtils::getHeader(refWrp)->subSpecification); + if (ss < mSSpecMin || ss > mSSpecMax) { continue; } - if (mSSpecMin >= 0 && mSSpecMin <= mSSpecMax) { // there is a selection - auto ss = std::int64_t(o2::framework::DataRefUtils::getHeader(refWrp)->subSpecification); - if (ss < mSSpecMin || ss > mSSpecMax) { - continue; - } - } - const auto wrp = pc.inputs().get(refWrp); - const auto pld = pc.inputs().get>(refPld); // this is actually an image of TMemFile - if (!wrp) { - LOGP(alarm, "No CcdbObjectInfo info for {} at slot {}", - o2::framework::DataRefUtils::getHeader(refWrp)->dataDescription.as(), isl); - continue; - } - const auto* md = &wrp->getMetaData(); - if (runNoFromDH > 0 && md->find(o2::base::NameConf::CCDBRunTag.data()) == md->end()) { // if valid run number is provided and it is not filled in the metadata, add it to the clone - metadata = *md; // clone since the md from the message is const - metadata[o2::base::NameConf::CCDBRunTag.data()] = runNoStr; - md = &metadata; - } - std::string msg = fmt::format("Storing in ccdb {}/{} of size {} valid for {} : {}", wrp->getPath(), wrp->getFileName(), pld.size(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - auto& lastLog = mThrottling[wrp->getPath()]; - if (lastLog.first + mThrottlingDelayMS < nowMS) { - if (lastLog.second) { - msg += fmt::format(" ({} uploads were logged as INFO)", lastLog.second); - lastLog.second = 0; - } - lastLog.first = nowMS; - LOG(important) << msg; + } + const auto wrp = pc.inputs().get(refWrp); + const auto pld = pc.inputs().get>(refPld); // this is actually an image of TMemFile + if (!wrp) { + LOGP(alarm, "No CcdbObjectInfo info for {} at slot {}", + o2::framework::DataRefUtils::getHeader(refWrp)->dataDescription.as(), isl); + continue; + } + if (mOrderingLatencyMS <= 0) { // ordering is not requested + doUpload(*wrp, pld); + } else { + auto& pathCache = mOrdCache[wrp->getPath()]; + auto stt = pathCache.emplace(*wrp, std::make_pair(nowMS, std::vector(pld.size()))); + if (stt.second) { // insertion success + stt.first->second.second.assign(pld.begin(), pld.end()); + std::string msg = fmt::format("Bufferizing for ordering ccdb object {}/{} of size {} valid for {} : {}", + wrp->getPath(), wrp->getFileName(), pld.size(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); + logAsNeeded(nowMS, wrp->getPath(), msg); } else { - lastLog.second++; - LOG(info) << msg; + bool v = stt.first != pathCache.end(); + LOGP(error, "failed to bufferize a {} object with SOV={}/EOV={} received at {}, conflicting with previously bufferized one SOV={}/EOV={} received at {}", + wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp(), nowMS, + v ? std::to_string(stt.first->first.getStartValidityTimestamp()) : std::string{"N/A"}, + v ? std::to_string(stt.first->first.getEndValidityTimestamp()) : std::string{"N/A"}, + v ? std::to_string(stt.first->second.first) : std::string{"N/A"}); } + } + } + if (mOrderingLatencyMS > 0) { + checkCache(mOrderingLatencyMS); + } +} - auto uploadTS = o2::ccdb::getCurrentTimestamp(); - - int res = mAPI.storeAsBinaryFile(&pld[0], pld.size(), wrp->getFileName(), wrp->getObjectType(), wrp->getPath(), - *md, wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - if (res) { - if (mFatalOnFailure) { - LOGP(fatal, "failed on uploading to {} / {} for [{}:{}]", mAPI.getURL(), wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - } else { - LOGP(error, "failed on uploading to {} / {} for [{}:{}]", mAPI.getURL(), wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - } - } - // do we need to override previous object? - if (wrp->isAdjustableEOV() && !mAPI.isSnapshotMode()) { - o2::ccdb::adjustOverriddenEOV(mAPI, *wrp.get()); +void CCDBPopulator::checkCache(long delay) +{ + // check if some entries in cache are ripe enough to upload + auto nowMS = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + for (auto& pathCache : mOrdCache) { // loop over paths + if (delay < 0 && pathCache.second.size()) { + LOGP(important, "Uploading {} cached objects for path {}", pathCache.second.size(), pathCache.first); + } + for (auto it = pathCache.second.begin(); it != pathCache.second.end();) { // loop over objects of the path + if (nowMS - it->second.first > delay) { + doUpload(it->first, {it->second.second.data(), it->second.second.size()}, true); + it = pathCache.second.erase(it); + } else { + break; } - // if requested, make sure that the new object can be queried - if (mValidateUpload || wrp->getValidateUpload()) { - constexpr long MAXDESYNC = 3; - auto headers = mAPI.retrieveHeaders(wrp->getPath(), {}, wrp->getStartValidityTimestamp() + (wrp->getEndValidityTimestamp() - wrp->getStartValidityTimestamp()) / 2); - if (headers.empty() || - std::atol(headers["Created"].c_str()) < uploadTS - MAXDESYNC || - std::atol(headers["Valid-From"].c_str()) != wrp->getStartValidityTimestamp() || - std::atol(headers["Valid-Until"].c_str()) != wrp->getEndValidityTimestamp()) { - if (mFatalOnFailure) { - LOGP(fatal, "Failed to validate upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - } else { - LOGP(error, "Failed to validate upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - } - } else { - LOGP(important, "Validated upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp->getPath(), wrp->getStartValidityTimestamp(), wrp->getEndValidityTimestamp()); - } + } + } +} + +void CCDBPopulator::doUpload(const CcdbObjectInfo& wrp, const gsl::span& pld, bool cached) +{ + std::string msg = fmt::format("Storing in ccdb {}{}/{} of size {} valid for {} : {}", cached ? "cached " : "", wrp.getPath(), wrp.getFileName(), pld.size(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); + auto uploadTS = o2::ccdb::getCurrentTimestamp(); + logAsNeeded(uploadTS, wrp.getPath(), msg); + std::map metadata; + const auto* md = &wrp.getMetaData(); + if (mRunNoFromDH > 0 && md->find(o2::base::NameConf::CCDBRunTag.data()) == md->end()) { // if valid run number is provided and it is not filled in the metadata, add it to the clone + metadata = *md; // clone since the md from the message is const + metadata[o2::base::NameConf::CCDBRunTag.data()] = mRunNoStr; + md = &metadata; + } + int res = mAPI.storeAsBinaryFile(&pld[0], pld.size(), wrp.getFileName(), wrp.getObjectType(), wrp.getPath(), *md, wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); + if (res) { + if (mFatalOnFailure) { + LOGP(fatal, "failed on uploading to {} / {} for [{}:{}]", mAPI.getURL(), wrp.getPath(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); + } else { + LOGP(error, "failed on uploading to {} / {} for [{}:{}]", mAPI.getURL(), wrp.getPath(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); + } + } + // if requested, make sure that the new object can be queried + if (mValidateUpload || wrp.getValidateUpload()) { + constexpr long MAXDESYNC = 3; + auto headers = mAPI.retrieveHeaders(wrp.getPath(), {}, wrp.getStartValidityTimestamp() + (wrp.getEndValidityTimestamp() - wrp.getStartValidityTimestamp()) / 2); + if (headers.empty() || + std::atol(headers["Created"].c_str()) < uploadTS - MAXDESYNC || + std::atol(headers["Valid-From"].c_str()) != wrp.getStartValidityTimestamp() || + std::atol(headers["Valid-Until"].c_str()) != wrp.getEndValidityTimestamp()) { + if (mFatalOnFailure) { + LOGP(fatal, "Failed to validate upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp.getPath(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); + } else { + LOGP(error, "Failed to validate upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp.getPath(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); } + } else { + LOGP(important, "Validated upload to {} / {} for [{}:{}]", mAPI.getURL(), wrp.getPath(), wrp.getStartValidityTimestamp(), wrp.getEndValidityTimestamp()); } } + if (wrp.isAdjustableEOV() && !mAPI.isSnapshotMode()) { + o2::ccdb::adjustOverriddenEOV(mAPI, wrp); + } +} - void endOfStream(o2::framework::EndOfStreamContext& ec) final - { - LOG(info) << "EndOfStream received"; +void CCDBPopulator::logAsNeeded(long nowMS, const std::string& path, std::string& msg) +{ + auto& lastLog = mThrottling[path]; + if (lastLog.first + mThrottlingDelayMS < nowMS) { + if (lastLog.second) { + msg += fmt::format(" ({} uploads were logged as INFO)", lastLog.second); + lastLog.second = 0; + } + lastLog.first = nowMS; + LOG(important) << msg; + } else { + lastLog.second++; + LOG(info) << msg; } +} - private: - CcdbApi mAPI; - long mThrottlingDelayMS = 0; // LOG(important) at most once per this period for given path - bool mFatalOnFailure = true; // produce fatal on failed upload - bool mValidateUpload = false; // validate upload by querying its headers - std::unordered_map> mThrottling; - std::int64_t mSSpecMin = -1; // min subspec to accept - std::int64_t mSSpecMax = -1; // max subspec to accept - std::string mCCDBpath = "http://ccdb-test.cern.ch:8080"; // CCDB path -}; +void CCDBPopulator::endOfStream(o2::framework::EndOfStreamContext& ec) +{ + if (mEnded) { + return; + } + mEnded = true; + LOG(info) << "EndOfStream received"; + if (mOrderingLatencyMS > 0) { + checkCache(-mOrderingLatencyMS); // force + } +} + +void CCDBPopulator::stop() +{ + if (mEnded) { + return; + } + mEnded = true; + LOG(info) << "Forced stop"; + if (mOrderingLatencyMS > 0) { + checkCache(-mOrderingLatencyMS); // force + } +} } // namespace calibration @@ -186,6 +272,7 @@ DataProcessorSpec getCCDBPopulatorDeviceSpec(const std::string& defCCDB, const s {"ccdb-path", VariantType::String, defCCDB, {"Path to CCDB"}}, {"sspec-min", VariantType::Int64, -1L, {"min subspec to accept"}}, {"sspec-max", VariantType::Int64, -1L, {"max subspec to accept"}}, + {"ordering-latency", VariantType::Int, -1, {"if enabled (positive) bufferize object and upload it if no object with smaller SOV received in given waiting time (ms)"}}, {"throttling-delay", VariantType::Int64, 300000L, {"produce important type log at most once per this period in ms for each CCDB path"}}, {"validate-upload", VariantType::Bool, false, {"valider upload by querying its headers"}}, {"fatal-on-failure", VariantType::Bool, false, {"do not produce fatal on failed upload"}}}}; diff --git a/Detectors/Calibration/workflow/ccdb-populator-workflow.cxx b/Detectors/Calibration/workflow/ccdb-populator-workflow.cxx index 5432712dfdac3..d03920b7a657f 100644 --- a/Detectors/Calibration/workflow/ccdb-populator-workflow.cxx +++ b/Detectors/Calibration/workflow/ccdb-populator-workflow.cxx @@ -33,7 +33,8 @@ void customize(std::vector& policies) // we customize the pipeline processors to consume data as it comes using CompletionPolicy = o2::framework::CompletionPolicy; using CompletionPolicyHelpers = o2::framework::CompletionPolicyHelpers; - policies.push_back(CompletionPolicyHelpers::defineByName("ccdb-populator.*", CompletionPolicy::CompletionOp::Consume)); + auto& pol = policies.emplace_back(CompletionPolicyHelpers::defineByName("ccdb-populator.*", CompletionPolicy::CompletionOp::Consume)); + pol.order = CompletionPolicy::CompletionOrder::Slot; } // ------------------------------------------------------------------ diff --git a/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h b/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h index 3ee4c02bab3f2..9cfbe79484837 100644 --- a/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h +++ b/Detectors/Calibration/workflow/include/DetectorsCalibrationWorkflow/MeanVertexCalibratorSpec.h @@ -29,7 +29,7 @@ namespace calibration class MeanVertexCalibDevice : public Task { public: - MeanVertexCalibDevice(std::shared_ptr req) : mCCDBRequest(req) {} + MeanVertexCalibDevice(std::shared_ptr req, uint32_t dcsMVsubspec) : mCCDBRequest(req), mDCSSubSpec(dcsMVsubspec) {} void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; @@ -37,7 +37,9 @@ class MeanVertexCalibDevice : public Task private: void sendOutput(DataAllocator& output); - + int mFillNumber = 0; + int mRunNumber = 0; + uint32_t mDCSSubSpec = 0; std::unique_ptr mCalibrator; std::shared_ptr mCCDBRequest; }; @@ -46,7 +48,7 @@ class MeanVertexCalibDevice : public Task namespace framework { -DataProcessorSpec getMeanVertexCalibDeviceSpec(); +DataProcessorSpec getMeanVertexCalibDeviceSpec(uint32_t dcsMVsubspec); } // namespace framework } // namespace o2 diff --git a/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx b/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx index 70737f3b16726..1a64adcd9bf3c 100644 --- a/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx +++ b/Detectors/Calibration/workflow/src/MeanVertexCalibratorSpec.cxx @@ -45,13 +45,19 @@ void MeanVertexCalibDevice::init(InitContext& ic) void MeanVertexCalibDevice::run(o2::framework::ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // new run is starting + mRunNumber = (tinfo.runNumber != -1 && tinfo.runNumber > 0) ? tinfo.runNumber : o2::base::GRPGeomHelper::instance().getGRPECS()->getRun(); + mFillNumber = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getFillNumber(); + } + auto data = pc.inputs().get>("input"); o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); LOG(debug) << "Processing TF " << mCalibrator->getCurrentTFInfo().tfCounter << " with " << data.size() << " vertices"; mCalibrator->process(data); sendOutput(pc.outputs()); const auto& infoVec = mCalibrator->getMeanVertexObjectInfoVector(); - LOG(info) << "Processed TF " << mCalibrator->getCurrentTFInfo().tfCounter << " with " << data.size() << " vertices, for which we created " << infoVec.size() << " objects for TF " << mCalibrator->getCurrentTFInfo().tfCounter; + LOG(detail) << "Processed TF " << mCalibrator->getCurrentTFInfo().tfCounter << " with " << data.size() << " vertices, for which we created " << infoVec.size() << " objects for TF " << mCalibrator->getCurrentTFInfo().tfCounter; } //_________________________________________________________________ @@ -82,13 +88,30 @@ void MeanVertexCalibDevice::sendOutput(DataAllocator& output) auto& infoVec = mCalibrator->getMeanVertexObjectInfoVector(); // use non-const version as we update it assert(payloadVec.size() == infoVec.size()); + if (mDCSSubSpec && mDCSSubSpec < payloadVec.size()) { + LOGP(alarm, "Minimum subspec {} of messages for DCS CCDB is below the maximum subspec {} for production CCDB, increase the former", mDCSSubSpec, payloadVec.size()); + } + static std::vector dcsMVObj; for (uint32_t i = 0; i < payloadVec.size(); i++) { - auto& w = infoVec[i]; - auto image = o2::ccdb::CcdbApi::createObjectImage(&payloadVec[i], &w); + auto w = infoVec[i]; + auto& mv = payloadVec[i]; + auto image = o2::ccdb::CcdbApi::createObjectImage(&mv, &w); LOG(info) << (MeanVertexParams::Instance().skipObjectSending ? "Skip " : "") << "sending object " << w.getPath() << "/" << w.getFileName() << " of size " << image->size() << " bytes, valid for " << w.getStartValidityTimestamp() << " : " << w.getEndValidityTimestamp(); if (!MeanVertexParams::Instance().skipObjectSending) { + if (mDCSSubSpec) { // create message for DCS CCDB + auto ts = (w.getStartValidityTimestamp() + w.getEndValidityTimestamp()) / 2; + o2::ccdb::CcdbObjectInfo dcsw("GLO/Calib/MeanVertexCSV", "csv", fmt::format("meanvertex_{}.csv", ts), {}, w.getStartValidityTimestamp(), w.getEndValidityTimestamp()); + + std::string csvMeanVertex = fmt::format("timestamp={},fillNumber={},runNumber={},x={:+.4e},y={:+.4e},z={:+.4e},sigmax={:+.4e},sigmay={:+.4e},sigmaz={:+.4e}", + ts, mFillNumber, mRunNumber, mv.getX(), mv.getY(), mv.getZ(), mv.getSigmaX(), mv.getSigmaY(), mv.getSigmaZ()); + dcsMVObj.clear(); + std::copy(csvMeanVertex.begin(), csvMeanVertex.end(), std::back_inserter(dcsMVObj)); + output.snapshot(Output{clbUtils::gDataOriginCDBPayload, "MEANVERTEX_DCS", mDCSSubSpec + i}, dcsMVObj); + output.snapshot(Output{clbUtils::gDataOriginCDBWrapper, "MEANVERTEX_DCS", mDCSSubSpec + i}, dcsw); + } + w.setEndValidityTimestamp(w.getEndValidityTimestamp() + o2::ccdb::CcdbObjectInfo::MONTH); output.snapshot(Output{clbUtils::gDataOriginCDBPayload, "MEANVERTEX", i}, *image.get()); // vector output.snapshot(Output{clbUtils::gDataOriginCDBWrapper, "MEANVERTEX", i}, w); // root-serialized } @@ -102,7 +125,7 @@ void MeanVertexCalibDevice::sendOutput(DataAllocator& output) namespace framework { -DataProcessorSpec getMeanVertexCalibDeviceSpec() +DataProcessorSpec getMeanVertexCalibDeviceSpec(uint32_t dcsMVsubspec) { using device = o2::calibration::MeanVertexCalibDevice; @@ -111,7 +134,7 @@ DataProcessorSpec getMeanVertexCalibDeviceSpec() inputs.emplace_back("input", "GLO", "PVTX"); auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true - false, // GRPLHCIF + true, // GRPLHCIF false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry @@ -120,12 +143,16 @@ DataProcessorSpec getMeanVertexCalibDeviceSpec() std::vector outputs; outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "MEANVERTEX"}, Lifetime::Sporadic); outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "MEANVERTEX"}, Lifetime::Sporadic); + if (dcsMVsubspec) { + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "MEANVERTEX_DCS"}, Lifetime::Sporadic); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "MEANVERTEX_DCS"}, Lifetime::Sporadic); + } return DataProcessorSpec{ "mean-vertex-calibration", inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, dcsMVsubspec)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}}}; } diff --git a/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx b/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx index 337690ec5104d..a893f99a60552 100644 --- a/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx +++ b/Detectors/Calibration/workflow/src/mean-vertex-calibration-workflow.cxx @@ -20,6 +20,7 @@ void customize(std::vector& workflowOptions) { // option allowing to set parameters std::vector options{ + {"meanvertex-dcs-subspec", VariantType::Int, 0x7fff, {"If >0, send text version of MeanVertex to this subspec"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -32,6 +33,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - specs.emplace_back(getMeanVertexCalibDeviceSpec()); + specs.emplace_back(getMeanVertexCalibDeviceSpec(configcontext.options().get("meanvertex-dcs-subspec"))); return specs; } diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 3929dc52943e7..9aaf6e517336d 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -39,7 +39,7 @@ namespace dcs * @see ADAPRO::ADAPOS::DataPointIdentifier * @see ADAPRO::ADAPOS::DataPointValue */ -struct alignas(128) DataPointCompositeObject final { +struct DataPointCompositeObject final { /** * The DataPointIdentifier object, which occupies the first 64 bytes of * the DataPointCompositeObject. This object contains the immutable diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index dc4abb3269d9d..79b7d7cf886c7 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -39,7 +39,7 @@ namespace dcs * DataPointIdentifier object is responsible for storing the alias and type * information of a data point. */ -class alignas(64) DataPointIdentifier final +class DataPointIdentifier final { const uint64_t pt1; const uint64_t pt2; diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointValue.h b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h index 021e8bdf0beba..4c8686d16d6e4 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointValue.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h @@ -45,7 +45,7 @@ namespace dcs * constructors and setters of this struct, that take an array containing * binary data as parameter, don't check the length of the given array. */ -struct alignas(64) DataPointValue final { +struct DataPointValue final { /** *

This flag is used for signaling that a DPVAL stream connection is * working, but there is no data to be sent.

diff --git a/Detectors/DCS/include/DetectorsDCS/DeliveryType.h b/Detectors/DCS/include/DetectorsDCS/DeliveryType.h index 236d9eb084759..f0ee5573a9421 100644 --- a/Detectors/DCS/include/DetectorsDCS/DeliveryType.h +++ b/Detectors/DCS/include/DetectorsDCS/DeliveryType.h @@ -20,20 +20,12 @@ #define O2_DCS_DELIVERY_TYPE #include -#include +#include #include #include "DetectorsDCS/GenericFunctions.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { -/** - * This regular expression matches with strings representing payload types. - */ -static const std::regex REGEX_PT( - "^(Raw|DPVAL)/(Int|Uint|Float|Double|Bool|Char|String|Time|Binary)$"); - /** *

DeliveryType is a piece of meta-information used for deducing types of * DPVAL payloads and DIM service description strings used with services @@ -406,8 +398,8 @@ inline size_t dim_buffer_size(const DeliveryType type) throw std::domain_error("Illegal DeliveryType."); } } -} // namespace dcs +} // namespace o2::dcs + -} // namespace o2 #endif /* O2_DCS_DELIVERY_TYPE */ diff --git a/Detectors/DCS/src/AliasExpander.cxx b/Detectors/DCS/src/AliasExpander.cxx index b197d2fb78d67..41aaec8351f72 100644 --- a/Detectors/DCS/src/AliasExpander.cxx +++ b/Detectors/DCS/src/AliasExpander.cxx @@ -82,7 +82,7 @@ std::vector extractRange(std::string range) std::vector result; for (auto i = a; i <= b; i++) { - auto substituted = fmt::format(intFormat, i); + auto substituted = fmt::format(fmt::runtime(intFormat), i); result.push_back(substituted); } return result; @@ -124,7 +124,7 @@ std::vector expandAlias(const std::string& pattern) std::vector result; for (auto r : range) { - auto substituted = fmt::format(newPattern, r); + auto substituted = fmt::format(fmt::runtime(newPattern), r); result.emplace_back(substituted); } diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx index eca5440519fcc..0bfb5bcd7d387 100644 --- a/Detectors/DCS/src/DataPointCreator.cxx +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -37,10 +37,9 @@ DataPointCompositeObject createDataPointCompositeObject(const std::string& alias template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, float val, uint32_t seconds, uint16_t msec, uint16_t flags) { - float tmp[2]; - tmp[0] = val; - tmp[1] = 0; - return createDPCOM(alias, reinterpret_cast(&tmp[0]), seconds, msec, flags, DeliveryType::DPVAL_FLOAT); + uint64_t tmp = 0; + memcpy(&tmp, &val, sizeof(val)); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_FLOAT); } template <> @@ -54,29 +53,38 @@ template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, long long val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + uint64_t tmp{static_cast(val)}; + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) { - return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::DPVAL_CHAR); + uint64_t tmp = 0; + memcpy(&tmp, &val, 1); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_CHAR); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_BOOL); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_BOOL); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) { constexpr int N{56}; - char str[N]; - strncpy(str, val.c_str(), N); - return createDPCOM(alias, reinterpret_cast(&str[0]), seconds, msec, flags, DeliveryType::DPVAL_STRING); + uint64_t tmp[N / sizeof(uint64_t)]; + strncpy((char*)tmp, val.c_str(), N); + return createDPCOM(alias, tmp, seconds, msec, flags, DeliveryType::DPVAL_STRING); } } // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointGenerator.cxx b/Detectors/DCS/src/DataPointGenerator.cxx index 3e7ead2146687..15f79a709bba1 100644 --- a/Detectors/DCS/src/DataPointGenerator.cxx +++ b/Detectors/DCS/src/DataPointGenerator.cxx @@ -63,7 +63,7 @@ std::pair getDate(const std::string& refDate) namespace o2::dcs { -//std::enable_if_t::value, bool> = true> +// std::enable_if_t::value, bool> = true> template std::vector @@ -72,16 +72,15 @@ std::vector { std::vector dpcoms; static_assert(std::is_arithmetic::value, "T must be an arithmetic type"); - typedef typename std::conditional::value, - std::uniform_int_distribution, - std::uniform_real_distribution>::type distType; - + using distType = std::conditional_t::value, + std::uniform_int_distribution, + std::uniform_real_distribution>; 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); + T value = dist(mt); dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); } return dpcoms; @@ -106,11 +105,13 @@ template std::vector generateRandomDataPoints template std::vector generateRandomDataPoints(const std::vector& aliases, int32_t minValue, int32_t maxValue, std::string); +template std::vector generateRandomDataPoints(const std::vector& aliases, long long minValue, long long maxValue, std::string); + template std::vector generateRandomDataPoints(const std::vector& aliases, char minValue, char maxValue, std::string); /** Need a specific specialization for bool as got into trouble compiling uniform_int_distribution - * on some platform (e.g. CC7). - */ + * on some platform (e.g. CC7). + */ template <> std::vector generateRandomDataPoints(const std::vector& aliases, bool minValue, bool maxValue, std::string refDate) { @@ -127,9 +128,9 @@ std::vector generateRandomDataPoints(co } /** - * Generate data points of type string, where each string is random, with - * a length between the length of the two input strings (minLength,maxLength) - */ + * 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 generateRandomDataPoints(const std::vector& aliases, std::string minLength, std::string maxLength, std::string refDate) { @@ -139,7 +140,7 @@ std::vector generateRandomDataPoints dist{minLength.size(), maxLength.size()}; auto [seconds, msec] = getDate(refDate); for (auto alias : expandAliases(aliases)) { - auto value = o2::dcs::random_string2(dist(mt)); + std::string value = o2::dcs::random_string2(dist(mt)); dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); } return dpcoms; diff --git a/Detectors/DCS/src/StringUtils.cxx b/Detectors/DCS/src/StringUtils.cxx index e66e7625c6836..f65f522fa426d 100644 --- a/Detectors/DCS/src/StringUtils.cxx +++ b/Detectors/DCS/src/StringUtils.cxx @@ -34,8 +34,8 @@ using namespace std; random_device dev; default_random_engine gen(dev()); -uniform_int_distribution uniform_dist(32, 126); -geometric_distribution geom_dist(0.1); +uniform_int_distribution uniform_dist(32, 126); +geometric_distribution geom_dist(0.1); constexpr char ALPHABET[39]{ 'E', 'T', 'A', 'O', 'I', '_', 'N', 'S', 'H', 'R', diff --git a/Detectors/DCS/testWorkflow/src/DCSDataReplaySpec.cxx b/Detectors/DCS/testWorkflow/src/DCSDataReplaySpec.cxx index 05daf522f372a..8dc003fc176f3 100644 --- a/Detectors/DCS/testWorkflow/src/DCSDataReplaySpec.cxx +++ b/Detectors/DCS/testWorkflow/src/DCSDataReplaySpec.cxx @@ -46,6 +46,10 @@ class DCSDataReplayer : public o2::framework::Task char mAlias[50]; uint64_t mMaxTF; uint64_t mTFs = 0; + int deltaTimeSendData = -1; + uint32_t startTime = -1; + uint32_t endTime = 0; + std::vector> dataIndicesPerTF; TTree mInputData; std::vector mDataPointHints; o2::header::DataDescription mDataDescription; @@ -59,6 +63,7 @@ void DCSDataReplayer::init(o2::framework::InitContext& ic) { mMaxTF = ic.options().get("max-timeframes"); mInputFileName = ic.options().get("input-file"); + deltaTimeSendData = ic.options().get("delta-time-send-data"); mInputData.ReadFile(mInputFileName.data(), "time/D:alias/C:value/D", ';'); mInputData.SetBranchAddress("time", &mTime); mInputData.SetBranchAddress("value", &mValue); @@ -73,22 +78,67 @@ void DCSDataReplayer::run(o2::framework::ProcessingContext& pc) LOG(info) << "Data generator reached TF " << tfid << ", stopping"; pc.services().get().endOfStream(); pc.services().get().readyToQuit(o2::framework::QuitRequest::Me); + return; } std::vector dpcoms; + for (Long64_t iEntry = 0; iEntry < mInputData.GetEntries(); ++iEntry) { - mInputData.GetEntry(iEntry); + int entryTree = iEntry; + + // load only releavant entries if requested + if (deltaTimeSendData > 0 && tfid > 2) { + + if (tfid - 1 >= dataIndicesPerTF.size()) { + LOGP(warning, "TF ID {} is larger than the number of TFs in dataIndicesPerTF: {}", tfid, dataIndicesPerTF.size()); + break; + } + + if (iEntry >= dataIndicesPerTF[tfid - 1].size()) { + break; + } else { + entryTree = dataIndicesPerTF[tfid - 1][iEntry]; + } + } + + mInputData.GetEntry(entryTree); const auto ultime = uint64_t(std::round(mTime * 1000)); const auto seconds = uint32_t(ultime / 1000); const auto msec = uint16_t(ultime % 1000); - - dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(mAlias, float(mValue), seconds, msec)); + if (deltaTimeSendData > 0) { + // send data in packages + if (tfid == 0) { + startTime = std::min(startTime, seconds); + endTime = std::max(endTime, seconds); + if (iEntry == mInputData.GetEntries() - 1) { + const int totalTFs = (endTime - startTime) / deltaTimeSendData + 1; + dataIndicesPerTF.resize(totalTFs); + LOGP(info, "Sending data from {} to {} with {} TFs", startTime, endTime, totalTFs); + } + } else { + if (tfid == 1) { + const int index = (seconds - startTime) / deltaTimeSendData; + dataIndicesPerTF[index].emplace_back(iEntry); + } + const uint64_t startTimeTF = startTime + (tfid - 1) * deltaTimeSendData; + const uint64_t endTimeTF = startTimeTF + deltaTimeSendData; + if (seconds >= startTimeTF && seconds < endTimeTF) { + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(mAlias, float(mValue), seconds, msec)); + // check if all data has been processed + if (seconds == endTime) { + mMaxTF = tfid; + } + } + } + } else { + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(mAlias, float(mValue), seconds, msec)); + } } // auto dpcoms = generate(mDataPointHints, fraction, tfid); LOG(info) << "***************** TF " << tfid << " has generated " << dpcoms.size() << " DPs"; - pc.outputs().snapshot(Output{"DCS", mDataDescription, 0, Lifetime::Timeframe}, dpcoms); + pc.outputs().snapshot(Output{"DCS", mDataDescription, 0}, dpcoms); mTFs++; } } // namespace @@ -113,6 +163,7 @@ o2::framework::DataProcessorSpec getDCSDataReplaySpec(std::vector(timeNow.time_since_epoch()).count(); // in ms - pc.outputs().snapshot(Output{"DCS", mDataDescription, 0, Lifetime::Timeframe}, dpcoms); + pc.outputs().snapshot(Output{"DCS", mDataDescription, 0}, dpcoms); mTFs++; } } // namespace diff --git a/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h b/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h index 2cf97e55e8c7c..679a5b9d91f41 100644 --- a/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h +++ b/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h @@ -14,6 +14,7 @@ #include "Framework/DataSpecUtils.h" #include "Framework/ExternalFairMQDeviceProxy.h" +#include "Framework/RawDeviceService.h" #include #include #include "DetectorsDCS/DataPointIdentifier.h" @@ -40,9 +41,7 @@ struct hash { }; } // namespace std -namespace o2 -{ -namespace dcs +namespace o2::dcs { using DPID = o2::dcs::DataPointIdentifier; using DPVAL = o2::dcs::DataPointValue; @@ -51,10 +50,11 @@ 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& dpid2group, bool fbiFirst, bool verbose = false, int FBIPerInterval = 1) +o2f::InjectorFunction dcs2dpl(std::unordered_map>& dpid2group, bool fbiFirst, bool verbose = false, int FBIPerInterval = 1) { - return [dpid2group, fbiFirst, verbose, FBIPerInterval](o2::framework::TimingInfo& tinfo, fair::mq::Device& device, fair::mq::Parts& parts, o2f::ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [dpid2group, fbiFirst, verbose, FBIPerInterval](o2::framework::TimingInfo& tinfo, framework::ServiceRegistryRef const& services, fair::mq::Parts& parts, o2f::ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { + auto *device = services.get().device(); static std::unordered_map cache; // will keep only the latest measurement in the 1-second wide window for each DPID static std::unordered_map sentToChannel; static auto timer = std::chrono::high_resolution_clock::now(); @@ -69,7 +69,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp // check if we got FBI (Master) or delta (MasterDelta) if (!parts.Size()) { LOGP(warn, "Empty input recieved at timeslice {}", tinfo.timeslice); - return; + return false; } std::string firstName = std::string((char*)&(reinterpret_cast(parts.At(0)->GetData()))->id); @@ -104,7 +104,15 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp // 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 dest; + if (mapEl == dpid2group.end()) { + dest = "none"; + } else { + for (const auto& ds : mapEl->second) { + dest += fmt::format("{}, ", ds.as()); + } + } + LOG(info) << "Received DP " << src.id << " (data = " << src.data << "), matched to output-> " << dest; } if (mapEl != dpid2group.end()) { cache[src.id] = src; // this is needed in case in the 1s window we get a new value for the same DP @@ -124,6 +132,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp } std::chrono::duration> duration = timerNow - timer; + bool didSendMessages = false; if (duration.count() > 1 && (seenFBI || !fbiFirst)) { // did we accumulate for 1 sec and have we seen FBI if it was requested? std::unordered_map, std::hash> outputs; // in the cache we have the final values of the DPs that we should put in the output @@ -131,7 +140,9 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp for (auto& it : cache) { auto mapEl = dpid2group.find(it.first); if (mapEl != dpid2group.end()) { - outputs[mapEl->second].push_back(it.second); + for (const auto& ds : mapEl->second) { + outputs[ds].push_back(it.second); + } } } std::uint64_t creation = std::chrono::time_point_cast(timerNow).time_since_epoch().count(); @@ -158,7 +169,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp hdr.payloadSize = it.second.size() * sizeof(DPCOM); hdr.firstTForbit = 0; // this should be irrelevant for DCS o2h::Stack headerStack{hdr, o2::framework::DataProcessingHeader{tinfo.timeslice, 1, creation}}; - auto fmqFactory = device.GetChannel(channel).Transport(); + 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()); @@ -181,8 +192,9 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp if (verbose) { LOG(info) << "Sending " << msgIt.second->Size() / 2 << " parts to channel " << msgIt.first; } - o2f::sendOnChannel(device, *msgIt.second.get(), msgIt.first, tinfo.timeslice); + o2f::sendOnChannel(*device, *msgIt.second.get(), msgIt.first, tinfo.timeslice); sentToChannel[msgIt.first]++; + didSendMessages |= msgIt.second->Size() > 0; } timer = timerNow; cache.clear(); @@ -200,10 +212,10 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp } LOGP(info, "{} inputs ({} bytes) of which {} FBI ({} bytes) seen in {:.3f} s | {}", nInp, fmt::group_digits(szInp), nInpFBI, fmt::group_digits(szInpFBI), runtime, sent); } + return didSendMessages; }; } -} // namespace dcs } // namespace o2 #endif /* O2_DCS_TO_DPL_CONVERTER_H */ diff --git a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx index cd94bb61b87d3..b660093fb6029 100644 --- a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx +++ b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ControlService.h" +#include "Framework/RawDeviceService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" #include "Framework/ConfigParamSpec.h" @@ -33,7 +34,7 @@ using namespace o2::framework; using DetID = o2::detectors::DetID; -std::array exceptionsDetID{"GRP"}; +std::array exceptionsDetID{"GRP", "AGD"}; void sendAnswer(const std::string& what, const std::string& ack_chan, fair::mq::Device& device) { @@ -66,16 +67,17 @@ auto getDataOriginFromFilename(const std::string& filename) InjectorFunction dcs2dpl(const std::string& acknowledge) { - return [acknowledge](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + return [acknowledge](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto *device = services.get().device(); if (parts.Size() == 0) { // received at ^c, ignore LOG(info) << "ignoring empty message"; - return; + return false; } // make sure just 2 messages received if (parts.Size() != 2) { LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - sendAnswer("error0: wrong number of messages", acknowledge, device); - return; + sendAnswer("error0: wrong number of messages", acknowledge, *device); + return false; } std::string filename{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t filesize = parts.At(1)->GetSize(); @@ -83,8 +85,8 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) o2::header::DataOrigin dataOrigin = getDataOriginFromFilename(filename); if (dataOrigin == o2::header::gDataOriginInvalid) { LOG(error) << "unknown detector for " << filename; - sendAnswer(fmt::format("{}:error1: unrecognized filename", filename), acknowledge, device); - return; + sendAnswer(fmt::format("{}:error1: unrecognized filename", filename), acknowledge, *device); + return false; } o2::header::DataHeader hdrF("DCS_CONFIG_FILE", dataOrigin, 0); @@ -93,8 +95,8 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) auto channel = channelRetriever(outsp, newTimesliceId); if (channel.empty()) { LOG(error) << "No output channel found for OutputSpec " << outsp; - sendAnswer(fmt::format("{}:error2: no channel to send", filename), acknowledge, device); - return; + sendAnswer(fmt::format("{}:error2: no channel to send", filename), acknowledge, *device); + return false; } hdrF.tfCounter = newTimesliceId; @@ -111,7 +113,7 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) hdrN.payloadSize = parts.At(0)->GetSize(); hdrN.firstTForbit = 0; // this should be irrelevant for DCS - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); std::uint64_t creation = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); o2::header::Stack headerStackF{hdrF, DataProcessingHeader{newTimesliceId, 1, creation}}; @@ -129,15 +131,16 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) fair::mq::Parts outPartsF; outPartsF.AddPart(std::move(hdMessageF)); outPartsF.AddPart(std::move(plMessageF)); - sendOnChannel(device, outPartsF, channel, (size_t)-1); + sendOnChannel(*device, outPartsF, channel, (size_t)-1); fair::mq::Parts outPartsN; outPartsN.AddPart(std::move(hdMessageN)); outPartsN.AddPart(std::move(plMessageN)); - sendOnChannel(device, outPartsN, channel, newTimesliceId); + sendOnChannel(*device, outPartsN, channel, newTimesliceId); - sendAnswer(fmt::format("{}:ok", filename), acknowledge, device); + sendAnswer(fmt::format("{}:ok", filename), acknowledge, *device); LOG(info) << "Sent DPL message and acknowledgment for file " << filename; + return true; }; } diff --git a/Detectors/DCS/testWorkflow/src/dcs-config-test-workflow.cxx b/Detectors/DCS/testWorkflow/src/dcs-config-test-workflow.cxx index 68a7172dddd4d..d762037445d88 100644 --- a/Detectors/DCS/testWorkflow/src/dcs-config-test-workflow.cxx +++ b/Detectors/DCS/testWorkflow/src/dcs-config-test-workflow.cxx @@ -53,8 +53,8 @@ DataProcessorSpec getDCSConsumerSpec(DetID det) procName += det.getName(); return DataProcessorSpec{ procName, - Inputs{{"confFile", ConcreteDataTypeMatcher{det.getDataOrigin(), "DCS_CONFIG_FILE"}, Lifetime::Timeframe}, - {"confFileName", ConcreteDataTypeMatcher{det.getDataOrigin(), "DCS_CONFIG_NAME"}, Lifetime::Timeframe}}, + Inputs{{"confFile", ConcreteDataTypeMatcher{det.getDataOrigin(), "DCS_CONFIG_FILE"}, Lifetime::Sporadic}, + {"confFileName", ConcreteDataTypeMatcher{det.getDataOrigin(), "DCS_CONFIG_NAME"}, Lifetime::Sporadic}}, Outputs{}, AlgorithmSpec{adaptFromTask()}, Options{}}; diff --git a/Detectors/DCS/testWorkflow/src/dcs-proxy.cxx b/Detectors/DCS/testWorkflow/src/dcs-proxy.cxx index 770305f909dc1..bfe91a946d13b 100644 --- a/Detectors/DCS/testWorkflow/src/dcs-proxy.cxx +++ b/Detectors/DCS/testWorkflow/src/dcs-proxy.cxx @@ -65,18 +65,18 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); std::string url = config.options().get("ccdb-url"); - std::unordered_map dpid2DataDesc; + std::unordered_map> dpid2DataDesc; if (testMode) { DPID dpidtmp; DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000100", DeliveryType::DPVAL_STRING); - dpid2DataDesc[dpidtmp] = "COMMON"; // i.e. this will go to {DCS/COMMON/0} OutputSpec + dpid2DataDesc[dpidtmp] = {"COMMON"}; // i.e. this will go to {DCS/COMMON/0} OutputSpec DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000110", DeliveryType::DPVAL_STRING); - dpid2DataDesc[dpidtmp] = "COMMON"; + dpid2DataDesc[dpidtmp] = {"COMMON"}; DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000200", DeliveryType::DPVAL_STRING); - dpid2DataDesc[dpidtmp] = "COMMON1"; + dpid2DataDesc[dpidtmp] = {"COMMON1"}; DPID::FILL(dpidtmp, "ADAPOS_LG/TEST_000240", DeliveryType::DPVAL_INT); - dpid2DataDesc[dpidtmp] = "COMMON1"; + dpid2DataDesc[dpidtmp] = {"COMMON1"}; } else { @@ -95,7 +95,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) for (auto& el : *dpid2Det) { o2::header::DataDescription tmpd; tmpd.runtimeInit(el.second.c_str(), el.second.size()); - dpid2DataDesc[el.first] = tmpd; + dpid2DataDesc[el.first].push_back(tmpd); } } } @@ -107,7 +107,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) // now collect all required outputs to define OutputSpecs for specifyExternalFairMQDeviceProxy std::unordered_map> outMap; for (auto itdp : dpid2DataDesc) { - outMap[itdp.second]++; + for (const auto& ds : itdp.second) { + outMap[ds]++; + } } Outputs dcsOutputs; @@ -120,6 +122,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) std::move(dcsOutputs), "type=pull,method=connect,address=tcp://aldcsadaposactor:60000,rateLogging=1,transport=zeromq", dcs2dpl(dpid2DataDesc, fbiFirst, verbose, repRate)); + dcsProxy.labels.emplace_back(DataProcessorLabel{"input-proxy"}); WorkflowSpec workflow; workflow.emplace_back(dcsProxy); diff --git a/Detectors/EMCAL/base/CMakeLists.txt b/Detectors/EMCAL/base/CMakeLists.txt index 9a852c00616e3..62a0cb6d9a25b 100644 --- a/Detectors/EMCAL/base/CMakeLists.txt +++ b/Detectors/EMCAL/base/CMakeLists.txt @@ -42,6 +42,13 @@ o2_add_test(Mapper LABELS emcal ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) +o2_add_test(Geometry + SOURCES test/testGeometry.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALBase + COMPONENT_NAME emcal + LABELS emcal + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) + o2_add_test(RCUTrailer SOURCES test/testRCUTrailer.cxx PUBLIC_LINK_LIBRARIES O2::EMCALBase diff --git a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h index c8816cfaa282f..0c3438042ca77 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h +++ b/Detectors/EMCAL/base/include/EMCALBase/ClusterFactory.h @@ -11,6 +11,7 @@ #ifndef ALICEO2_EMCAL_CLUSTERFACTORY_H_ #define ALICEO2_EMCAL_CLUSTERFACTORY_H_ #include +#include #include #include #include "Rtypes.h" @@ -19,6 +20,8 @@ #include "DataFormatsEMCAL/Digit.h" #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/AnalysisCluster.h" +#include "DataFormatsEMCAL/CellLabel.h" +#include "DataFormatsEMCAL/ClusterLabel.h" #include "EMCALBase/Geometry.h" #include "MathUtils/Cartesian.h" @@ -188,7 +191,7 @@ class ClusterFactory /// /// Dummy constructor - ClusterFactory(); + ClusterFactory() = default; /// /// \brief Constructor initializing the ClusterFactory @@ -230,7 +233,7 @@ class ClusterFactory /// /// evaluates cluster parameters: position, shower shape, primaries ... - AnalysisCluster buildCluster(int index) const; + AnalysisCluster buildCluster(int index, o2::emcal::ClusterLabel* clusterLabel = nullptr) const; void SetECALogWeight(Float_t w) { mLogWeight = w; } float GetECALogWeight() const { return mLogWeight; } @@ -273,9 +276,10 @@ class ClusterFactory /// \brief Look to cell neighbourhood and reject if it seems exotic /// \param towerId: tower ID of cell with largest energy fraction in cluster /// \param ecell: energy of the cell with largest energy fraction in cluster - /// \param exoticTime time of the cell with largest energy fraction in cluster + /// \param exoticTime: time of the cell with largest energy fraction in cluster + /// \param fCross: exoticity parameter (1-E_cross/E_cell^max) will be caluclated for this check /// \return bool true if cell is found exotic - bool isExoticCell(short towerId, float ecell, float const exoticTime) const; + bool isExoticCell(short towerId, float ecell, float const exoticTime, float& fCross) const; /// \brief Calculate the energy in the cross around the energy of a given cell. /// \param absID: controlled cell absolute ID number @@ -331,7 +335,7 @@ class ClusterFactory bool getUseWeightExotic() const { return mUseWeightExotic; } void setUseWeightExotic(float useWeightExotic) { mUseWeightExotic = useWeightExotic; } - void setContainer(gsl::span clusterContainer, gsl::span cellContainer, gsl::span indicesContainer) + void setContainer(gsl::span clusterContainer, gsl::span cellContainer, gsl::span indicesContainer, gsl::span cellLabelContainer = {}) { mClustersContainer = clusterContainer; mInputsContainer = cellContainer; @@ -339,6 +343,9 @@ class ClusterFactory if (!getLookUpInit()) { setLookUpTable(); } + if (!cellLabelContainer.empty()) { + mCellLabelContainer = cellLabelContainer; + } } void setLookUpTable(void) @@ -394,6 +401,10 @@ class ClusterFactory /// in cell units void evalElipsAxis(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; + /// + /// Calculate the number of local maxima in the cluster + void evalNExMax(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; + /// /// Time is set to the time of the digit with the maximum energy void evalTime(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const; @@ -425,10 +436,11 @@ class ClusterFactory float mExoticCellInCrossMinAmplitude = 0.1; ///< Minimum energy of cells in cross, if lower not considered in cross bool mUseWeightExotic = false; ///< States if weights should be used for exotic cell cut - gsl::span mClustersContainer; ///< Container for all the clusters in the event - gsl::span mInputsContainer; ///< Container for all the cells/digits in the event - gsl::span mCellsIndices; ///< Container for cells indices in the event - std::array mLoolUpTowerToIndex; ///< Lookup table to match tower id with cell index, needed for exotic check + gsl::span mClustersContainer; ///< Container for all the clusters in the event + gsl::span mInputsContainer; ///< Container for all the cells/digits in the event + gsl::span mCellsIndices; ///< Container for cells indices in the event + std::array mLoolUpTowerToIndex; ///< Lookup table to match tower id with cell index, needed for exotic check + gsl::span mCellLabelContainer; ///< Container for all the cell labels in the event ClassDefNV(ClusterFactory, 2); }; diff --git a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h index 5be8095a4eb7a..d07f42689bf7a 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Geometry.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Geometry.h @@ -12,14 +12,16 @@ #ifndef ALICEO2_EMCAL_GEOMETRY_H_ #define ALICEO2_EMCAL_GEOMETRY_H_ -#include +#include #include +#include #include #include #include +#include #include -#include +#include #include #include @@ -57,7 +59,7 @@ class Geometry /// | EMCAL_COMPLETE12SMV1_DCAL | Full EMCAL, 10 DCAL Supermodules (not used in practice) | /// | EMCAL_COMPLETE12SMV1_DCAL_8SM | Full EMCAL, 8 DCAL Supermodules (run2) | /// | EMCAL_COMPLETE12SMV1_DCAL_DEV | Full EMCAL, DCAL development geometry (not used) | - Geometry(const std::string_view name, const std::string_view mcname = "", const std::string_view mctitle = ""); + explicit Geometry(const std::string_view name, const std::string_view mcname = "", const std::string_view mctitle = ""); /// \brief Copy constructor. Geometry(const Geometry& geom); @@ -70,6 +72,7 @@ class Geometry /// \brief Get geometry instance. It should have been set before. /// \return the pointer of the unique instance of the geometry + /// \throw GeometryNotInitializedException in case the geometry is not initialized static Geometry* GetInstance(); /// \brief Get instance of the EMCAL geometry @@ -426,6 +429,14 @@ class Geometry /// \return Position (0 - phi, 1 - eta) of the cell inside teh supermodule std::tuple GetCellPhiEtaIndexInSModule(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const; + /// \brief Get topological row and column of cell in SM (same as for clusteriser with artifical gaps) + /// \param supermoduleID super module number + /// \param moduleID module number + /// \param phiInModule index in phi direction in module + /// \param etaInModule index in phi direction in module + /// \return tuple with (row, column) of the cell, which is global numbering scheme + std::tuple GetTopologicalRowColumn(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const; + /// \brief Adapt cell indices in supermodule to online indexing /// \param supermoduleID super module number of the channel/cell /// \param iphi row/phi cell index, modified for DCal @@ -512,13 +523,21 @@ class Geometry /// \return col std::tuple getOnlineID(int towerID); + /// \brief Check if 2 cells belong to the same T-Card + /// \param absId1: Reference absId cell + /// \param absId2: Cross checked cell absId + /// \return true if belong to same TCard else false + /// \return rowDiff: Distance in rows + /// \return colDiff: Distance in columns + std::tuple areAbsIDsFromSameTCard(int absId1, int absId2) const; + /// \brief Temporary link assignment (till final link assignment is known - /// \brief eventually taken from CCDB) /// \brief Current mapping can be found under https://alice.its.cern.ch/jira/browse/EMCAL-660 /// \param ddlID DDL ID /// \return CRORC ID /// \return CRORC Link - std::tuple getLinkAssignment(int ddlID) const { return std::make_tuple(mCRORCID[ddlID / 2], mCRORCLink[ddlID]); }; + std::tuple getLinkAssignment(int ddlID) const { return std::make_tuple(mCRORCID[ddlID], mCRORCLink[ddlID]); }; std::vector GetEMCSystem() const { return mEMCSMSystem; } // EMC System, SM type list // Local Coordinates of SM @@ -563,6 +582,11 @@ class Geometry /// void SetMisalMatrix(const TGeoHMatrix* m, Int_t smod) const; + /// + /// Method to set shift-rotational matrixes from CCDB + /// + void SetMisalMatrixFromCcdb(const char* path = "Users/m/mhemmer/EMCAL/Config/GeometryAligned", int timestamp = 10000) const; + /// /// Transform clusters cell position into global with alternative method, taking into account the depth calculation. /// Input are: @@ -689,8 +713,8 @@ class Geometry Float_t mSteelFrontThick; ///< Thickness of the front stell face of the support box - 9-sep-04; obsolete? - std::array mCRORCID = {110, 112, 110, 112, 110, 112, 111, 113, 111, 113, 111, 113, 114, 116, 114, 116, 115, 117, 115, 117}; // CRORC ID w.r.t SM - std::array mCRORCLink = {0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 4, -1, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 2, -1}; // CRORC limk w.r.t FEE ID + std::array mCRORCID = {110, 110, 112, 112, 110, 110, 112, 112, 110, 110, 112, 112, 111, 111, 113, 113, 111, 111, 113, 113, 111, 111, 113, 113, 114, 114, 116, 116, 114, 114, 116, 116, 115, 115, 117, 117, 115, 115, 117, 117, -1, -1, -1, -1, 111, 117}; // CRORC ID w.r.t SM + std::array mCRORCLink = {0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 4, -1, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 0, 1, 0, 1, 2, 3, 2, -1, -1, -1, -1, -1, 5, 3}; // CRORC limk w.r.t FEE ID mutable const TGeoHMatrix* SMODULEMATRIX[EMCAL_MODULES]; ///< Orientations of EMCAL super modules std::vector> mCellIndexLookup; ///< Lookup table for cell indices diff --git a/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h b/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h index 5cba99a3567c0..3fda26cbbfbc6 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h +++ b/Detectors/EMCAL/base/include/EMCALBase/GeometryBase.h @@ -33,6 +33,23 @@ enum AcceptanceType_t { EMCAL_ACCEPTANCE = 1, const std::string DEFAULT_GEOMETRY = "EMCAL_COMPLETE12SMV1_DCAL_8SM"; +/// \class GeometryNotInitializedException +/// \brief Error handling access to non-initialized geometry +/// \ingroup EMCALbase +class GeometryNotInitializedException final : public std::exception +{ + public: + /// \brief Constructor + GeometryNotInitializedException() = default; + + /// \brief Destructor + ~GeometryNotInitializedException() noexcept final = default; + + /// \brief Access to error message + /// \return Error message + const char* what() const noexcept { return "Geometry not initialized"; } +}; + /// \class InvalidModuleException /// \brief Error Handling when an invalid module ID (outside the limits) is called /// \ingroup EMCALbase diff --git a/Detectors/EMCAL/base/include/EMCALBase/Hit.h b/Detectors/EMCAL/base/include/EMCALBase/Hit.h index 640001a64872d..345a0918c3831 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/Hit.h +++ b/Detectors/EMCAL/base/include/EMCALBase/Hit.h @@ -22,6 +22,8 @@ namespace emcal /// \class Hit /// \brief EMCAL simulation hit information /// \ingroup EMCALbase +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since August 31st, 2017 class Hit : public o2::BasicXYZEHit { public: @@ -63,11 +65,6 @@ class Hit : public o2::BasicXYZEHit /// \return This point with the summed energy loss Hit& operator+=(const Hit& rhs); - /// \brief Creates a new point base on this point but adding the energy loss of the right hand side - /// \param rhs Hit to add to - /// \return New EMAL point base on this point - Hit operator+(const Hit& rhs) const; - /// \brief Destructor ~Hit() = default; @@ -99,6 +96,16 @@ class Hit : public o2::BasicXYZEHit ClassDefNV(Hit, 1); }; +/// \brief Creates a new point base on this point but adding the energy loss of the right hand side +/// \param lhs Left hand side of the sum +/// \param rhs Right hand side of the sum +/// \return New EMAL point base on this point +Hit operator+(const Hit& lhs, const Hit& rhs); + +/// \brief Output stream operator for EMCAL hits +/// \param stream Stream to write on +/// \param point Hit to be printed +/// \return Stream after printing std::ostream& operator<<(std::ostream& stream, const Hit& point); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h index 8723a7a68264b..0fcb87f20f2ff 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h +++ b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h @@ -153,6 +153,10 @@ class RCUTrailer /// \return Size of the payload as number of 32 bit workds uint32_t getPayloadSize() const { return mPayloadSize; } + /// \brief Get number of corrupted trailer words (undefined trailer word code) + /// \return Number of trailer word corruptions + uint32_t getTrailerWordCorruptions() const { return mWordCorruptions; } + /// \brief Get the firmware version /// \return Firmware version uint8_t getFirmwareVersion() const { return mFirmwareVersion; } @@ -384,6 +388,11 @@ class RCUTrailer /// are assigned based on the trailer word marker. static RCUTrailer constructFromPayloadWords(const gsl::span payloadwords); + /// \brief Check whether the word is a valid last trailer word + /// \param trailerword Word to be checked + /// \return True if the word is a valid last trailer word, false if there are inconsistencies + static bool checkLastTrailerWord(uint32_t trailerword); + private: /// \struct AltroConfig /// \brief Bit field configuration of the ALTRO config registers @@ -438,6 +447,7 @@ class RCUTrailer uint8_t mFirmwareVersion = 0; ///< RCU firmware version uint32_t mTrailerSize = 0; ///< Size of the trailer (in number of 32 bit words) uint32_t mPayloadSize = 0; ///< Size of the payload (in nunber of 32 bit words) + uint32_t mWordCorruptions = 0; ///< Number of trailer word corruptions (decoding only) uint32_t mFECERRA = 0; ///< contains errors related to ALTROBUS transactions uint32_t mFECERRB = 0; ///< contains errors related to ALTROBUS transactions ErrorCounters mErrorCounter = {0, 0}; ///< Error counter registers diff --git a/Detectors/EMCAL/base/include/EMCALBase/TriggerMappingV2.h b/Detectors/EMCAL/base/include/EMCALBase/TriggerMappingV2.h index 3b56c1794d3e4..24d8c1686f005 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/TriggerMappingV2.h +++ b/Detectors/EMCAL/base/include/EMCALBase/TriggerMappingV2.h @@ -49,6 +49,7 @@ class TriggerMappingV2 static constexpr unsigned int FASTORSPHI = (5 * FASTORSPHISM) + (1 * FASTORSPHISM / 3) /*EMCAL*/ + (3 * FASTORSPHISM) + (1 * FASTORSPHISM / 3) /*DCAL */; ///< Number of FastOR/EMCALs in Phi static constexpr unsigned int ALLFASTORS = FASTORSETA * FASTORSPHI; ///< Number of FastOR/EMCALs + static constexpr unsigned int PATCHESINTRU = 77; //******************************************** // Index types diff --git a/Detectors/EMCAL/base/src/ClusterFactory.cxx b/Detectors/EMCAL/base/src/ClusterFactory.cxx index c868625a25a7d..1752e5c0e98ee 100644 --- a/Detectors/EMCAL/base/src/ClusterFactory.cxx +++ b/Detectors/EMCAL/base/src/ClusterFactory.cxx @@ -18,6 +18,8 @@ #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/AnalysisCluster.h" #include "DataFormatsEMCAL/Constants.h" +#include "DataFormatsEMCAL/CellLabel.h" +#include "DataFormatsEMCAL/ClusterLabel.h" #include "EMCALBase/Geometry.h" #include "MathUtils/Cartesian.h" @@ -25,17 +27,9 @@ using namespace o2::emcal; -template -ClusterFactory::ClusterFactory() -{ - mGeomPtr = o2::emcal::Geometry::GetInstance(); -} - template ClusterFactory::ClusterFactory(gsl::span clustersContainer, gsl::span inputsContainer, gsl::span cellsIndices) { - mGeomPtr = o2::emcal::Geometry::GetInstance(); - setContainer(clustersContainer, inputsContainer, cellsIndices); } @@ -46,13 +40,14 @@ void ClusterFactory::reset() mInputsContainer = gsl::span(); mCellsIndices = gsl::span(); mLookUpInit = false; + mCellLabelContainer = gsl::span(); } /// /// evaluates cluster parameters: position, shower shape, primaries ... //____________________________________________________________________________ template -o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIndex) const +o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIndex, o2::emcal::ClusterLabel* clusterLabel) const { if (clusterIndex >= mClustersContainer.size()) { throw ClusterRangeException(clusterIndex, mClustersContainer.size()); @@ -78,8 +73,11 @@ o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIn float exoticTime = mInputsContainer[inputIndMax].getTimeStamp(); + float fCross = 0.; + try { - clusterAnalysis.setIsExotic(isExoticCell(towerId, inputEnergyMax, exoticTime)); + clusterAnalysis.setIsExotic(isExoticCell(towerId, inputEnergyMax, exoticTime, fCross)); + clusterAnalysis.setFCross(fCross); } catch (UninitLookUpTableException& e) { LOG(error) << e.what(); } @@ -94,8 +92,22 @@ o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIn std::vector cellsIdices; + bool addClusterLabels = ((clusterLabel != nullptr) && (mCellLabelContainer.size() > 0)); for (auto cellIndex : inputsIndices) { cellsIdices.push_back(cellIndex); + if (addClusterLabels) { + for (size_t iLabel = 0; iLabel < mCellLabelContainer[cellIndex].GetLabelSize(); iLabel++) { + if (mCellLabelContainer[cellIndex].GetAmplitudeFraction(iLabel) <= 0.f) { + continue; // skip 0 entries + } + clusterLabel->addValue(mCellLabelContainer[cellIndex].GetLabel(iLabel), + mCellLabelContainer[cellIndex].GetAmplitudeFraction(iLabel) * mInputsContainer[cellIndex].getEnergy()); + } + } + } + if (addClusterLabels) { + clusterLabel->orderLabels(); + clusterLabel->normalize(cellAmp); } clusterAnalysis.setCellsIndices(cellsIdices); @@ -108,6 +120,9 @@ o2::emcal::AnalysisCluster ClusterFactory::buildCluster(int clusterIn evalElipsAxis(inputsIndices, clusterAnalysis); evalDispersion(inputsIndices, clusterAnalysis); + // evaluate number of local maxima + evalNExMax(inputsIndices, clusterAnalysis); + evalCoreEnergy(inputsIndices, clusterAnalysis); evalTime(inputsIndices, clusterAnalysis); @@ -244,7 +259,7 @@ void ClusterFactory::evalLocalPosition(gsl::span inputsInd clRmsXYZ[i] += (w * xyzi[i] * xyzi[i]); } } // w > 0 - } // dig loop + } // dig loop // cout << " wtot " << wtot << endl; @@ -477,6 +492,64 @@ void ClusterFactory::evalCoreEnergy(gsl::span inputsIndice clusterAnalysis.setCoreEnergy(coreEnergy); } +/// +/// Calculate the number of local maxima in the cluster +//____________________________________________________________________________ +template +void ClusterFactory::evalNExMax(gsl::span inputsIndices, AnalysisCluster& clusterAnalysis) const +{ + // Pre-compute cell indices and energies for all cells in cluster to avoid multiple expensive geometry lookups + const size_t n = inputsIndices.size(); + std::vector rows; + std::vector columns; + std::vector energies; + + rows.reserve(n); + columns.reserve(n); + energies.reserve(n); + + for (auto iInput : inputsIndices) { + auto [nSupMod, nModule, nIphi, nIeta] = mGeomPtr->GetCellIndex(mInputsContainer[iInput].getTower()); + + // get a nice topological indexing that is done in exactly the same way as used by the clusterizer + // this way we can handle the shared cluster cases correctly + const auto [row, column] = mGeomPtr->GetTopologicalRowColumn(nSupMod, nModule, nIphi, nIeta); + + rows.push_back(row); + columns.push_back(column); + energies.push_back(mInputsContainer[iInput].getEnergy()); + } + + // Now find local maxima using pre-computed data + int nExMax = 0; + for (size_t i = 0; i < n; i++) { + // this cell is assumed to be local maximum unless we find a higher energy cell in the neighborhood + bool isExMax = true; + + // loop over all other cells in cluster + for (size_t j = 0; j < n; j++) { + if (i == j) { + continue; + } + + // adjacent cell is any cell with adjacent phi or eta index + if (std::abs(rows[i] - rows[j]) <= 1 && + std::abs(columns[i] - columns[j]) <= 1) { + + // if there is a cell with higher energy than the current cell, it is not a local maximum + if (energies[j] > energies[i]) { + isExMax = false; + break; + } + } + } + if (isExMax) { + nExMax++; + } + } + clusterAnalysis.setNExMax(nExMax); +} + /// /// Calculates the axis of the shower ellipsoid in eta and phi /// in cell units @@ -591,7 +664,7 @@ std::tuple ClusterFactory::getMaximalEnergyI /// Look to cell neighbourhood and reject if it seems exotic //____________________________________________________________________________ template -bool ClusterFactory::isExoticCell(short towerId, float ecell, float const exoticTime) const +bool ClusterFactory::isExoticCell(short towerId, float ecell, float const exoticTime, float& fCross) const { if (ecell < mExoticCellMinAmplitude) { return false; // do not reject low energy cells @@ -603,8 +676,9 @@ bool ClusterFactory::isExoticCell(short towerId, float ecell, float c } float eCross = getECross(towerId, ecell, exoticTime); + fCross = 1.f - eCross / ecell; - if (1 - eCross / ecell > mExoticCellFraction) { + if (fCross > mExoticCellFraction) { LOG(debug) << "EXOTIC CELL id " << towerId << ", eCell " << ecell << ", eCross " << eCross << ", 1-eCross/eCell " << 1 - eCross / ecell; return true; } diff --git a/Detectors/EMCAL/base/src/Geometry.cxx b/Detectors/EMCAL/base/src/Geometry.cxx index ff668e02dc9a2..3707e22f2da57 100644 --- a/Detectors/EMCAL/base/src/Geometry.cxx +++ b/Detectors/EMCAL/base/src/Geometry.cxx @@ -8,17 +8,40 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "EMCALBase/Geometry.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include #include -#include - -#include -#include "EMCALBase/Geometry.h" +#include "DataFormatsEMCAL/Constants.h" +#include "EMCALBase/GeometryBase.h" +#include "CCDB/CcdbApi.h" #include "EMCALBase/ShishKebabTrd1Module.h" +#include "GPUROOTCartesianFwd.h" #include @@ -190,6 +213,9 @@ Geometry::~Geometry() Geometry* Geometry::GetInstance() { Geometry* rv = static_cast(sGeom); + if (!rv) { + throw GeometryNotInitializedException(); + } return rv; } @@ -306,6 +332,9 @@ void Geometry::DefineSamplingFraction(const std::string_view mcname, const std:: } Float_t samplingFactorTranportModel = 1.; + // Note: The sampling factors are chosen so that results from the simulation + // engines correspond well with testbeam data + if (contains(mcname, "Geant3")) { samplingFactorTranportModel = 1.; // 0.988 // Do nothing } else if (contains(mcname, "Fluka")) { @@ -315,6 +344,9 @@ void Geometry::DefineSamplingFraction(const std::string_view mcname, const std:: LOG(info) << "Selected physics list: " << physicslist; // sampling factors for different Geant4 physics list // GEANT4 10.7 -> EMCAL-784 + + // set a default (there may be many physics list strings) + samplingFactorTranportModel = 0.81; if (physicslist == "FTFP_BERT_EMV+optical") { samplingFactorTranportModel = 0.821; } else if (physicslist == "FTFP_BERT_EMV+optical+biasing") { @@ -1023,7 +1055,7 @@ std::tuple Geometry::CalculateCellIndex(Int_t absId) const Int_t nModule = tmp / mNCellsInModule; tmp = tmp % mNCellsInModule; - Int_t nIphi = tmp / mNPHIdiv, nIeta = tmp % mNPHIdiv; + Int_t nIphi = tmp / mNPHIdiv, nIeta = tmp % mNETAdiv; return std::make_tuple(nSupMod, nModule, nIphi, nIeta); } @@ -1071,6 +1103,30 @@ std::tuple Geometry::GetCellPhiEtaIndexInSModule(int supermoduleID, in return std::make_tuple(phiInSupermodule, etaInSupermodule); } +std::tuple Geometry::GetTopologicalRowColumn(int supermoduleID, int moduleID, int phiInModule, int etaInModule) const +{ + auto [iphi, ieta] = GetCellPhiEtaIndexInSModule(supermoduleID, moduleID, phiInModule, etaInModule); + int row = iphi; + int column = ieta; + + // Add shifts wrt. supermodule and type of calorimeter + // NOTE: + // * Rows (phi) are arranged that one space is left empty between supermodules in phi + // This is due to the physical gap that forbids clustering + // * For DCAL, there is an additional empty column between two supermodules in eta + // Again, this is to account for the gap in DCAL + + row += supermoduleID / 2 * (24 + 1); + // In DCAL, leave a gap between two SMs with same phi + if (!IsDCALSM(supermoduleID)) { // EMCAL + column += supermoduleID % 2 * 48; + } else { + column += supermoduleID % 2 * (48 + 1); + } + + return std::make_tuple(static_cast(row), static_cast(column)); +} + std::tuple Geometry::ShiftOnlineToOfflineCellIndexes(Int_t supermoduleID, Int_t iphi, Int_t ieta) const { if (supermoduleID == 13 || supermoduleID == 15 || supermoduleID == 17) { @@ -1548,6 +1604,7 @@ const TGeoHMatrix* Geometry::GetMatrixForSuperModule(Int_t smod) const if (!SMODULEMATRIX[smod]) { if (gGeoManager) { + LOG(info) << "Loading EMCAL misalignment matrix for SM " << smod << " from GeoManager."; SetMisalMatrix(GetMatrixForSuperModuleFromGeoManager(smod), smod); } else { LOG(fatal) << "Cannot find EMCAL misalignment matrices! Recover them either: \n" @@ -1753,6 +1810,25 @@ void Geometry::SetMisalMatrix(const TGeoHMatrix* m, Int_t smod) const } } +void Geometry::SetMisalMatrixFromCcdb(const char* path, int timestamp) const +{ + LOG(info) << "Using CCDB to obtain EMCal alignment."; + o2::ccdb::CcdbApi api; + std::map metadata; // can be empty + api.init("http://alice-ccdb.cern.ch"); + TObjArray* matrices = api.retrieveFromTFileAny(path, metadata, timestamp); + + for (int iSM = 0; iSM < mNumberOfSuperModules; ++iSM) { + TGeoHMatrix* mat = reinterpret_cast(matrices->At(iSM)); + if (mat) { + + SetMisalMatrix(mat, iSM); + } else { + LOG(info) << "Could not obtain Alignment Matrix for SM " << iSM; + } + } +} + Bool_t Geometry::IsDCALSM(Int_t iSupMod) const { if (mEMCSMSystem[iSupMod] == DCAL_STANDARD || mEMCSMSystem[iSupMod] == DCAL_EXT) { @@ -1825,3 +1901,44 @@ std::tuple Geometry::getOnlineID(int towerID) return std::make_tuple(supermoduleID * 2 + ddlInSupermoudel, row, col); } + +std::tuple Geometry::areAbsIDsFromSameTCard(int absId1, int absId2) const +{ + + int rowDiff = -100; + int colDiff = -100; + + if (absId1 == absId2) { + return {false, rowDiff, colDiff}; + } + + // Check if in same SM, if not for sure not same TCard + const int sm1 = GetSuperModuleNumber(absId1); + const int sm2 = GetSuperModuleNumber(absId2); + if (sm1 != sm2) { + return {false, rowDiff, colDiff}; + } + + // Get the column and row of each absId + const auto [_, iTower1, iIphi1, iIeta1] = GetCellIndex(absId1); + const auto [row1, col1] = GetCellPhiEtaIndexInSModule(sm1, iTower1, iIphi1, iIeta1); + + const auto [__, iTower2, iIphi2, iIeta2] = GetCellIndex(absId2); + const auto [row2, col2] = GetCellPhiEtaIndexInSModule(sm2, iTower2, iIphi2, iIeta2); + + // Define corner of TCard for absId1 + const int tcardRow0 = row1 - row1 % 8; + const int tcardCol0 = col1 - col1 % 2; + + // Difference of absId2 from corner of absId1's TCard + const int rowOffset = row2 - tcardRow0; + const int colOffset = col2 - tcardCol0; + + // Differences between the two cells directly + rowDiff = row1 - row2; + colDiff = col1 - col2; + + const bool sameTCard = (rowOffset >= 0 && rowOffset < 8 && + colOffset >= 0 && colOffset < 2); + return {sameTCard, rowDiff, colDiff}; +} diff --git a/Detectors/EMCAL/base/src/Hit.cxx b/Detectors/EMCAL/base/src/Hit.cxx index 39edba54e6b12..898d86dc2e2c4 100644 --- a/Detectors/EMCAL/base/src/Hit.cxx +++ b/Detectors/EMCAL/base/src/Hit.cxx @@ -32,7 +32,7 @@ Bool_t Hit::operator<(const Hit& rhs) const Bool_t Hit::operator==(const Hit& rhs) const { - return (GetDetectorID() == GetDetectorID()) && (GetTrackID() == rhs.GetTrackID()); + return (GetDetectorID() == rhs.GetDetectorID()) && (GetTrackID() == rhs.GetTrackID()); } Hit& Hit::operator+=(const Hit& rhs) @@ -41,14 +41,14 @@ Hit& Hit::operator+=(const Hit& rhs) return *this; } -Hit Hit::operator+(const Hit& rhs) const +Hit o2::emcal::operator+(const Hit& lhs, const Hit& rhs) { - Hit result(*this); + Hit result(lhs); result.SetEnergyLoss(result.GetEnergyLoss() + rhs.GetEnergyLoss()); - return *this; + return result; } -std::ostream& operator<<(std::ostream& stream, const Hit& p) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const Hit& p) { p.PrintStream(stream); return stream; diff --git a/Detectors/EMCAL/base/src/RCUTrailer.cxx b/Detectors/EMCAL/base/src/RCUTrailer.cxx index 770f2857ac86a..24aac7b5bb25a 100644 --- a/Detectors/EMCAL/base/src/RCUTrailer.cxx +++ b/Detectors/EMCAL/base/src/RCUTrailer.cxx @@ -14,6 +14,7 @@ #include #include "CommonConstants/LHCConstants.h" #include "EMCALBase/RCUTrailer.h" +#include using namespace o2::emcal; @@ -34,6 +35,29 @@ void RCUTrailer::reset() mIsInitialized = false; } +bool RCUTrailer::checkLastTrailerWord(uint32_t trailerword) +{ + const int MIN_FWVERSION = 2; + const int MAX_FWVERSION = 2; + if ((trailerword >> 30) != 3) { + return false; + } + auto firmwarevesion = (trailerword >> 16) & 0xFF; + auto trailerSize = (trailerword & 0x7F); + if (firmwarevesion < MIN_FWVERSION || firmwarevesion > MAX_FWVERSION) { + return false; + } + if (trailerSize < 2) { + return false; + } + if (firmwarevesion == 2) { + if (trailerSize < 9) { + return false; + } + } + return true; +} + void RCUTrailer::constructFromRawPayload(const gsl::span payloadwords) { reset(); @@ -62,7 +86,7 @@ void RCUTrailer::constructFromRawPayload(const gsl::span payload foundTrailerWords++; int parCode = (word >> 26) & 0xF; int parData = word & 0x3FFFFFF; - // std::cout << "Found trailer word 0x" << std::hex << word << "(Par code: " << std::dec << parCode << ", Par data: 0x" << std::hex << parData << std::dec << ")"; + // std::cout << "Found trailer word 0x" << std::hex << word << "(Par code: " << std::dec << parCode << ", Par data: 0x" << std::hex << parData << std::dec << ")" << std::endl; switch (parCode) { case 1: // ERR_REG1 @@ -94,7 +118,8 @@ void RCUTrailer::constructFromRawPayload(const gsl::span payload mAltroConfig.mWord2 = parData & 0x1FFFFFF; break; default: - std::cerr << "Undefined parameter code " << parCode << ", ignore it !\n"; + LOG(warning) << "RCU trailer: Undefined parameter code " << parCode << " in word " << index << " (0x" << std::hex << word << std::dec << "), ignoring word"; + mWordCorruptions++; break; } } @@ -147,7 +172,7 @@ void RCUTrailer::setTimeSamplePhaseNS(uint64_t triggertime, uint64_t timesample) sample = 2; break; default: - throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format("invalid time sample: {:f}", timesample).data()); + throw Error(Error::ErrorType_t::SAMPLINGFREQ_INVALID, fmt::format(fmt::runtime("invalid time sample: {:f}"), timesample).data()); }; mAltroConfig.mSampleTime = sample; // calculate L1 phase @@ -226,8 +251,8 @@ void RCUTrailer::printStream(std::ostream& stream) const << "Sparse readout: " << (isSparseReadout() ? "yes" : "no") << "\n" << "AltroCFG1: 0x" << std::hex << mAltroConfig.mWord1 << "\n" << "AltroCFG2: 0x" << std::hex << mAltroConfig.mWord2 << "\n" - << "Sampling time: " << timesample << " ns\n" - << "L1 Phase: " << l1phase << " ns\n" + << "Sampling time: " << std::dec << timesample << " ns\n" + << "L1 Phase: " << std::dec << l1phase << " ns (" << mAltroConfig.mL1Phase << ")\n" << std::dec << std::fixed; if (errors.size()) { stream << "Errors: \n" @@ -250,4 +275,4 @@ std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RCUTr { trailer.printStream(stream); return stream; -} \ No newline at end of file +} diff --git a/Detectors/EMCAL/base/src/TriggerMappingV2.cxx b/Detectors/EMCAL/base/src/TriggerMappingV2.cxx index 5d5ee46dc6f2f..e60d7b31e5305 100644 --- a/Detectors/EMCAL/base/src/TriggerMappingV2.cxx +++ b/Detectors/EMCAL/base/src/TriggerMappingV2.cxx @@ -390,7 +390,7 @@ TriggerMappingV2::IndexTRU TriggerMappingV2::getTRUIndexFromOnlineHardareAddree( unsigned short branch = (hardwareAddress >> 11) & 0x1; // 0/1 - IndexTRU truIndex = ((ddlID << 1) | branch) - 1; // 0..2 + IndexTRU truIndex = (((ddlID % 2) << 1) | branch) - 1; // 0..2 truIndex = (supermoduleID % 2) ? 2 - truIndex : truIndex; diff --git a/Detectors/EMCAL/base/test/testGeometry.cxx b/Detectors/EMCAL/base/test/testGeometry.cxx new file mode 100644 index 0000000000000..f297410b6959f --- /dev/null +++ b/Detectors/EMCAL/base/test/testGeometry.cxx @@ -0,0 +1,66 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#define BOOST_TEST_MODULE Test EMCAL Base +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include "EMCALBase/Geometry.h" +#include +#include + +std::tuple GetRefCellIndex(int CellId); + +/// \macro Test implementation of the EMCAL Geometry +/// +/// Test coverage: +/// - GetCellIndex (get #sm, #mod, phi index and eta index): all cells (0-17663) +/// - Invalid CellId: exception test for cell -1 and 17664 +BOOST_AUTO_TEST_CASE(Geometry_test) +{ + auto testgeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); + + // Check GetCellIndex function for all valid cells by comparing to GetRefCellIndex function + for (int iCell = 0; iCell < 17664; iCell++) { + auto [smod, mod, iphi, ieta] = testgeometry->GetCellIndex(iCell); + auto [smod_ref, mod_ref, iphi_ref, ieta_ref] = GetRefCellIndex(iCell); + BOOST_CHECK_EQUAL(smod, smod_ref); + BOOST_CHECK_EQUAL(mod, mod_ref); + BOOST_CHECK_EQUAL(iphi, iphi_ref); + BOOST_CHECK_EQUAL(ieta, ieta_ref); + } // And then check the exeptions of -1 and 17664 + BOOST_CHECK_EXCEPTION(testgeometry->GetCellIndex(-1), o2::emcal::InvalidCellIDException, [](o2::emcal::InvalidCellIDException const& mCellID) { return -1; }); + BOOST_CHECK_EXCEPTION(testgeometry->GetCellIndex(17664), o2::emcal::InvalidCellIDException, [](o2::emcal::InvalidCellIDException const& mCellID) { return 17664; }); +} + +std::tuple GetRefCellIndex(int CellId) +{ + // Four cells per module: + int ieta = CellId % 2; // cells 0 and 2 (in each module) have eta index 0 + int iphi = (CellId % 4 == 2 || CellId % 4 == 3) ? 1 : 0; // cells 0 and 1 (in each module) have phi index 0 + + int smod = 0, mod = 0; // Super module number and module number + if (CellId >= 0 && CellId < 11520) { // The first 10 super modules are full modules + smod = CellId / 1152; // Their number is their cell number divided by the cells per sm (rounded down) + mod = (CellId % 1152) / 4; // And the module is the cell number within the sm (%) divided by four (four cells in one module) + } else if (CellId >= 11520 && CellId < 12288) { // First two one thirds + smod = 10 + (CellId - 11520) / 384; // +10 to account for the - 11520 + mod = ((CellId - 11520) % 384) / 4; // -11520 to subtract all cells in full super modules + } else if (CellId >= 12288 && CellId < 16896) { // Six two third modules + smod = 12 + (CellId - 12288) / 768; + mod = ((CellId - 12288) % 768) / 4; + } else if (CellId >= 16896 && CellId < 17664) { // Second two one third modules + smod = 18 + (CellId - 16896) / 384; + mod = ((CellId - 16896) % 384) / 4; + } + + return std::make_tuple(smod, mod, iphi, ieta); +} \ No newline at end of file diff --git a/Detectors/EMCAL/calib/CMakeLists.txt b/Detectors/EMCAL/calib/CMakeLists.txt index c9c70d88c9b6b..a8ca8d9505318 100644 --- a/Detectors/EMCAL/calib/CMakeLists.txt +++ b/Detectors/EMCAL/calib/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(EMCALCalib src/TempCalibrationParams.cxx src/TempCalibParamSM.cxx src/GainCalibrationFactors.cxx + src/Pedestal.cxx src/TriggerTRUDCS.cxx src/TriggerSTUDCS.cxx src/TriggerSTUErrorCounter.cxx @@ -37,6 +38,7 @@ o2_target_root_dictionary(EMCALCalib include/EMCALCalib/TempCalibrationParams.h include/EMCALCalib/TempCalibParamSM.h include/EMCALCalib/GainCalibrationFactors.h + include/EMCALCalib/Pedestal.h include/EMCALCalib/TriggerTRUDCS.h include/EMCALCalib/TriggerSTUDCS.h include/EMCALCalib/TriggerSTUErrorCounter.h @@ -90,6 +92,13 @@ o2_add_test(GainCalibrationFactors LABELS emcal ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) +o2_add_test(Pedestal + SOURCES test/testPedestal.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALCalib + COMPONENT_NAME emcal + LABELS emcal + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) + o2_add_test(TriggerTRUDCS SOURCES test/testTriggerTRUDCS.cxx PUBLIC_LINK_LIBRARIES O2::EMCALCalib diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h b/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h index 4c5bb5ea98713..d4fc0329c0bf5 100644 --- a/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h +++ b/Detectors/EMCAL/calib/include/EMCALCalib/CalibDB.h @@ -8,12 +8,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 ALICEO2_EMCAL_CALIBDB +#define ALICEO2_EMCAL_CALIBDB + #include #include #include #include "Rtypes.h" #include "RStringView.h" #include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" namespace o2 { @@ -30,6 +35,7 @@ class GainCalibrationFactors; class EMCALChannelScaleFactors; class FeeDCS; class ElmbData; +class Pedestal; /// \class CalibDB /// \brief Interface to calibration data from CCDB for EMCAL @@ -44,6 +50,7 @@ class ElmbData; /// - Time calibration /// - Gain calibration /// - Temperature calibration +/// - Pedestals /// Users only need to specify the CCDB server, the timestamp and /// (optionally) additional meta data. Handling of the CCDB path /// and type conversions is done internally - users deal directly @@ -59,6 +66,9 @@ class ElmbData; /// Users must handle the exceptions. class CalibDB { + + using CcdbManager = o2::ccdb::BasicCCDBManager; + public: /// \class ObjectNotFoundException /// \brief Handling errors due to objects not found in the CCDB @@ -293,6 +303,20 @@ class CalibDB /// \throw TypeMismatchException if object is present but type is different (CCDB corrupted) ElmbData* readTemperatureSensorData(ULong_t timestamp, const std::map& metadata); + /// \brief Store pedestal data in the CCDB + /// \param pedestals Pedestal data to be stored + /// \param metadata Additional metadata that can be used in the query + /// \param timestart Start of the time range of the validity of the object + /// \param timeend End of the time range of the validity of the object + void storePedestalData(Pedestal* pedestals, const std::map& metadata, ULong_t timestart, ULong_t timeend); + + /// \brief Find pedestal data in the CCDB for given timestamp + /// \param timestamp Timestamp used in query + /// \param metadata Additional metadata to be used in the query + /// \throw ObjectNotFoundException if object is not found for the given timestamp + /// \throw TypeMismatchException if object is present but type is different (CCDB corrupted) + Pedestal* readPedestalData(ULong_t timestamp, const std::map& metadata); + /// \brief Set new CCDB server URL /// \param server Name of the CCDB server to be used in queries /// @@ -341,6 +365,10 @@ class CalibDB /// \return Path of the scale factors used in the bad channel calibration in the CCDB static const char* getCDBPathChannelScaleFactors() { return "EMC/Config/ChannelScaleFactors"; } + /// \brief Get CCDB path for the pedestal data + /// \return Path of the pedestal data + static const char* getCDBPathChannelPedestals() { return "EMC/Calib/Pedestal"; } + private: /// \brief Initialize CCDB server (when new object is created or the server URL changes) void @@ -355,3 +383,5 @@ class CalibDB } // namespace emcal } // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h b/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h index 571b43d05ef08..ea8a0445bbe5e 100644 --- a/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h +++ b/Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h @@ -62,7 +62,7 @@ class CellRecalibrator public: /// \class CellTypeException /// \brief Handling of invalid cell types in calibration - class CellTypeException : public std::exception + class CellTypeException final : public std::exception { public: /// \brief Constructor @@ -73,7 +73,7 @@ class CellRecalibrator /// \brief Get error message of the exception /// \return Error message - const char* what() const noexcept final + [[nodiscard]] char const* what() const noexcept final { return "Only possible to calibrate cells of type high gain or low gain"; } @@ -208,4 +208,4 @@ std::ostream& operator<<(std::ostream& in, const CellRecalibrator& calib); } // namespace o2 -#endif // !ALCEO2_EMCAL_CELLRECALIBRATOR_H \ No newline at end of file +#endif // !ALCEO2_EMCAL_CELLRECALIBRATOR_H diff --git a/Detectors/EMCAL/calib/include/EMCALCalib/Pedestal.h b/Detectors/EMCAL/calib/include/EMCALCalib/Pedestal.h new file mode 100644 index 0000000000000..7a8983409d995 --- /dev/null +++ b/Detectors/EMCAL/calib/include/EMCALCalib/Pedestal.h @@ -0,0 +1,96 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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_PEDESTAL_H +#define ALICEO2_EMCAL_PEDESTAL_H + +#include +#include +#include + +class TH1; +class TH2; + +namespace o2 +{ + +namespace emcal +{ + +/// \class Pedestal +/// \brief CCDB container for pedestal values +/// \ingroup EMCALcalib +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since July 16th, 2019 +/// +/// Pedestal values can be added for each channel by +/// ~~~.{cxx} +/// o2::emcal::Pedestal ped; +/// ped.addPedestalValue(23, 3, false); +/// ~~~ +/// For the High Gain cells the last parameter should be set to false, for low +/// gain it should be set to true. +/// +/// One can read the pedestal values by calling +/// ~~~.{cxx} +/// auto param = ped.getPedestalValue(23, false); +/// ~~~ +/// This will return the pedestal value for a certain HG cell. +/// For low gain cells you have to set the last parameter false +class Pedestal +{ + public: + /// \brief Constructor + Pedestal() = default; + + /// \brief Destructor + ~Pedestal() = default; + + /// \brief Comparison of two pedestal containers + /// \return true if the two list of pedestal values are the same, false otherwise + bool operator==(const Pedestal& other) const; + + /// \brief Add pedestal to the container + /// \param cellID Absolute ID of cell + /// \param isLowGain Cell type is low gain cell + /// \param pedestal Pedestal value + /// \throw CalibContainerIndexException in case the cell ID exceeds the range of cells in EMCAL + void addPedestalValue(unsigned short cellID, short pedestal, bool isLowGain, bool isLEDMON); + + /// \brief Get the time calibration coefficient for a certain cell + /// \param cellID Absolute ID of cell + /// \param isLowGain Cell type is low gain cell + /// \return Pedestal value of the cell + /// \throw CalibContainerIndexException in case the cell ID exceeds the range of cells in EMCAL + short getPedestalValue(unsigned short cellID, bool isLowGain, bool isLEDMON) const; + + /// \brief Convert the pedestal container to a histogram + /// \param isLowGain Monitor low gain cells + /// \return Histogram representation of the pedestal container + TH1* getHistogramRepresentation(bool isLowGain, bool isLEDMON) const; + + /// \brief Convert the pedestal container to a 2D histogram + /// \param isLowGain Monitor low gain cells + /// \return 2D Histogram representation (heatmap with respect to column and row) of the pedestal container + TH2* getHistogramRepresentation2D(bool isLowGain, bool isLEDMON) const; + + private: + std::array mPedestalValuesHG; ///< Container for the pedestal values (high gain) + std::array mPedestalValuesLG; ///< Container for the pedestal values (low gain) + std::array mPedestalValuesLEDMONHG; ///< Container for the LEDMON pedestal values (high gain) + std::array mPedestalValuesLEDMONLG; ///< Container for the LEDMON pedestal values (low gain) + + ClassDefNV(Pedestal, 1); +}; + +} // namespace emcal + +} // namespace o2 +#endif diff --git a/Detectors/EMCAL/calib/src/BadChannelMap.cxx b/Detectors/EMCAL/calib/src/BadChannelMap.cxx index 189ea4beb5226..03476b3a7508f 100644 --- a/Detectors/EMCAL/calib/src/BadChannelMap.cxx +++ b/Detectors/EMCAL/calib/src/BadChannelMap.cxx @@ -72,25 +72,24 @@ TH2* BadChannelMap::getHistogramRepresentation() const MAXCOLS = 96; auto hist = new TH2S("badchannelmap", "Bad Channel Map", MAXCOLS, -0.5, double(MAXCOLS) - 0.5, MAXROWS, -0.5, double(MAXROWS) - 0.5); hist->SetDirectory(nullptr); - auto geo = Geometry::GetInstance(); - if (!geo) { - LOG(error) << "Geometry needs to be initialized"; - return hist; - } - - for (size_t cellID = 0; cellID < mBadCells.size(); cellID++) { - int value(0); - if (mBadCells.test(cellID)) { - value = 1; - } else if (mDeadCells.test(cellID)) { - value = 2; - } 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); + try { + auto geo = Geometry::GetInstance(); + for (size_t cellID = 0; cellID < mBadCells.size(); cellID++) { + int value(0); + if (mBadCells.test(cellID)) { + value = 1; + } else if (mDeadCells.test(cellID)) { + value = 2; + } 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); + } } + } catch (o2::emcal::GeometryNotInitializedException& e) { + LOG(error) << "Geometry needs to be initialized"; } return hist; } diff --git a/Detectors/EMCAL/calib/src/CalibDB.cxx b/Detectors/EMCAL/calib/src/CalibDB.cxx index 0726f923e0150..44dcf728b598c 100644 --- a/Detectors/EMCAL/calib/src/CalibDB.cxx +++ b/Detectors/EMCAL/calib/src/CalibDB.cxx @@ -19,6 +19,7 @@ #include "EMCALCalib/FeeDCS.h" #include "EMCALCalib/CalibDB.h" #include "EMCALCalib/ElmbData.h" +#include "EMCALCalib/Pedestal.h" using namespace o2::emcal; @@ -30,6 +31,8 @@ CalibDB::CalibDB(const std::string_view server) : CalibDB() void CalibDB::init() { mCCDBManager.init(mCCDBServer); + auto& mgr = CcdbManager::instance(); + mgr.setURL(mCCDBServer); mInit = true; } @@ -97,12 +100,21 @@ void CalibDB::storeTemperatureSensorData(ElmbData* dcs, const std::map& metadata, ULong_t rangestart, ULong_t rangeend) +{ + if (!mInit) { + init(); + } + mCCDBManager.storeAsTFileAny(pedestals, getCDBPathTemperatureSensor(), metadata, rangestart, rangeend); +} + BadChannelMap* CalibDB::readBadChannelMap(ULong_t timestamp, const std::map& metadata) { if (!mInit) { init(); } - BadChannelMap* result = mCCDBManager.retrieveFromTFileAny(getCDBPathBadChannelMap(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + BadChannelMap* result = mgr.getForTimeStamp(getCDBPathBadChannelMap(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathBadChannelMap(), metadata, timestamp); } @@ -114,7 +126,8 @@ TimeCalibrationParams* CalibDB::readTimeCalibParam(ULong_t timestamp, const std: if (!mInit) { init(); } - TimeCalibrationParams* result = mCCDBManager.retrieveFromTFileAny(getCDBPathTimeCalibrationParams(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + TimeCalibrationParams* result = mgr.getForTimeStamp(getCDBPathTimeCalibrationParams(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathTimeCalibrationParams(), metadata, timestamp); } @@ -126,7 +139,8 @@ TimeCalibParamL1Phase* CalibDB::readTimeCalibParamL1Phase(ULong_t timestamp, con if (!mInit) { init(); } - TimeCalibParamL1Phase* result = mCCDBManager.retrieveFromTFileAny(getCDBPathL1Phase(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + TimeCalibParamL1Phase* result = mgr.getForTimeStamp(getCDBPathL1Phase(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathL1Phase(), metadata, timestamp); } @@ -138,7 +152,8 @@ TempCalibrationParams* CalibDB::readTempCalibParam(ULong_t timestamp, const std: if (!mInit) { init(); } - TempCalibrationParams* result = mCCDBManager.retrieveFromTFileAny(getCDBPathTemperatureCalibrationParams(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + TempCalibrationParams* result = mgr.getForTimeStamp(getCDBPathTemperatureCalibrationParams(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathTemperatureCalibrationParams(), metadata, timestamp); } @@ -150,7 +165,8 @@ TempCalibParamSM* CalibDB::readTempCalibParamSM(ULong_t timestamp, const std::ma if (!mInit) { init(); } - TempCalibParamSM* result = mCCDBManager.retrieveFromTFileAny(getCDBPathTemperatureCalibrationParamsSM(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + TempCalibParamSM* result = mgr.getForTimeStamp(getCDBPathTemperatureCalibrationParamsSM(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathTemperatureCalibrationParamsSM(), metadata, timestamp); } @@ -162,7 +178,8 @@ GainCalibrationFactors* CalibDB::readGainCalibFactors(ULong_t timestamp, const s if (!mInit) { init(); } - GainCalibrationFactors* result = mCCDBManager.retrieveFromTFileAny(getCDBPathGainCalibrationParams(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + GainCalibrationFactors* result = mgr.getForTimeStamp(getCDBPathGainCalibrationParams(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathGainCalibrationParams(), metadata, timestamp); } @@ -174,7 +191,8 @@ EMCALChannelScaleFactors* CalibDB::readChannelScaleFactors(ULong_t timestamp, co if (!mInit) { init(); } - EMCALChannelScaleFactors* result = mCCDBManager.retrieveFromTFileAny(getCDBPathChannelScaleFactors(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + EMCALChannelScaleFactors* result = mgr.getForTimeStamp(getCDBPathChannelScaleFactors(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathChannelScaleFactors(), metadata, timestamp); } @@ -186,7 +204,8 @@ FeeDCS* CalibDB::readFeeDCSData(ULong_t timestamp, const std::map(getCDBPathFeeDCS(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + FeeDCS* result = mgr.getForTimeStamp(getCDBPathFeeDCS(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathFeeDCS(), metadata, timestamp); } @@ -198,9 +217,23 @@ ElmbData* CalibDB::readTemperatureSensorData(ULong_t timestamp, const std::map(getCDBPathTemperatureSensor(), metadata, timestamp); + auto& mgr = CcdbManager::instance(); + ElmbData* result = mgr.getForTimeStamp(getCDBPathTemperatureSensor(), timestamp); if (!result) { throw ObjectNotFoundException(mCCDBServer, getCDBPathTemperatureSensor(), metadata, timestamp); } return result; } + +Pedestal* CalibDB::readPedestalData(ULong_t timestamp, const std::map& metadata) +{ + if (!mInit) { + init(); + } + auto& mgr = CcdbManager::instance(); + Pedestal* result = mgr.getForTimeStamp(getCDBPathChannelPedestals(), timestamp); + if (!result) { + throw ObjectNotFoundException(mCCDBServer, getCDBPathChannelPedestals(), metadata, timestamp); + } + return result; +} \ No newline at end of file diff --git a/Detectors/EMCAL/calib/src/EMCALCalibLinkDef.h b/Detectors/EMCAL/calib/src/EMCALCalibLinkDef.h index 8fae66b9558e7..7d2328a6f80aa 100644 --- a/Detectors/EMCAL/calib/src/EMCALCalibLinkDef.h +++ b/Detectors/EMCAL/calib/src/EMCALCalibLinkDef.h @@ -18,10 +18,12 @@ #pragma link C++ class o2::emcal::CalibDB + ; #pragma link C++ class o2::emcal::BadChannelMap + ; #pragma link C++ class o2::emcal::TimeCalibrationParams + ; +#pragma link C++ class o2::emcal::TimeCalibrationSlewingParams + ; #pragma link C++ class o2::emcal::TimeCalibParamL1Phase + ; #pragma link C++ class o2::emcal::TempCalibrationParams + ; #pragma link C++ class o2::emcal::TempCalibParamSM + ; #pragma link C++ class o2::emcal::GainCalibrationFactors + ; +#pragma link C++ class o2::emcal::Pedestal + ; #pragma link C++ class o2::emcal::TriggerTRUDCS + ; #pragma link C++ class o2::emcal::TriggerSTUDCS + ; #pragma link C++ class o2::emcal::TriggerSTUErrorCounter + ; diff --git a/Detectors/EMCAL/calib/src/Pedestal.cxx b/Detectors/EMCAL/calib/src/Pedestal.cxx new file mode 100644 index 0000000000000..7d67205fc7943 --- /dev/null +++ b/Detectors/EMCAL/calib/src/Pedestal.cxx @@ -0,0 +1,153 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "EMCALBase/Geometry.h" +#include "EMCALCalib/CalibContainerErrors.h" +#include "EMCALCalib/Pedestal.h" + +#include + +#include +#include + +#include + +#include + +using namespace o2::emcal; + +void Pedestal::addPedestalValue(unsigned short cellID, short pedestal, bool isLowGain, bool isLEDMON) +{ + if (cellID >= mPedestalValuesHG.size()) { + throw CalibContainerIndexException(cellID); + } + if (isLEDMON) { + if (isLowGain) { + mPedestalValuesLEDMONLG[cellID] = pedestal; + } else { + mPedestalValuesLEDMONHG[cellID] = pedestal; + } + } else { + if (isLowGain) { + mPedestalValuesLG[cellID] = pedestal; + } else { + mPedestalValuesHG[cellID] = pedestal; + } + } +} + +short Pedestal::getPedestalValue(unsigned short cellID, bool isLowGain, bool isLEDMON) const +{ + if (cellID >= mPedestalValuesHG.size()) { + throw CalibContainerIndexException(cellID); + } + if (isLEDMON) { + if (isLowGain) { + return mPedestalValuesLEDMONLG[cellID]; + } else { + return mPedestalValuesLEDMONHG[cellID]; + } + } else { + if (isLowGain) { + return mPedestalValuesLG[cellID]; + } else { + return mPedestalValuesHG[cellID]; + } + } +} + +TH1* Pedestal::getHistogramRepresentation(bool isLowGain, bool isLEDMON) const +{ + gsl::span data; + std::string histname, histtitle; + if (isLEDMON) { + if (isLowGain) { + histname = "PedestalLG_LEDMON"; + histtitle = "LEDMON Pedestal values Params low Gain"; + data = gsl::span(mPedestalValuesLEDMONLG); + } else { + histname = "PedestalHG_LEDMON"; + histtitle = "LEDMON Pedestal values Params high Gain"; + data = gsl::span(mPedestalValuesLEDMONHG); + } + } else { + if (isLowGain) { + histname = "PedestalLG"; + histtitle = "Pedestal values Params low Gain"; + data = gsl::span(mPedestalValuesLG); + } else { + histname = "PedestalHG"; + histtitle = "Pedestal values Params high Gain"; + data = gsl::span(mPedestalValuesHG); + } + } + + auto hist = new TH1S(histname.data(), histtitle.data(), data.size(), -0.5, data.size() - 0.5); + hist->SetDirectory(nullptr); + for (std::size_t icell{0}; icell < data.size(); ++icell) { + hist->SetBinContent(icell + 1, data[icell]); + } + return hist; +} + +TH2* Pedestal::getHistogramRepresentation2D(bool isLowGain, bool isLEDMON) const +{ + gsl::span data; + std::string histname, histtitle; + if (isLEDMON) { + if (isLowGain) { + histname = "PedestalLG_LEDMON"; + histtitle = "LEDMON Pedestal values Params low Gain"; + data = gsl::span(mPedestalValuesLEDMONLG); + } else { + histname = "PedestalHG_LEDMON"; + histtitle = "LEDMON Pedestal values Params high Gain"; + data = gsl::span(mPedestalValuesLEDMONHG); + } + } else { + if (isLowGain) { + histname = "PedestalLG"; + histtitle = "Pedestal values Params low Gain"; + data = gsl::span(mPedestalValuesLG); + } else { + histname = "PedestalHG"; + histtitle = "Pedestal values Params high Gain"; + data = gsl::span(mPedestalValuesHG); + } + } + + const int MAXROWS = isLEDMON ? 10 : 208, + MAXCOLS = isLEDMON ? 48 : 96; + + auto hist = new TH2S(histname.data(), histtitle.data(), MAXCOLS, -0.5, double(MAXCOLS) - 0.5, MAXROWS, -0.5, double(MAXROWS) - 0.5); + hist->SetDirectory(nullptr); + try { + auto geo = Geometry::GetInstance(); + for (size_t ichan = 0; ichan < data.size(); ichan++) { + if (isLEDMON) { + int col = ichan % 48, + row = ichan / 48; + hist->Fill(col, row, data[ichan]); + } else { + auto position = geo->GlobalRowColFromIndex(ichan); + hist->Fill(std::get<1>(position), std::get<0>(position), data[ichan]); + } + } + } catch (o2::emcal::GeometryNotInitializedException& e) { + LOG(error) << "Geometry needs to be initialized"; + } + return hist; +} + +bool Pedestal::operator==(const Pedestal& other) const +{ + return mPedestalValuesHG == other.mPedestalValuesHG && mPedestalValuesLG == other.mPedestalValuesLG && mPedestalValuesLEDMONHG == other.mPedestalValuesLEDMONHG && mPedestalValuesLEDMONLG == other.mPedestalValuesLEDMONLG; +} diff --git a/Detectors/EMCAL/calib/test/testPedestal.cxx b/Detectors/EMCAL/calib/test/testPedestal.cxx new file mode 100644 index 0000000000000..1adcfd8b9ea25 --- /dev/null +++ b/Detectors/EMCAL/calib/test/testPedestal.cxx @@ -0,0 +1,124 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test_EMCAL_Calib +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include +#include "EMCALCalib/CalibContainerErrors.h" +#include "EMCALCalib/Pedestal.h" + +/// \brief Standard tests for Pedestal container + +namespace o2 +{ +namespace emcal +{ + +using pedestalarray = std::vector; + +pedestalarray createRandomPedestals(bool isLEDMON) +{ + std::random_device rd{}; + std::mt19937 gen{rd()}; + std::normal_distribution gaussrand{40., 5.}; + pedestalarray pedestalcontainer(isLEDMON ? 480 : 17664); + for (std::size_t ichan{0}; ichan < pedestalcontainer.size(); ++ichan) { + pedestalcontainer[ichan] = std::round(gaussrand(gen)); + } + + return pedestalcontainer; +} + +pedestalarray shiftPedestalValue(const pedestalarray& input, short shift = 1) +{ + pedestalarray shifted; + for (std::size_t ichan{0}; ichan < input.size(); ++ichan) { + shifted[ichan] = input[ichan] + shift; + } + return shifted; +} + +BOOST_AUTO_TEST_CASE(testPedestal) +{ + auto pedestalsHG = createRandomPedestals(false), + pedestalsLG = createRandomPedestals(false), + pedestalsLEDMONHG = createRandomPedestals(true), + pedestalsLEDMONLG = createRandomPedestals(true); + + o2::emcal::Pedestal pedestalObject; + for (std::size_t ichan{0}; ichan < pedestalsHG.size(); ++ichan) { + pedestalObject.addPedestalValue(ichan, pedestalsHG[ichan], false, false); + pedestalObject.addPedestalValue(ichan, pedestalsLG[ichan], true, false); + } + for (std::size_t ichan{0}; ichan < pedestalsLEDMONHG.size(); ++ichan) { + pedestalObject.addPedestalValue(ichan, pedestalsLEDMONHG[ichan], false, true); + pedestalObject.addPedestalValue(ichan, pedestalsLEDMONLG[ichan], true, true); + } + + // test adding entries beyond range + for (std::size_t ichan{17665}; ichan < 18000; ++ichan) { + BOOST_CHECK_EXCEPTION(pedestalObject.addPedestalValue(ichan, 2, false, true), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + BOOST_CHECK_EXCEPTION(pedestalObject.addPedestalValue(ichan, 3, true, false), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + } + + // test reading values in range + for (std::size_t ichan{0}; ichan < pedestalsHG.size(); ++ichan) { + BOOST_CHECK_EQUAL(pedestalObject.getPedestalValue(ichan, false, false), pedestalsHG[ichan]); + BOOST_CHECK_EQUAL(pedestalObject.getPedestalValue(ichan, true, false), pedestalsLG[ichan]); + } + for (std::size_t ichan{0}; ichan < pedestalsLEDMONHG.size(); ++ichan) { + BOOST_CHECK_EQUAL(pedestalObject.getPedestalValue(ichan, false, true), pedestalsLEDMONHG[ichan]); + BOOST_CHECK_EQUAL(pedestalObject.getPedestalValue(ichan, true, true), pedestalsLEDMONLG[ichan]); + } + + // test reading entries beyond range + for (std::size_t ichan{17665}; ichan < 18000; ++ichan) { + BOOST_CHECK_EXCEPTION(pedestalObject.getPedestalValue(ichan, false, false), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + BOOST_CHECK_EXCEPTION(pedestalObject.getPedestalValue(ichan, true, false), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + BOOST_CHECK_EXCEPTION(pedestalObject.getPedestalValue(ichan, false, true), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + BOOST_CHECK_EXCEPTION(pedestalObject.getPedestalValue(ichan, true, true), o2::emcal::CalibContainerIndexException, [ichan](const o2::emcal::CalibContainerIndexException& e) { return e.getIndex() == ichan; }); + } + + // tests for operator== + // shift pedestal by 1 for false test + // Test all cases: + // - same object + // - same HG, different LG + // - same LG, different HG + // - both HG and LG different + /* + auto shiftedPedestalsHG = shiftPedestalValue(pedestalsHG, 1), + shiftedPedestalsLG = shiftPedestalValue(pedestalsLG, 1); + o2::emcal::Pedestal same, differLow, differHigh, differBoth; + for (std::size_t ichan{0}; ichan < pedestalsHG.size(); ++ichan) { + same.addPedestalValue(ichan, pedestalsHG[ichan], false); + same.addPedestalValue(ichan, pedestalsLG[ichan], true); + differLow.addPedestalValue(ichan, pedestalsHG[ichan], false); + differLow.addPedestalValue(ichan, shiftedPedestalsLG[ichan], true); + differHigh.addPedestalValue(ichan, shiftedPedestalsHG[ichan], false); + differHigh.addPedestalValue(ichan, pedestalsLG[ichan], true); + differBoth.addPedestalValue(ichan, shiftedPedestalsHG[ichan], false); + differBoth.addPedestalValue(ichan, shiftedPedestalsLG[ichan], true); + } + BOOST_CHECK_EQUAL(pedestalObject, same); + BOOST_CHECK_NE(pedestalObject, differLow); + BOOST_CHECK_NE(pedestalObject, differHigh); + BOOST_CHECK_NE(pedestalObject, differBoth); + */ +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/CMakeLists.txt b/Detectors/EMCAL/calibration/CMakeLists.txt index 28a6d4e11029b..7fec9fcef0f93 100644 --- a/Detectors/EMCAL/calibration/CMakeLists.txt +++ b/Detectors/EMCAL/calibration/CMakeLists.txt @@ -16,11 +16,18 @@ o2_add_library(EMCALCalibration src/EMCALCalibExtractor.cxx src/EMCALCalibParams.cxx src/EMCDCSProcessor.cxx + src/EMCALPedestalHelper.cxx + src/PedestalCalibDevice.cxx + src/PedestalProcessorDevice.cxx + src/PedestalProcessorData.cxx + src/EMCALTempCalibExtractor.cxx PUBLIC_LINK_LIBRARIES O2::CCDB O2::EMCALBase O2::EMCALCalib + O2::EMCALReconstruction O2::CommonUtils O2::DetectorsCalibration O2::DetectorsDCS + O2::DetectorsRaw O2::DataFormatsEMCAL O2::Framework O2::Algorithm @@ -38,6 +45,9 @@ o2_target_root_dictionary(EMCALCalibration include/EMCALCalibration/EMCALTimeCalibData.h include/EMCALCalibration/EMCALCalibParams.h include/EMCALCalibration/EMCDCSProcessor.h + include/EMCALCalibration/EMCALPedestalHelper.h + include/EMCALCalibration/PedestalProcessorData.h + include/EMCALCalibration/EMCALTempCalibExtractor.h LINKDEF src/EMCALCalibrationLinkDef.h) o2_add_executable(emcal-channel-calib-workflow @@ -52,6 +62,22 @@ if (OpenMP_CXX_FOUND) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) endif() +o2_add_executable(emcal-pedestal-processor-workflow + COMPONENT_NAME calibration + SOURCES testWorkflow/emc-pedestal-processor-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::EMCALCalibration + O2::DetectorsRaw + O2::DetectorsCalibration) + +o2_add_executable(emcal-pedestal-calib-workflow + COMPONENT_NAME calibration + SOURCES testWorkflow/emc-pedestal-calib-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::EMCALCalibration + O2::DetectorsRaw + O2::DetectorsCalibration) + o2_add_executable(run-calib-offline COMPONENT_NAME emcal TARGETVARNAME targetName @@ -64,6 +90,13 @@ if (OpenMP_CXX_FOUND) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) endif() +o2_add_test(PedestalProcessorData + SOURCES test/testPedestalProcessorData.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALCalibration + COMPONENT_NAME emcal + LABELS emcal + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage) + o2_add_test_root_macro(macros/makeEMCALCCDBEntryForDCS.C PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB LABELS emcal COMPILE_ONLY) diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibExtractor.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibExtractor.h index 71fa389ee4213..035dc92ecfe09 100644 --- a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibExtractor.h +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibExtractor.h @@ -19,6 +19,7 @@ #define EMCALCALIBEXTRACTOR_H_ #include +#include #include #include "CCDB/BasicCCDBManager.h" #include "EMCALCalib/BadChannelMap.h" @@ -28,9 +29,12 @@ #include "CommonUtils/BoostHistogramUtils.h" #include "EMCALBase/Geometry.h" #include "EMCALCalibration/EMCALCalibParams.h" +#include "EMCALCalib/Pedestal.h" +#include "EMCALCalibration/PedestalProcessorData.h" #include #include +#include #if (defined(WITH_OPENMP) && !defined(__CLING__)) #include @@ -40,10 +44,9 @@ namespace o2 { namespace emcal { - +using boostHisto = boost::histogram::histogram, boost::histogram::axis::integer<>>, boost::histogram::unlimited_storage>>; class EMCALCalibExtractor { - using boostHisto = boost::histogram::histogram, boost::histogram::axis::integer<>>, boost::histogram::unlimited_storage>>; using slice_t = int; using cell_t = int; @@ -56,19 +59,23 @@ class EMCALCalibExtractor }; struct BadChannelCalibTimeInfo { - std::array sigmaCell; // sigma value of time distribution for single cells - double goodCellWindow; // cut value for good cells + std::array sigmaCell; // sigma value of time distribution for single cells + double goodCellWindow; // cut value for good cells + std::array fracHitsPreTrigg; // fraction of hits before the main time peak (pre-trigger pile-up) + double goodCellWindowFracHitsPreTrigg; // cut value for good cells for pre-trigger pile-up + std::array fracHitsPostTrigg; // fraction of hits after the main time peak (post-trigger pile-up) + double goodCellWindowFracHitsPostTrigg; // cut value for good cells for post-trigger pile-up }; public: EMCALCalibExtractor() { LOG(info) << "initialized EMCALCalibExtractor"; - if (!mGeometry) { + try { + // Try to access geometry initialized ountside mGeometry = o2::emcal::Geometry::GetInstance(); - if (!mGeometry) { - mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); // fallback option - } + } catch (o2::emcal::GeometryNotInitializedException& e) { + mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); // fallback option } // mNcells = mGeometry->GetNCells(); }; @@ -91,11 +98,17 @@ class EMCALCalibExtractor /// \param hist histogram cell energy vs. cell ID. Main histogram for the bad channel calibration /// \param histTime histogram cell time vs. cell ID. If default argument is taken, no calibration based on the timing signal will be performed template - o2::emcal::BadChannelMap calibrateBadChannels(boost::histogram::histogram& hist, const boost::histogram::histogram& histTime = boost::histogram::make_histogram(boost::histogram::axis::variable<>{0., 1.}, boost::histogram::axis::variable<>{0., 1.})) + o2::emcal::BadChannelMap calibrateBadChannels(const boost::histogram::histogram& hist, const boost::histogram::histogram& histTime = boost::histogram::make_histogram(boost::histogram::axis::variable<>{0., 1.}, boost::histogram::axis::variable<>{0., 1.})) { double time1 = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); std::map> slices = {{0, {0.1, 0.3}}, {1, {0.3, 0.5}}, {2, {0.5, 1.0}}, {3, {1.0, 4.0}}, {4, {4.0, 39.0}}}; + std::fill(std::begin(mBadCellFracSM), std::end(mBadCellFracSM), 0); // reset all fractions to 0 + for (unsigned int i = 0; i < mBadCellFracFEC.size(); ++i) { + std::fill(std::begin(mBadCellFracFEC[i]), std::end(mBadCellFracFEC[i]), 0); + } + + auto histScaled = hist; if (mBCMScaleFactors) { LOG(info) << "Rescaling BCM histo"; // rescale the histogram @@ -104,13 +117,13 @@ class EMCALCalibExtractor double lowerE = hist.axis(0).bin(ebin).lower(); double upperE = hist.axis(0).bin(ebin).upper(); double midE = (lowerE + upperE) / 2.; - hist.at(ebin, icell) = hist.at(ebin, icell) / mBCMScaleFactors->getScaleVal(icell, midE); + histScaled.at(ebin, icell) = hist.at(ebin, icell) / mBCMScaleFactors->getScaleVal(icell, midE); } } } // get all ofthe calibration information that we need in a struct - BadChannelCalibInfo calibrationInformation = buildHitAndEnergyMean(slices, hist); + BadChannelCalibInfo calibrationInformation = buildHitAndEnergyMean(slices, histScaled); // only initialize this if the histo is not the default one const bool doIncludeTime = (histTime.axis(0).size() > 1 && EMCALCalibParams::Instance().useTimeInfoForCalib_bc) ? true : false; @@ -138,6 +151,8 @@ class EMCALCalibExtractor if (calibrationInformation.energyPerHitMap[0][cellID] == 0) { LOG(debug) << "Cell " << cellID << " is dead."; mOutputBCM.addBadChannel(cellID, o2::emcal::BadChannelMap::MaskType_t::DEAD_CELL); + mBadCellFracSM[mGeometry->GetSuperModuleNumber(cellID)] += 1; + mBadCellFracFEC[mGeometry->GetSuperModuleNumber(cellID)][getFECNumberInSM(cellID)] += 1; } else { bool failed = false; for (auto& [sliceIndex, slice] : slices) { @@ -181,6 +196,12 @@ class EMCALCalibExtractor if (calibrationTimeInfo.sigmaCell[cellID] > calibrationTimeInfo.goodCellWindow) { LOG(debug) << "Cell " << cellID << " is flagged due to time distribution"; failed = true; + } else if (calibrationTimeInfo.fracHitsPreTrigg[cellID] > calibrationTimeInfo.goodCellWindowFracHitsPreTrigg) { + LOG(debug) << "Cell " << cellID << " is flagged due to time distribution (pre-trigger)"; + failed = true; + } else if (calibrationTimeInfo.fracHitsPostTrigg[cellID] > calibrationTimeInfo.goodCellWindowFracHitsPostTrigg) { + LOG(debug) << "Cell " << cellID << " is flagged due to time distribution (post-trigger)"; + failed = true; } } } @@ -188,12 +209,25 @@ class EMCALCalibExtractor if (failed) { LOG(debug) << "Cell " << cellID << " is bad."; mOutputBCM.addBadChannel(cellID, o2::emcal::BadChannelMap::MaskType_t::BAD_CELL); + mBadCellFracSM[mGeometry->GetSuperModuleNumber(cellID)] += 1; + mBadCellFracFEC[mGeometry->GetSuperModuleNumber(cellID)][getFECNumberInSM(cellID)] += 1; } else { LOG(debug) << "Cell " << cellID << " is good."; mOutputBCM.addBadChannel(cellID, o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL); } } } + + // Check if the fraction of bad+dead cells in a SM is above a certain threshold + // If yes, mask the whole SM + if (EMCALCalibParams::Instance().fracMaskSMFully_bc < 1) { + checkMaskSM(mOutputBCM); + } + // Same as above for FECs + if (EMCALCalibParams::Instance().fracMaskFECFully_bc < 1) { + checkMaskFEC(mOutputBCM); + } + double time2 = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); double diffTransfer = time2 - time1; LOG(info) << "Total time" << diffTransfer << " ns"; @@ -252,7 +286,6 @@ class EMCALCalibExtractor outputMapEnergyPerHit[sliceIndex][cellID] = meanVal; outputMapNHits[sliceIndex][cellID] = sumVal; } - } // end loop over the slices } // end loop over the cells for (const auto& [sliceIndex, slice] : sliceMap) { @@ -299,7 +332,7 @@ class EMCALCalibExtractor template BadChannelCalibTimeInfo buildTimeMeanAndSigma(const boost::histogram::histogram& histCellTime) { - std::array meanSigma; + BadChannelCalibTimeInfo timeInfo; for (int i = 0; i < mNcells; ++i) { // calculate sigma per cell const int indexLow = histCellTime.axis(1).index(i); @@ -311,24 +344,45 @@ class EMCALCalibExtractor maxElementIndex = 0; } float maxElementCenter = 0.5 * (boostHistCellSlice.axis(0).bin(maxElementIndex).upper() + boostHistCellSlice.axis(0).bin(maxElementIndex).lower()); - meanSigma[i] = std::sqrt(o2::utils::getVarianceBoost1D(boostHistCellSlice, -999999, maxElementCenter - 50, maxElementCenter + 50)); + timeInfo.sigmaCell[i] = std::sqrt(o2::utils::getVarianceBoost1D(boostHistCellSlice, -999999, maxElementCenter - 50, maxElementCenter + 50)); + + // get number of hits within mean+-25ns (trigger bunch), from -500ns to -25ns before trigger bunch (pre-trigger), and for 25ns to 500ns (post-trigger) + double sumTrigg = o2::utils::getIntegralBoostHist(boostHistCellSlice, maxElementCenter - 25, maxElementCenter + 25); + double sumPreTrigg = o2::utils::getIntegralBoostHist(boostHistCellSlice, maxElementCenter - 500, maxElementCenter - 25); + double sumPostTrigg = o2::utils::getIntegralBoostHist(boostHistCellSlice, maxElementCenter + 25, maxElementCenter + 500); + + // calculate fraction of hits of post and pre-trigger to main trigger bunch + timeInfo.fracHitsPreTrigg[i] = sumTrigg == 0 ? 0. : sumPreTrigg / sumTrigg; + timeInfo.fracHitsPostTrigg[i] = sumTrigg == 0 ? 0. : sumPostTrigg / sumTrigg; } // get the mean sigma and the std. deviation of the sigma distribution // those will be the values we cut on double avMean = 0, avSigma = 0; TRobustEstimator robustEstimator; - robustEstimator.EvaluateUni(meanSigma.size(), meanSigma.data(), avMean, avSigma, 0.5 * meanSigma.size()); + robustEstimator.EvaluateUni(timeInfo.sigmaCell.size(), timeInfo.sigmaCell.data(), avMean, avSigma, 0.5 * timeInfo.sigmaCell.size()); // protection for the following case: For low statistics cases, it can happen that more than half of the cells is in one bin // in that case the sigma will be close to zero. In that case, we take 95% of the data to calculate the truncated mean if (std::abs(avMean) < 0.001 && std::abs(avSigma) < 0.001) { - robustEstimator.EvaluateUni(meanSigma.size(), meanSigma.data(), avMean, avSigma, 0.95 * meanSigma.size()); + robustEstimator.EvaluateUni(timeInfo.sigmaCell.size(), timeInfo.sigmaCell.data(), avMean, avSigma, 0.95 * timeInfo.sigmaCell.size()); } - - BadChannelCalibTimeInfo timeInfo; - timeInfo.sigmaCell = meanSigma; + // timeInfo.sigmaCell = meanSigma; timeInfo.goodCellWindow = avMean + (avSigma * o2::emcal::EMCALCalibParams::Instance().sigmaTime_bc); // only upper limit needed + double avMeanPre = 0, avSigmaPre = 0; + robustEstimator.EvaluateUni(timeInfo.fracHitsPreTrigg.size(), timeInfo.fracHitsPreTrigg.data(), avMeanPre, avSigmaPre, 0.5 * timeInfo.fracHitsPreTrigg.size()); + if (std::abs(avMeanPre) < 0.001 && std::abs(avSigmaPre) < 0.001) { + robustEstimator.EvaluateUni(timeInfo.fracHitsPreTrigg.size(), timeInfo.fracHitsPreTrigg.data(), avMeanPre, avSigmaPre, 0.95 * timeInfo.fracHitsPreTrigg.size()); + } + timeInfo.goodCellWindowFracHitsPreTrigg = avMeanPre + (avSigmaPre * o2::emcal::EMCALCalibParams::Instance().sigmaTimePreTrigg_bc); // only upper limit needed + + double avMeanPost = 0, avSigmaPost = 0; + robustEstimator.EvaluateUni(timeInfo.fracHitsPostTrigg.size(), timeInfo.fracHitsPostTrigg.data(), avMeanPost, avSigmaPost, 0.5 * timeInfo.fracHitsPostTrigg.size()); + if (std::abs(avMeanPost) < 0.001 && std::abs(avSigmaPost) < 0.001) { + robustEstimator.EvaluateUni(timeInfo.fracHitsPostTrigg.size(), timeInfo.fracHitsPostTrigg.data(), avMeanPost, avSigmaPost, 0.95 * timeInfo.fracHitsPostTrigg.size()); + } + timeInfo.goodCellWindowFracHitsPostTrigg = avMeanPost + (avSigmaPost * o2::emcal::EMCALCalibParams::Instance().sigmaTimePostTrigg_bc); // only upper limit needed + return timeInfo; } @@ -340,7 +394,7 @@ class EMCALCalibExtractor /// \param maxTime -- max. time considered for fit /// \param restrictFitRangeToMax -- restrict the fit range to the maximum entry in the histogram in the range +-restrictFitRangeToMax (default: 25ns) template - o2::emcal::TimeCalibrationParams calibrateTime(boost::histogram::histogram& hist, double minTime = 0, double maxTime = 1000, double restrictFitRangeToMax = 25) + o2::emcal::TimeCalibrationParams calibrateTime(const boost::histogram::histogram& hist, double minTime = 0, double maxTime = 1000, double restrictFitRangeToMax = 25) { auto histReduced = boost::histogram::algorithm::reduce(hist, boost::histogram::algorithm::shrink(minTime, maxTime), boost::histogram::algorithm::shrink(0, mNcells)); @@ -396,13 +450,80 @@ class EMCALCalibExtractor return TCP; } - private: - EMCALChannelScaleFactors* mBCMScaleFactors = nullptr; ///< Scale factors for nentries scaling in bad channel calibration - int mSigma = 5; ///< number of sigma used in the calibration to define outliers - int mNThreads = 1; ///< number of threads used for calibration + //____________________________________________ + /// \brief Extract the pedestals from Stat Accumulators + /// \param obj PedestalProcessorData containing the data + /// \return Pedestal data + Pedestal extractPedestals(PedestalProcessorData& obj) + { + Pedestal pedestalData; + // loop over both low and high gain data as well as normal and LEDMON data + for (const auto& isLEDMON : {false, true}) { + auto maxChannels = isLEDMON ? mLEDMONs : mNcells; + for (const auto& isLG : {false, true}) { + for (unsigned short iCell = 0; iCell < maxChannels; ++iCell) { + auto [mean, rms] = obj.getValue(iCell, isLG, isLEDMON); // get mean and rms for pedestals + if (rms > EMCALCalibParams::Instance().maxPedestalRMS) { + mean = mMaxPedestalVal; + } + pedestalData.addPedestalValue(iCell, std::round(mean), isLG, isLEDMON); + } + } + } + return pedestalData; + } - o2::emcal::Geometry* mGeometry = nullptr; - static constexpr int mNcells = 17664; + //____________________________________________ + /// \brief Extract the pedestals from TProfile (for old data) + /// \param objHG TProfile containing the HG data + /// \param objLHG TProfile containing the LG data + /// \param isLEDMON if true, data is LED data + /// \return Pedestal data + Pedestal extractPedestals(TProfile* objHG = nullptr, TProfile* objLG = nullptr, bool isLEDMON = false) + { + Pedestal pedestalData; + auto maxChannels = isLEDMON ? mLEDMONs : mNcells; + // loop over both low and high gain data + for (const auto& isLG : {false, true}) { + auto obj = (isLG == true ? objLG : objHG); + if (!obj) + continue; + for (unsigned short iCell = 0; iCell < maxChannels; ++iCell) { + short mean = static_cast(std::round(obj->GetBinContent(iCell + 1))); + short rms = static_cast(obj->GetBinError(iCell + 1) / obj->GetBinEntries(iCell + 1)); + if (rms > EMCALCalibParams::Instance().maxPedestalRMS) { + mean = mMaxPedestalVal; + } + pedestalData.addPedestalValue(iCell, mean, isLG, isLEDMON); + } + } + return pedestalData; + } + + private: + //____________________________________________ + /// \brief Check if a SM exceeds a certain fraction of dead+bad channels. If yes, mask the entire SM + /// \param bcm -- current bad channel map + void checkMaskSM(o2::emcal::BadChannelMap& bcm); + + /// \brief Check if a FEC exceeds a certain fraction of dead+bad channels. If yes, mask the entire FEC + /// \param bcm -- current bad channel map + void checkMaskFEC(o2::emcal::BadChannelMap& bcm); + + /// \brief Get the FEC ID in a SM (IDs are just for internal handling in this task itself) + /// \param absCellID -- cell ID + unsigned int getFECNumberInSM(int absCellID) const; + + EMCALChannelScaleFactors* mBCMScaleFactors = nullptr; ///< Scale factors for nentries scaling in bad channel calibration + int mSigma = 5; ///< number of sigma used in the calibration to define outliers + int mNThreads = 1; ///< number of threads used for calibration + std::array mBadCellFracSM; ///< Fraction of bad+dead channels per SM + std::array, 20> mBadCellFracFEC; ///< Fraction of bad+dead channels per FEC + + o2::emcal::Geometry* mGeometry = nullptr; ///< pointer to the emcal geometry class + static constexpr int mNcells = 17664; ///< Number of total cells of EMCal + DCal + static constexpr int mLEDMONs = 480; ///< Number of total LEDMONS of EMCal + DCal + static constexpr short mMaxPedestalVal = 1023; ///< Maximum value for pedestals ClassDefNV(EMCALCalibExtractor, 1); }; diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibParams.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibParams.h index 6fc7e8a6bd90d..0de733dd953ab 100644 --- a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibParams.h +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALCalibParams.h @@ -37,16 +37,23 @@ struct EMCALCalibParams : public o2::conf::ConfigurableParamHelper #include -using boostHisto2d = boost::histogram::histogram, boost::histogram::axis::regular>, boost::histogram::unlimited_storage>>; - namespace o2 { namespace emcal @@ -59,7 +59,10 @@ class EMCALChannelCalibrator : public o2::calibration::TimeSlotCalibration; public: - EMCALChannelCalibrator(int nb = 1000, float r = 0.35) : mNBins(nb), mRange(r){}; + EMCALChannelCalibrator(int nb = 1000, float r = 0.35) : mNBins(nb), mRange(r) + { + this->setSaveDirectory(EMCALCalibParams::Instance().filePathSave); + }; ~EMCALChannelCalibrator() final = default; @@ -96,12 +99,29 @@ class EMCALChannelCalibrator : public o2::calibration::TimeSlotCalibration extr) { mCalibrator = extr; }; + bool setGainCalibrationFactors(o2::emcal::GainCalibrationFactors* gainCalibFactors); + + /// \brief Set current fill number + /// \param fn fill number + void setFillNr(int fn) { mFillNr = fn; } + + /// \brief Set current run type + /// \param rt tun type + void setRunType(o2::parameters::GRPECSObject::RunType rt) { mRunType = rt; } + + /// \brief Set current timestamp obtained from data + /// \param ts timestamp in hours + void setCurrTSInHours(int ts) { mStartTSCalib = ts; } + private: - int mNBins = 0; ///< bins of the histogram for passing - float mRange = 0.; ///< range of the histogram for passing - bool mTest = false; ///< flag to be used when running in test mode: it simplify the processing (e.g. does not go through all channels) - bool mSaveAtEOR = false; ///< flag to pretend to have enough data in order to trigger the saving of the calib histograms for loading them at the next run - bool mLoadAtSOR = false; ///< flag weather to load the calibration histograms from the previous run at the SOR + int mNBins = 0; ///< bins of the histogram for passing + float mRange = 0.; ///< range of the histogram for passing + bool mTest = false; ///< flag to be used when running in test mode: it simplify the processing (e.g. does not go through all channels) + bool mSaveAtEOR = false; ///< flag to pretend to have enough data in order to trigger the saving of the calib histograms for loading them at the next run + bool mLoadAtSOR = false; ///< flag weather to load the calibration histograms from the previous run at the SOR + o2::parameters::GRPECSObject::RunType mRunType = o2::parameters::GRPECSObject::RunType::NONE; ///< Run type needed if previous calibration is loaded. + int mFillNr = 0; ///< fill nr. needed if previous calibration is loaded. + int mStartTSCalib = -1; ///< First timestamp for calibration. Needed to check if calibration data from previous slot should be loaded std::shared_ptr mCalibrator; // output @@ -147,6 +167,11 @@ void EMCALChannelCalibrator::finalizeSlot(o2::calibration // Extract results for the single slot DataInput* c = slot.getContainer(); LOG(info) << "Finalize slot " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd(); + // check if slot contains a minimum amount of data + if (c->getNEvents() < EMCALCalibParams::Instance().minNEventsSaveSlot) { + LOG(info) << "Slot only contains " << c->getNEvents() << " events. Not saving this slot. " << EMCALCalibParams::Instance().minNEventsSaveSlot << " required"; + return; + } if constexpr (std::is_same::value) { if (c->getNEvents() < EMCALCalibParams::Instance().minNEvents_bc) { @@ -162,15 +187,22 @@ void EMCALChannelCalibrator::finalizeSlot(o2::calibration } } + // get the start timestamp of the slot. Either use the manually set start ts if available, or just the start time of the slot. + long tsStart = slot.getStartTimeMS(); + if (EMCALCalibParams::Instance().useStaticStartTimeSlot && slot.getStaticStartTimeMS() > 0) { + tsStart = slot.getStaticStartTimeMS(); + LOG(info) << "Adjusting the start TS of the slot from " << slot.getStartTimeMS() << " to " << slot.getStaticStartTimeMS(); + } + std::map md; if constexpr (std::is_same::value) { LOG(debug) << "Launching the calibration."; - auto bcm = mCalibrator->calibrateBadChannels(c->getHisto(), c->getHistoTime()); + o2::emcal::BadChannelMap bcm = mCalibrator->calibrateBadChannels(c->getHisto(), c->getHistoTime()); LOG(debug) << "Done with the calibraiton"; // for the CCDB entry auto clName = o2::utils::MemFileHelper::getClassName(bcm); auto flName = o2::ccdb::CcdbApi::generateFileName(clName); - mInfoVector.emplace_back(CalibDB::getCDBPathBadChannelMap(), clName, flName, md, slot.getStartTimeMS(), o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP); + mInfoVector.emplace_back(CalibDB::getCDBPathBadChannelMap(), clName, flName, md, tsStart, slot.getEndTimeMS() + EMCALCalibParams::Instance().endTimeMargin, true); mCalibObjectVector.push_back(bcm); if ((EMCALCalibParams::Instance().localRootFilePath).find(".root") != std::string::npos) { @@ -178,29 +210,30 @@ void EMCALChannelCalibrator::finalizeSlot(o2::calibration TFile fLocalStorage((EMCALCalibParams::Instance().localRootFilePath).c_str(), ffile.good() == true ? "update" : "recreate"); fLocalStorage.cd(); + TH2F* histBCMap = (TH2F*)bcm.getHistogramRepresentation(); - std::string nameBCHist = "BadChannels_" + std::to_string(slot.getStartTimeMS()); + std::string nameBCHist = "BadChannels_" + std::to_string(tsStart); histBCMap->Write(nameBCHist.c_str(), TObject::kOverwrite); TH2F hCalibHist = o2::utils::TH2FFromBoost(c->getHisto()); - std::string nameBCInputHist = "EnergyVsCellID_" + std::to_string(slot.getStartTimeMS()); + std::string nameBCInputHist = "EnergyVsCellID_" + std::to_string(tsStart); hCalibHist.Write(nameBCInputHist.c_str(), TObject::kOverwrite); TH2F hCalibHistTime = o2::utils::TH2FFromBoost(c->getHistoTime()); - std::string nameBCInputHistTime = "TimeVsCellID_" + std::to_string(slot.getStartTimeMS()); + std::string nameBCInputHistTime = "TimeVsCellID_" + std::to_string(tsStart); hCalibHistTime.Write(nameBCInputHistTime.c_str(), TObject::kOverwrite); fLocalStorage.Close(); } } else if constexpr (std::is_same::value) { - auto tcd = mCalibrator->calibrateTime(c->getHisto(), EMCALCalibParams::Instance().minTimeForFit_tc, EMCALCalibParams::Instance().maxTimeForFit_tc, EMCALCalibParams::Instance().restrictFitRangeToMax_tc); + o2::emcal::TimeCalibrationParams tcd = mCalibrator->calibrateTime(c->getHisto(), EMCALCalibParams::Instance().minTimeForFit_tc, EMCALCalibParams::Instance().maxTimeForFit_tc, EMCALCalibParams::Instance().restrictFitRangeToMax_tc); // for the CCDB entry auto clName = o2::utils::MemFileHelper::getClassName(slot); auto flName = o2::ccdb::CcdbApi::generateFileName(clName); - //prepareCCDBobjectInfo - mInfoVector.emplace_back(CalibDB::getCDBPathTimeCalibrationParams(), clName, flName, md, slot.getStartTimeMS(), o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP); + // prepareCCDBobjectInfo + mInfoVector.emplace_back(CalibDB::getCDBPathTimeCalibrationParams(), clName, flName, md, tsStart, slot.getEndTimeMS() + EMCALCalibParams::Instance().endTimeMargin, true); mCalibObjectVector.push_back(tcd); if ((EMCALCalibParams::Instance().localRootFilePath).find(".root") != std::string::npos) { @@ -209,15 +242,15 @@ void EMCALChannelCalibrator::finalizeSlot(o2::calibration TFile fLocalStorage((EMCALCalibParams::Instance().localRootFilePath).c_str(), ffile.good() == true ? "update" : "recreate"); fLocalStorage.cd(); TH1F* histTCparams = (TH1F*)tcd.getHistogramRepresentation(false); // high gain calibration - std::string nameTCHist = "TCParams_HG_" + std::to_string(slot.getStartTimeMS()); + std::string nameTCHist = "TCParams_HG_" + std::to_string(tsStart); histTCparams->Write(nameTCHist.c_str(), TObject::kOverwrite); TH1F* histTCparams_LG = (TH1F*)tcd.getHistogramRepresentation(true); // low gain calibration - std::string nameTCHist_LG = "TCParams_LG_" + std::to_string(slot.getStartTimeMS()); + std::string nameTCHist_LG = "TCParams_LG_" + std::to_string(tsStart); histTCparams_LG->Write(nameTCHist_LG.c_str(), TObject::kOverwrite); TH2F hCalibHist = o2::utils::TH2FFromBoost(c->getHisto()); - std::string nameTCInputHist = "TimeVsCellID_" + std::to_string(slot.getStartTimeMS()); + std::string nameTCInputHist = "TimeVsCellID_" + std::to_string(tsStart); hCalibHist.Write(nameTCInputHist.c_str(), TObject::kOverwrite); fLocalStorage.Close(); } @@ -239,17 +272,37 @@ template bool EMCALChannelCalibrator::saveLastSlotData(TFile& fl) { LOG(info) << "EMC calib histos are saved in " << fl.GetName(); - // does this work? + // we only have 1 slot auto& cont = o2::calibration::TimeSlotCalibration::getSlots(); auto& slot = cont.at(0); DataInput* c = slot.getContainer(); + // get the start timestamp of the slot. Either use the manually set start ts if available, or just the start time of the slot. + long tsStart = slot.getStartTimeMS(); + if (EMCALCalibParams::Instance().useStaticStartTimeSlot && slot.getStaticStartTimeMS() > 0) { + tsStart = slot.getStaticStartTimeMS(); + } + // timestamp in hours and seconds + int timeNowHour = static_cast(tsStart / o2::ccdb::CcdbObjectInfo::HOUR); + int timeNowSec = static_cast(tsStart / o2::ccdb::CcdbObjectInfo::SECOND); + + // create global event properties histogram. Same for both calibration types + TH1I hGlobalProperties("hGlobalProperties", "hGlobalProperties", 4, -0.5, 3.5); + hGlobalProperties.GetXaxis()->SetBinLabel(1, "Fill nr."); + hGlobalProperties.GetXaxis()->SetBinLabel(2, "run type"); + hGlobalProperties.GetXaxis()->SetBinLabel(3, "ts in hours"); + hGlobalProperties.GetXaxis()->SetBinLabel(4, "ts in seconds"); + hGlobalProperties.SetBinContent(1, mFillNr); + hGlobalProperties.SetBinContent(2, mRunType); + hGlobalProperties.SetBinContent(3, timeNowHour); + hGlobalProperties.SetBinContent(4, timeNowSec); + if constexpr (std::is_same::value) { auto hist = c->getHisto(); auto histTime = c->getHistoTime(); TH2F hEnergy = o2::utils::TH2FFromBoost(hist); - TH2F hTime = o2::utils::TH2FFromBoost(histTime); + TH2F hTime = o2::utils::TH2FFromBoost(histTime, "histTime"); TH1D hNEvents("hNEvents", "hNEvents", 1, 0, 1); hNEvents.SetBinContent(1, c->getNEvents()); @@ -257,6 +310,8 @@ bool EMCALChannelCalibrator::saveLastSlotData(TFile& fl) hEnergy.Write("EnergyVsCellID"); hTime.Write("TimeVsCellID"); hNEvents.Write("NEvents"); + hGlobalProperties.Write("GlobalProperties"); + } else if constexpr (std::is_same::value) { auto histTime = c->getHisto(); TH2F hTime = o2::utils::TH2FFromBoost(histTime); @@ -266,6 +321,7 @@ bool EMCALChannelCalibrator::saveLastSlotData(TFile& fl) fl.cd(); hTime.Write("TimeVsCellID"); hNEvents.Write("NEvents"); + hGlobalProperties.Write("GlobalProperties"); } return true; @@ -283,17 +339,52 @@ bool EMCALChannelCalibrator::adoptSavedData(const o2::cal return true; auto& cont = o2::calibration::TimeSlotCalibration::getSlots(); + + if (cont.size() == 0) { + LOG(warning) << "cont.size() is 0, calibration objects from previous run cannot be loaded..."; + return true; + } auto& slot = cont.at(0); DataInput* c = slot.getContainer(); + // check run type and fill + TH1I* hGlobalProperties = (TH1I*)fl.Get("GlobalProperties"); + if (!hGlobalProperties) { + LOG(error) << "GlobalProperties histogram not found. Will not load previous calibration histograms"; + } else { + int fillNr = hGlobalProperties->GetBinContent(1); + int runType = hGlobalProperties->GetBinContent(2); + int tsOld = hGlobalProperties->GetBinContent(3); + int tsOldSec = (hGlobalProperties->GetNbinsX() > 3) ? hGlobalProperties->GetBinContent(4) : 0; // Protection as 4th bin was only added later + int tsDiff = (mStartTSCalib > 0 ? mStartTSCalib : static_cast(o2::ccdb::getCurrentTimestamp() / o2::ccdb::CcdbObjectInfo::HOUR)) - tsOld; // get current timestamp if mStartTSCalib is not set + LOG(debug) << "tsOld " << tsOld << " tsNow " << (mStartTSCalib > 0 ? mStartTSCalib : static_cast(o2::ccdb::getCurrentTimestamp() / o2::ccdb::CcdbObjectInfo::HOUR)) << " tsDiff " << tsDiff; + + if (EMCALCalibParams::Instance().requireSameRunType && runType != static_cast(mRunType)) { + LOG(info) << "adoptSavedData: Same run type required but run types differ: " << runType << " != " << static_cast(mRunType); + return false; + } + if (EMCALCalibParams::Instance().requireSameFill && fillNr != mFillNr) { + LOG(info) << "adoptSavedData: Same fill nr. required but fills differ: " << fillNr << " != " << mFillNr; + return false; + } + if (EMCALCalibParams::Instance().tsDiffMax > 0 && (EMCALCalibParams::Instance().tsDiffMax < tsDiff || tsDiff < 0)) { + LOG(info) << "adoptSavedData: Maximum difference in ts is: " << EMCALCalibParams::Instance().tsDiffMax << " but " << tsDiff << " is given"; + return false; + } + if (EMCALCalibParams::Instance().useStaticStartTimeSlot && tsOldSec > 0) { + slot.setStaticStartTimeMS(static_cast(tsOldSec * o2::ccdb::CcdbObjectInfo::SECOND)); + LOG(info) << "adoptSavedData: Setting the start timestamp to " << static_cast(tsOldSec * o2::ccdb::CcdbObjectInfo::SECOND); + } + } + if constexpr (std::is_same::value) { TH2D* hEnergy = (TH2D*)fl.Get("EnergyVsCellID"); TH2D* hTime = (TH2D*)fl.Get("TimeVsCellID"); if (!hEnergy || !hTime) { return false; } - auto hEnergyBoost = o2::utils::boostHistoFromRoot_2D(hEnergy); - auto hTimeBoost = o2::utils::boostHistoFromRoot_2D(hTime); + auto hEnergyBoost = o2::utils::boostHistoFromRoot_2D(hEnergy); + auto hTimeBoost = o2::utils::boostHistoFromRoot_2D(hTime); c->setHisto(hEnergyBoost); c->setHistoTime(hTimeBoost); @@ -303,7 +394,7 @@ bool EMCALChannelCalibrator::adoptSavedData(const o2::cal if (!hTime) { return false; } - auto hTimeBoost = o2::utils::boostHistoFromRoot_2D(hTime); + auto hTimeBoost = o2::utils::boostHistoFromRoot_2D(hTime); c->setHisto(hTimeBoost); } @@ -312,6 +403,23 @@ bool EMCALChannelCalibrator::adoptSavedData(const o2::cal return false; } c->setNEvents(hEvents->GetBinContent(1)); + LOG(info) << "successfully loaded histograms with " << hEvents->GetBinContent(1) << " events"; + + return true; +} + +template +bool EMCALChannelCalibrator::setGainCalibrationFactors(o2::emcal::GainCalibrationFactors* gainCalibFactors) +{ + + auto& cont = o2::calibration::TimeSlotCalibration::getSlots(); + if (cont.size() == 0) { + return false; // time slot object not yet there + } + + auto& slot = cont.at(0); + DataInput* c = slot.getContainer(); + c->setGainCalibFactors(gainCalibFactors); return true; } diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALChannelData.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALChannelData.h index 16ba40e5cff1d..0919c157c6610 100644 --- a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALChannelData.h +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALChannelData.h @@ -23,6 +23,7 @@ // #include "CommonUtils/BoostHistogramUtils.h" // #include "EMCALCalib/BadChannelMap.h" // #include "EMCALCalib/TimeCalibrationParams.h" +#include "EMCALCalib/GainCalibrationFactors.h" #include "EMCALCalib/EMCALChannelScaleFactors.h" #include "EMCALCalibration/EMCALCalibExtractor.h" #include "EMCALCalibration/EMCALCalibParams.h" @@ -38,6 +39,8 @@ #include // #include +#include + // #include namespace o2 @@ -48,9 +51,9 @@ class EMCALCalibExtractor; class EMCALChannelData { - //using Slot = o2::calibration::TimeSlot; + // using Slot = o2::calibration::TimeSlot; using Cells = o2::emcal::Cell; - using boostHisto = boost::histogram::histogram>, boost::histogram::axis::variable>>>; + using boostHisto = boost::histogram::histogram, boost::histogram::axis::regular>>; using BadChannelMap = o2::emcal::BadChannelMap; public: @@ -58,29 +61,21 @@ class EMCALChannelData o2::emcal::Geometry* mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); int NCELLS = mGeometry->GetNCells(); - EMCALChannelData() : mNBins(EMCALCalibParams::Instance().nBinsEnergyAxis_bc), mRange(EMCALCalibParams::Instance().maxValueEnergyAxis_bc), mNBinsTime(EMCALCalibParams::Instance().nBinsTimeAxis_bc), mRangeTimeLow(EMCALCalibParams::Instance().rangeTimeAxisLow_bc), mRangeTimeHigh(EMCALCalibParams::Instance().rangeTimeAxisHigh_bc) + EMCALChannelData() : mNBins(EMCALCalibParams::Instance().nBinsEnergyAxis_bc), mRange(EMCALCalibParams::Instance().maxValueEnergyAxis_bc), mNBinsTime(EMCALCalibParams::Instance().nBinsTimeAxis_bc), mRangeTimeLow(EMCALCalibParams::Instance().rangeTimeAxisLow_bc), mRangeTimeHigh(EMCALCalibParams::Instance().rangeTimeAxisHigh_bc), mNThreads(EMCALCalibParams::Instance().nThreads_bc) { // NCELLS includes DCal, treat as one calibration o2::emcal::Geometry* mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); int NCELLS = mGeometry->GetNCells(); - // boost histogram with amplitude vs. cell ID, specify the range and binning of the amplitude axis - std::vector binEdgesCells; - for (int i = 0; i <= NCELLS; i++) { - binEdgesCells.push_back(i); - } - std::vector binEdgesEnergy; - for (int i = 0; i <= mNBins; i++) { - binEdgesEnergy.push_back(static_cast(i) * mRange / mNBins); + mVecNEntriesInHisto.resize(mNThreads); + mHisto.resize(mNThreads); + mHistoTime.resize(mNThreads); + for (size_t i = 0; i < mNThreads; ++i) { + mHisto[i] = boost::histogram::make_histogram(boost::histogram::axis::regular<>(mNBins, 0., mRange), boost::histogram::axis::regular<>(NCELLS, -0.5, NCELLS - 0.5)); + mHistoTime[i] = boost::histogram::make_histogram(boost::histogram::axis::regular<>(mNBinsTime, mRangeTimeLow, mRangeTimeHigh), boost::histogram::axis::regular<>(NCELLS, -0.5, NCELLS - 0.5)); + mVecNEntriesInHisto[i] = 0; } - std::vector binEdgesTime; - for (int i = 0; i <= mNBinsTime; i++) { - binEdgesTime.push_back(mRangeTimeLow + (static_cast(i) * std::abs(mRangeTimeHigh - mRangeTimeLow)) / mNBinsTime); - } - - mHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesEnergy), boost::histogram::axis::variable<>(binEdgesCells)); - mHistoTime = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesTime), boost::histogram::axis::variable<>(binEdgesCells)); } ~EMCALChannelData() = default; @@ -96,23 +91,40 @@ class EMCALChannelData /// \brief Fill the container with the cell ID and amplitude. void fill(const gsl::span data); /// \brief Merge the data of two slots. - void merge(const EMCALChannelData* prev); + void merge(EMCALChannelData* prev); // int findBin(float v) const; /// \brief Check if enough stataistics was accumulated to perform calibration bool hasEnoughData() const; /// \brief Get current calibration histogram - boostHisto& getHisto() { return mHisto; } - const boostHisto& getHisto() const { return mHisto; } + const boostHisto& getHisto() + { + // set the summed histogram to one of the existing histograms + mHistoSummed = mHisto[0]; + // reset the histogram + mHistoSummed.reset(); + // Sum up all entries + for (const auto& h : mHisto) { + mHistoSummed += h; + } + return mHistoSummed; + } /// \brief Set new calibration histogram - void setHisto(boostHisto hist) { mHisto = hist; } + void setHisto(boostHisto hist, int nthr = 0) { mHisto[nthr] = hist; } - /// \brief Get current calibration histogram with timing information - boostHisto& getHistoTime() { return mHistoTime; } - const boostHisto& getHistoTime() const { return mHistoTime; } + /// \brief Get current calibration histogram with time information + const boostHisto& getHistoTime() + { + mHistoTimeSummed = mHistoTime[0]; + mHistoTimeSummed.reset(); + for (const auto& h : mHistoTime) { + mHistoTimeSummed += h; + } + return mHistoTimeSummed; + } /// \brief Set new calibration histogram with timing info - void setHistoTime(boostHisto hist) { mHistoTime = hist; } + void setHistoTime(boostHisto hist, int nthr = 0) { mHistoTime[nthr] = hist; } /// \brief Peform the calibration and flag the bad channel map /// Average energy per hit histogram is fitted with a gaussian @@ -131,22 +143,39 @@ class EMCALChannelData long unsigned int getNEntriesInHisto() const { return mNEntriesInHisto; } void setNEntriesInHisto(long unsigned int n) { mNEntriesInHisto = n; } + void addEntriesInHisto(long unsigned int n) { mNEntriesInHisto += n; } + + void setGainCalibFactors(o2::emcal::GainCalibrationFactors* calibFactors) + { + mGainCalibFactors = calibFactors; + for (unsigned int i = 0; i < mArrGainCalibFactors.size(); ++i) { + mArrGainCalibFactors[i] = mGainCalibFactors->getGainCalibFactors(i); + } + mApplyGainCalib = true; + } private: float mRange = 10; ///< Maximum energy range of boost histogram (will be overwritten by values in the EMCALCalibParams) int mNBins = 1000; ///< Number of bins in the boost histogram (will be overwritten by values in the EMCALCalibParams) - boostHisto mHisto; ///< 2d boost histogram with cellID vs cell energy + size_t mNThreads = 1; ///< Number of threads used for filling the boost histograms + std::vector mHisto; ///< vector of 2d boost histogram with cellID vs cell energy + boostHisto mHistoSummed; ///< summed 2d boost histogram (sum of mHisto) int mNBinsTime = 1000; ///< Number of time bins in boost histogram (cell time vs. cell ID) float mRangeTimeLow = -500; ///< lower bound of time axis of mHistoTime float mRangeTimeHigh = 500; ///< upper bound of time axis of mHistoTime - boostHisto mHistoTime; ///< 2d boost histogram with cellID vs cell time + std::vector mHistoTime; ///< vector of 2d boost histogram with cellID vs cell time + boostHisto mHistoTimeSummed; ///< Summed 2d boost histogram with cellID vs cell time int mEvents = 0; ///< event counter long unsigned int mNEntriesInHisto = 0; ///< Number of entries in the histogram + std::vector mVecNEntriesInHisto; ///< Number of entries in the histogram for each thread per event boostHisto mEsumHisto; ///< contains the average energy per hit for each cell boostHisto mEsumHistoScaled; ///< contains the average energy (scaled) per hit for each cell boostHisto mCellAmplitude; ///< is the input for the calibration, hist of cell E vs. ID bool mTest = false; ///< flag to be used when running in test mode: it simplify the processing BadChannelMap mOutputBCM; ///< output bad channel map for the calibration + bool mApplyGainCalib = false; ///< Switch if gain calibration is applied or not + o2::emcal::GainCalibrationFactors* mGainCalibFactors; ///< Gain calibration factors applied to the data before filling the histograms + std::array mArrGainCalibFactors; ///< array of gain calibration factors std::shared_ptr mCalibExtractor; ///< calib extractor ClassDefNV(EMCALChannelData, 1); diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALPedestalHelper.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALPedestalHelper.h new file mode 100644 index 0000000000000..109fa795fd43a --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALPedestalHelper.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef EMCAL_PEDESTAL_HELPER_H_ +#define EMCAL_PEDESTAL_HELPER_H_ + +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "EMCALReconstruction/Channel.h" +#include "EMCALBase/Geometry.h" +#include "EMCALBase/Mapper.h" +#include "EMCALCalib/CalibDB.h" +#include "EMCALCalib/Pedestal.h" + +#include +#include +#include +#include +#include + +namespace o2::emcal +{ + +class EMCALPedestalHelper +{ + + public: + EMCALPedestalHelper() = default; + ~EMCALPedestalHelper() = default; + + /// \brief Encodes the pedestal object into a string. This function fills fMeanPed which is then converted to a string in createInstructionString + /// \param obj pedestal object as stored in production ccdb + /// \param runNum current runnumber. If -1, will not be added to string that goes in the ccdb, otherwise runNum is the first entrey in the string + std::vector createPedestalInstruction(const Pedestal& obj, const int runNum = -1); + + /// \brief print the vector produced by createInstructionString in a textfile + void dumpInstructions(const std::string_view filename, const gsl::span& data); + + private: + /// \brief initialize fMeanPed with zeros + void setZero(); + + /// \brief converts fMeanPed to a vector of char + /// \param runNum current runnumber. If -1, will not be added to string that goes in the ccdb, otherwise runNum is the first entrey in the string + std::vector createInstructionString(const int runNum = -1); + + static constexpr short kNSM = 20; ///< number of SuperModules + static constexpr short kNRCU = 2; ///< number of readout crates (and DDLs) per SM + static constexpr short kNDTC = 40; ///< links for full SRU + static constexpr short kNBranch = 2; ///< low gain/high gain + static constexpr short kNFEC = 10; ///< 0..9, when including LED Ref + static constexpr short kNChip = 5; ///< really 0,2..4, i.e. skip #1 + static constexpr short kNChan = 16; + short fMeanPed[kNSM][kNRCU][kNBranch][kNFEC][kNChip][kNChan]; +}; + +} // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h new file mode 100644 index 0000000000000..5dbaec4c933f8 --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTempCalibExtractor.h @@ -0,0 +1,93 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \class EMCALTempCalibExtractor +/// \brief Calculate gain correction factors based on the temperature and the cell-by-cell temperature dependent correction factors (slope and intercept) +/// \author Joshua König +/// \ingroup EMCALCalib +/// \since June 30, 2025 + +#ifndef EMCALTEMPCALIBEXTRACTOR_H_ +#define EMCALTEMPCALIBEXTRACTOR_H_ + +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" +#include "EMCALCalib/ElmbData.h" +#include "EMCALCalib/TempCalibrationParams.h" +#include "EMCALBase/Geometry.h" + +namespace o2 +{ +namespace emcal +{ + +class EMCALTempCalibExtractor +{ + + public: + /// \brief Constructor + EMCALTempCalibExtractor() + { + LOG(info) << "initialized EMCALTempCalibExtractor"; + try { + // Try to access geometry initialized ountside + mGeometry = o2::emcal::Geometry::GetInstance(); + } catch (o2::emcal::GeometryNotInitializedException& e) { + mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); // fallback option + } + }; + /// \brief Destructor + ~EMCALTempCalibExtractor() = default; + + /// \brief Initialize temperature data and slope for each cell from the ccdb + /// \param path path to the slope data + /// \param timestamp timestamp for the ccdb objects or runnumber (will detect automatically if its a runnumber and convert it) + void InitializeFromCCDB(std::string path, uint64_t timestamp); + + /// \brief get average temperature in a supermodule + /// \param iSM SM number + /// \param ElmbData object where temperature sensor values are stored + /// \return average temperature in a supermodule + float getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const; + + /// \brief get gain calibration factor depending on the temperature and the slope of the cell + /// \param cellID cell ID + /// \return gain calibration factor + float getGainCalibFactor(const unsigned short cellID) const; + + /// \brief set temperature range in which sensor ddata is assumed to be good + /// \param low lower temperature + /// \param high upper temperature + void setAcceptedEnergyRange(float low, float high); + + /// \brief set if median (true) or mean (false) should be used for averaging of the temperature in a SM + void setUseMedian(const bool tmp) { mUseMedian = tmp; } + + /// \brief get sensor IDs for a specific supermodule + /// \param iSM SM number + /// \return vector of sensor IDs + std::vector getSensorsForSM(const unsigned short iSM) const; + + private: + static constexpr unsigned short mNCells = 17664; ///< Number of EMCal cells + std::array mGainCalibFactors; ///< gain calibration factors that are calculated based on the temperature and the slopes for each cell + o2::emcal::Geometry* mGeometry; ///< pointer to the EMCal geometry + std::array mAcceptedTempRange = {15., 30.}; ///< Temperature range where sensors are believed to send good data. Temperatures outside this range will be rejected + bool mUseMedian = true; /// switch to decide if temperature within a SM should be calculated as the mean or the median of the individual sensor data +}; + +} // namespace emcal + +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTimeCalibData.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTimeCalibData.h index a00f1b81604ef..4eb89608c8f33 100644 --- a/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTimeCalibData.h +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/EMCALTimeCalibData.h @@ -24,6 +24,7 @@ #include "EMCALBase/Geometry.h" #include "CCDB/CcdbObjectInfo.h" #include "EMCALCalib/TimeCalibrationParams.h" +#include "EMCALCalib/GainCalibrationFactors.h" #include "EMCALCalibration/EMCALCalibParams.h" #include "Framework/Logger.h" @@ -45,23 +46,21 @@ class EMCALTimeCalibData { public: using Cells = o2::emcal::Cell; - using boostHisto = boost::histogram::histogram>, boost::histogram::axis::variable>>>; + using boostHisto = boost::histogram::histogram, boost::histogram::axis::regular>>; o2::emcal::Geometry* mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); int NCELLS = mGeometry->GetNCells(); EMCALTimeCalibData() { - std::vector binEdgesCells; - for (int i = 0; i <= NCELLS; i++) { - binEdgesCells.push_back(i); - } - std::vector binEdgesTime; - for (int i = 0; i <= EMCALCalibParams::Instance().nBinsTimeAxis_tc; i++) { - binEdgesTime.push_back(EMCALCalibParams::Instance().minValueTimeAxis_tc + (static_cast(i) * std::abs(EMCALCalibParams::Instance().maxValueTimeAxis_tc - EMCALCalibParams::Instance().minValueTimeAxis_tc)) / EMCALCalibParams::Instance().nBinsTimeAxis_tc); - } - mTimeHisto = boost::histogram::make_histogram(boost::histogram::axis::variable<>(binEdgesTime), boost::histogram::axis::variable<>(binEdgesCells)); + mNThreads = EMCALCalibParams::Instance().nThreads_tc; + mTimeHisto.resize(mNThreads); + mVecNEntriesInHisto.resize(mNThreads); + for (size_t i = 0; i < mNThreads; ++i) { + mTimeHisto[i] = boost::histogram::make_histogram(boost::histogram::axis::regular<>(EMCALCalibParams::Instance().nBinsTimeAxis_tc, EMCALCalibParams::Instance().minValueTimeAxis_tc, EMCALCalibParams::Instance().maxValueTimeAxis_tc), boost::histogram::axis::regular<>(NCELLS, -0.5, NCELLS - 0.5)); + mVecNEntriesInHisto[i] = 0; + } LOG(debug) << "initialize time histogram with " << NCELLS << " cells"; } @@ -71,7 +70,7 @@ class EMCALTimeCalibData void fill(const gsl::span data); /// \brief Merge the data of two slots. - void merge(const EMCALTimeCalibData* prev); + void merge(EMCALTimeCalibData* prev); /// \brief Check if enough data for calibration has been accumulated bool hasEnoughData() const; @@ -90,24 +89,49 @@ class EMCALTimeCalibData long unsigned int getNEntriesInHisto() const { return mNEntriesInHisto; } /// \brief Set the number of entries in histogram void setNEntriesInHisto(long unsigned int n) { mNEntriesInHisto = n; } + /// \brief Add the number of entries in histogram + void addNEntriesInHisto(long unsigned int n) { mNEntriesInHisto += n; } /// \brief Get current histogram - boostHisto& getHisto() { return mTimeHisto; } - const boostHisto& getHisto() const { return mTimeHisto; } + const boostHisto& getHisto() + { + // set the summed histogram to one of the existing histograms + mHistoSummed = mTimeHisto[0]; + // reset the histogram + mHistoSummed.reset(); + // Sum up all entries + for (const auto& h : mTimeHisto) { + mHistoSummed += h; + } + return mHistoSummed; + } + + /// \brief Set gain calibration factors applied to the cell energy before filling the histograms + /// \param calibFactors gain calibration object + void setGainCalibFactors(o2::emcal::GainCalibrationFactors* calibFactors) + { + mGainCalibFactors = calibFactors; + mApplyGainCalib = true; + } /// \brief Set new calibration histogram - void setHisto(boostHisto hist) { mTimeHisto = hist; } + void setHisto(boostHisto hist) { mTimeHisto[0] = hist; } + /// \brief print stream void PrintStream(std::ostream& stream) const; /// \brief Actual function where calibration is done. Has to be called in has enough data when enough data is there o2::emcal::TimeCalibrationParams process(); private: - boostHisto mTimeHisto; ///< histogram with cell time vs. cell ID - - int mEvents = 0; ///< current number of events - long unsigned int mNEntriesInHisto = 0; ///< number of entries in histogram + unsigned int mNThreads = 1; + std::vector mTimeHisto; ///< vector of histogram with cell time vs. cell ID (size = number of threads) + boostHisto mHistoSummed; ///< summed histogram (sum of all histograms in mTimeHisto) + int mEvents = 0; ///< current number of events + long unsigned int mNEntriesInHisto = 0; ///< Number of entries in the histogram + std::vector mVecNEntriesInHisto; ///< Number of entries in the histogram for each thread per event + bool mApplyGainCalib = false; ///< Switch if gain calibration is applied or not + o2::emcal::GainCalibrationFactors* mGainCalibFactors; ///< Gain calibration factors applied to the data before filling the histograms ClassDefNV(EMCALTimeCalibData, 1); }; diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalCalibDevice.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalCalibDevice.h new file mode 100644 index 0000000000000..6d4cfa8ff2775 --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalCalibDevice.h @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef EMCAL_PEDESTAL_CALIB_DEVICE_H_ +#define EMCAL_PEDESTAL_CALIB_DEVICE_H_ + +#include +#include + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "EMCALBase/Mapper.h" +#include "EMCALBase/Geometry.h" +#include "EMCALCalibration/PedestalProcessorData.h" +#include "EMCALCalibration/EMCALCalibExtractor.h" +#include "EMCALCalibration/EMCALPedestalHelper.h" + +namespace o2::emcal +{ + +class PedestalCalibDevice : o2::framework::Task +{ + public: + PedestalCalibDevice(bool dumpToFile, bool addRunNum) : mDumpToFile(dumpToFile), mAddRunNumber(addRunNum){}; + ~PedestalCalibDevice() final = default; + + void init(framework::InitContext& ctx) final; + + void run(framework::ProcessingContext& ctx) final; + + void sendData(o2::framework::EndOfStreamContext& ec, const Pedestal& data) const; + + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + void resetStartTS() { mStartTS = o2::ccdb::getCurrentTimestamp(); } + + static const char* getPedDataBinding() { return "PEDData"; } + + private: + Geometry* mGeometry = nullptr; ///< pointer to the emcal geometry class + o2::emcal::EMCALCalibExtractor mCalibExtractor; ///< instance of the calibration extraction class ///< Calibration postprocessing + PedestalProcessorData mPedestalData; ///< pedestal data to accumulate data + long int mStartTS = 0; ///< timestamp at the start of run used for the object in the ccdb + bool mDumpToFile; ///< if output of pedestal calib (DCS ccdb) should be written to text file + int mRun = 0; ///< current run number + bool mAddRunNumber = false; ///< if true, runNumber will be added to ccdb string +}; + +o2::framework::DataProcessorSpec getPedestalCalibDevice(bool dumpToFile, bool addRunNum); + +} // end namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorData.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorData.h new file mode 100644 index 0000000000000..8030c9bc4739a --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorData.h @@ -0,0 +1,154 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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 EMCAL_PEDESTAL_PROCESSOR_DATA_H_ +#define EMCAL_PEDESTAL_PROCESSOR_DATA_H_ +#include +#include +#include +#include +#include +#include "MathUtils/Utils.h" + +#include "Rtypes.h" + +namespace o2::emcal +{ + +/// \class PedestalProcessorData +/// \brief Exchange container between PedestalProcessorDevice and PedestalAggregatorDevice +/// \ingroup EMCALCalib +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since March 22, 2024 +/// +/// Object containing arrays of stat accumulators that behave like flat profile histograms +/// calculating mean and RMS of a set of ADC values. Corresponding arrays are used for +/// both FEC and LEDMON channels, and in both cases for high and low gain. Distinction between +/// channel and gain type is done via arguments in the fill and get functions, always defining +/// the true cases with LEDMON and low gain. +class PedestalProcessorData +{ + private: + public: + /// \class ChannelIndexException + /// \brief Handling access to invalid channel index (out-of-bounds) + class ChannelIndexException : public std::exception + { + private: + unsigned short mChannelIndex; ///< Index of the channel raising the exception + unsigned short mMaxChannels; ///< Max. number of channels for a given channel type + std::string mErrorMessage; ///< Buffer for the error message + + public: + /// \brief Constructor + /// \param channelIndex Index raising the exception + /// \param maxChannels Maximun number of channels for a given type + ChannelIndexException(unsigned short channelIndex, unsigned short maxChannels) : std::exception(), mChannelIndex(channelIndex), mMaxChannels(maxChannels) + { + mErrorMessage = "Channel index " + std::to_string(mChannelIndex) + " not found (max " + std::to_string(mMaxChannels) + ")"; + } + + /// \brief Destructor + ~ChannelIndexException() noexcept final = default; + + /// \brief Get error message of the exception + /// \return Error message + const char* what() const noexcept final { return mErrorMessage.data(); } + + /// \brief Get channel index raising the exception + /// \return Channel index + unsigned short getChannelIndex() const noexcept { return mChannelIndex; } + + /// \brief Get max number for channels for the type the exception was raised + /// \return Number of channels + unsigned short getMaxChannels() const noexcept { return mMaxChannels; } + }; + + using ProfileHistFEC = std::array; + using ProfileHistLEDMON = std::array; + using PedestalValue = std::tuple; + + /// \brief Constructor + PedestalProcessorData() = default; + + /// \brief Destructor + ~PedestalProcessorData() = default; + + /// \brief Accumulation operator + /// \param other Object to add to this object + /// \return This object after accumulation + /// + /// Adding stat. accumulators for all channels to this object. The state of this + /// object is modified. + PedestalProcessorData& operator+=(const PedestalProcessorData& other); + + /// \brief Fill ADC value for certain channel + /// \param adc ADC value + /// \param tower Absolute tower ID + /// \param lowGain Switch between low and high gain (true = low gain) + /// \param LEDMON Switch between LEDMON and FEE data (true = LEDMON) + /// \throw ChannelIndexException for channel index out-of-range + void fillADC(unsigned short adc, unsigned short tower, bool lowGain, bool LEDMON); + + /// \brief Get mean ADC and RMS for a certain channel + /// \param tower Absolute tower ID + /// \param lowGain Switch between low and high gain (true = low gain) + /// \param LEDMON Switch between LEDMON and FEE data (true = LEDMON) + /// \return std::tuple with mean and rms of the ADC distribution for the given channl + /// \throw ChannelIndexException for channel index out-of-range + PedestalValue getValue(unsigned short tower, bool lowGain, bool LEDMON) const; + + /// \brief Get number of entries for a certain channel + /// \param tower Absolute tower ID + /// \param lowGain Switch between low and high gain (true = low gain) + /// \param LEDMON Switch between LEDMON and FEE data (true = LEDMON) + /// \return Number of entries + /// \throw ChannelIndexException for channel index out-of-range + int getEntriesForChannel(unsigned short tower, bool lowGain, bool LEDMON) const; + + /// \brief Reset object + /// + /// Set all stat accumulators to 0. + void reset(); + + /// \brief Provide access to accumulated data for FEC channels + /// \param lowGain Low gain data + /// \return Accumulated data for low gain (if lowGain) or high gain + const ProfileHistFEC& accessFECData(bool lowGain) const { return lowGain ? mDataFECLG : mDataFECHG; } + + /// \brief Provide access to accumulated data for LEDMON channels + /// \param lowGain Low gain data + /// \return Accumulated data for low gain (if lowGain) or high gain + const ProfileHistLEDMON& accessLEDMONData(bool lowGain) const { return lowGain ? mDataLEDMONLG : mDataLEDMONHG; } + + private: + ProfileHistFEC mDataFECHG; ///< Profile for FEC channels, high gain + ProfileHistFEC mDataFECLG; ///< Profile for FEC channels, low gain + ProfileHistLEDMON mDataLEDMONHG; ///< Profile for LEDMON channels, high gain + ProfileHistLEDMON mDataLEDMONLG; ///< Profile for LEDMON channels, low gain + + ClassDefNV(PedestalProcessorData, 1); +}; + +/// \brief Sum operator for PedestalProcessorData +/// \param lhs Left hand value of the sum operation +/// \param rhs Right hand value of the sum operation +/// \return Sum of the two containers (all channels) +PedestalProcessorData operator+(const PedestalProcessorData& lhs, const PedestalProcessorData& rhs); + +/// @brief Output stream operator for PedestalProcessorData::ChannelIndexException +/// @param stream Stream used for printing +/// @param ex Exception to be printed +/// @return Stream after printing +std::ostream& operator<<(std::ostream& stream, const PedestalProcessorData::ChannelIndexException& ex); + +} // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorDevice.h b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorDevice.h new file mode 100644 index 0000000000000..5f84862838f0c --- /dev/null +++ b/Detectors/EMCAL/calibration/include/EMCALCalibration/PedestalProcessorDevice.h @@ -0,0 +1,130 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef EMCAL_PEDESTAL_PROCESSOR_DEVICE_H_ +#define EMCAL_PEDESTAL_PROCESSOR_DEVICE_H_ + +#include +#include + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "EMCALBase/Mapper.h" +#include "EMCALCalibration/PedestalProcessorData.h" + +namespace o2::emcal +{ + +class Geometry; + +/// \class PedestalProcessorDevice +/// \brief Processor part of the EMCAL pedestal calibration workflow +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALCalib +/// \since March 21, 2024 +class PedestalProcessorDevice : o2::framework::Task +{ + private: + /// \class ModuleIndexException + /// \brief Exception handling errors in calculation of the absolute module ID + class ModuleIndexException : public std::exception + { + public: + /// \enum ModuleType_t + /// \brief Type of module raising the exception + enum class ModuleType_t { + CELL_MODULE, ///< Cell module type + LEDMON_MODULE ///< LEDMON module type + }; + + /// \brief Constructor for cell indices + /// \param moduleIndex Index of the module raising the exception + /// \param column Column of the cell + /// \param row Row of the cell + /// \param columnshifted Shifted column index + /// \param rowshifted Shifted row index + ModuleIndexException(int moduleIndex, int column, int row, int columnshifted, int rowshifted) : mModuleType(ModuleType_t::CELL_MODULE), + mIndex(moduleIndex), + mColumn(column), + mRow(row), + mColumnShifted(columnshifted), + mRowShifted(rowshifted) {} + + /// \brief Constructor for LEDMON indices + /// \param moduleIndex Index of the module raising the exception + ModuleIndexException(int moduleIndex) : mModuleType(ModuleType_t::LEDMON_MODULE), mIndex(moduleIndex) {} + + /// \brief Destructor + ~ModuleIndexException() noexcept final = default; + + /// \brief Access to error message + /// \return Error message + const char* what() const noexcept final { return "Invalid cell / LEDMON index"; } + + /// \brief Get type of module raising the exception + /// \return Module type + ModuleType_t getModuleType() const { return mModuleType; } + + /// \brief Get index of the module raising the exception + /// \return Index of the module + int getIndex() const { return mIndex; } + + /// \brief Get column raising the exception (cell-case) + /// \return Column + int getColumn() const { return mColumn; } + + /// \brief Get row raising the exception (cell-case) + /// \return Row + int getRow() const { return mRow; } + + /// \brief Get shifted column raising the exception (cell-case) + /// \return Shifted column + int getColumnShifted() const { return mColumnShifted; } + + /// \brief Get shifted row raising the exception (cell-case) + /// \return Shifted row + int getRowShifted() const { return mRowShifted; } + + private: + ModuleType_t mModuleType; ///< Type of the module raising the exception + int mIndex = -1; ///< Index raising the exception + int mColumn = -1; ///< Column of the module (cell-case) + int mRow = -1; ///< Row of the module (cell-case) + int mColumnShifted = -1; ///< shifted column of the module (cell-case) + int mRowShifted = -1; /// << shifted row of the module (cell-case) + }; + + Geometry* mGeometry = nullptr; + std::unique_ptr mMapper = nullptr; + PedestalProcessorData mPedestalData; + + protected: + int getCellAbsID(int supermoduleID, int column, int row) const; + int geLEDMONAbsID(int supermoduleID, int moduleID) const; + + public: + PedestalProcessorDevice() = default; + ~PedestalProcessorDevice() final = default; + + void init(framework::InitContext& ctx) final; + + void run(framework::ProcessingContext& ctx) final; + + bool isLostTimeframe(framework::ProcessingContext& ctx) const; + + void sendData(framework::ProcessingContext& ctx, const PedestalProcessorData& data) const; +}; + +framework::DataProcessorSpec getPedestalProcessorDevice(bool askDistSTF); + +} // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/run/runCalibOffline.cxx b/Detectors/EMCAL/calibration/run/runCalibOffline.cxx index a9a8c4a373d9f..3ee505980ce29 100644 --- a/Detectors/EMCAL/calibration/run/runCalibOffline.cxx +++ b/Detectors/EMCAL/calibration/run/runCalibOffline.cxx @@ -50,11 +50,9 @@ int main(int argc, char** argv) std::string nameCalibInputHist; // hCellIdVsTimeAbove300 for time, hCellIdVsEnergy for bad channel std::string nameCalibInputHistAdd; // additional input histogram for bad channel calibration if time should be considered std::string namePathStoreLocal; // name for path + histogram to store the calibration locally in root TH1 format - - unsigned int nthreads; // number of threads used by openMP - - unsigned long rangestart; //30/10/2021, 01:02:32 for run 505566 -> 1635548552000 - unsigned long rangeend; // 30/10/2021, 02:31:10 for run 505566 -> 1635553870000 + unsigned int nthreads; // number of threads used by openMP + unsigned long rangestart; // 30/10/2021, 01:02:32 for run 505566 -> 1635548552000 + unsigned long rangeend; // 30/10/2021, 02:31:10 for run 505566 -> 1635553870000 double timeRangeLow; double timeRangeHigh; @@ -211,7 +209,7 @@ int main(int argc, char** argv) CalibExtractor.setNThreads(nthreads); // convert the test root histogram to boost - auto hCalibInputHist = o2::utils::boostHistoFromRoot_2D(hCalibInputHist_ROOT); + boostHisto2d_VarAxis hCalibInputHist = o2::utils::boostHistoFromRoot_2D(hCalibInputHist_ROOT); // instance of CalibDB o2::emcal::CalibDB calibdb(ccdbServerPath); @@ -225,7 +223,7 @@ int main(int argc, char** argv) o2::emcal::BadChannelMap BCMap; if (doBCCalibWithTime) { - auto hCalibInputHistAdd = o2::utils::boostHistoFromRoot_2D(hCalibInputHistAdd_ROOT); + boostHisto2d_VarAxis hCalibInputHistAdd = o2::utils::boostHistoFromRoot_2D(hCalibInputHistAdd_ROOT); BCMap = CalibExtractor.calibrateBadChannels(hCalibInputHist, hCalibInputHistAdd); } else { BCMap = CalibExtractor.calibrateBadChannels(hCalibInputHist); diff --git a/Detectors/EMCAL/calibration/src/EMCALCalibExtractor.cxx b/Detectors/EMCAL/calibration/src/EMCALCalibExtractor.cxx index 26f131e92d084..2219220d93c8e 100644 --- a/Detectors/EMCAL/calibration/src/EMCALCalibExtractor.cxx +++ b/Detectors/EMCAL/calibration/src/EMCALCalibExtractor.cxx @@ -15,7 +15,6 @@ namespace o2 { namespace emcal { -using boostHisto = boost::histogram::histogram, boost::histogram::axis::integer<>>, boost::histogram::unlimited_storage>>; //------------------------------------------------------------------------------------------- // This function builds the scaled hit distribution @@ -30,8 +29,7 @@ using boostHisto = boost::histogram::histogram(100, 0, 100, "t-texp"), boost::histogram::axis::integer<>(0, mNcells, "CELL ID")); - + boostHisto eSumHistoScaled = boost::histogram::make_histogram(boost::histogram::axis::regular<>(100, 0, 100, "t-texp"), boost::histogram::axis::integer<>(0, mNcells, "CELL ID")); // create a slice for each cell with energies ranging from emin to emax auto hEnergyCol = boost::histogram::make_histogram(boost::histogram::axis::regular<>(100, 0, 100., "t-texp")); auto hEnergyRow = boost::histogram::make_histogram(boost::histogram::axis::regular<>(250, 0, 250., "t-texp")); @@ -39,7 +37,7 @@ boostHisto EMCALCalibExtractor::buildHitAndEnergyMeanScaled(double emin, double auto hEnergyScaled = boost::histogram::make_histogram(boost::histogram::axis::regular<>(100, 0, 100, "t-texp"), boost::histogram::axis::integer<>(0, mNcells, "CELL ID")); //........................................... - //start iterative process of scaling of cells + // start iterative process of scaling of cells //........................................... for (int iter = 1; iter < 5; iter++) { // array of vectors for calculating the mean hits per col/row @@ -48,9 +46,8 @@ boostHisto EMCALCalibExtractor::buildHitAndEnergyMeanScaled(double emin, double for (int cellID = 0; cellID < mNcells; cellID++) { auto tempSlice = boost::histogram::algorithm::reduce(cellAmplitude, boost::histogram::algorithm::shrink(cellID, cellID), boost::histogram::algorithm::shrink(emin, emax)); - auto geo = Geometry::GetInstance(); // (0 - row, 1 - column) - auto position = geo->GlobalRowColFromIndex(cellID); + auto position = mGeometry->GlobalRowColFromIndex(cellID); int row = std::get<0>(position); int col = std::get<1>(position); @@ -100,11 +97,10 @@ boostHisto EMCALCalibExtractor::buildHitAndEnergyMeanScaled(double emin, double auto colResult = o2::utils::fitBoostHistoWithGaus(hEnergyCol); double meanValCol = colResult.at(1); - //Scale each cell by the deviation of the mean of the column and the global mean + // Scale each cell by the deviation of the mean of the column and the global mean for (int iCell = 0; iCell < mNcells; iCell++) { - auto geo = Geometry::GetInstance(); // (0 - row, 1 - column) - auto position = geo->GlobalRowColFromIndex(iCell); + auto position = mGeometry->GlobalRowColFromIndex(iCell); int col = std::get<1>(position); if (hEnergyCol.at(col) > 0.) { // will need to change the 100 depending on the number of energy bins we end up having @@ -114,11 +110,10 @@ boostHisto EMCALCalibExtractor::buildHitAndEnergyMeanScaled(double emin, double } } - //Scale each cell by the deviation of the mean of the row and the global mean + // Scale each cell by the deviation of the mean of the row and the global mean for (int iCell = 0; iCell < mNcells; iCell++) { - auto geo = Geometry::GetInstance(); // (0 - row, 1 - column) - auto position = geo->GlobalRowColFromIndex(iCell); + auto position = mGeometry->GlobalRowColFromIndex(iCell); int row = std::get<0>(position); if (hEnergyRow.at(row) > 0.) { // will need to change the 100 depending on the number of energy bins we end up having @@ -158,7 +153,59 @@ boostHisto EMCALCalibExtractor::buildHitAndEnergyMeanScaled(double emin, double return eSumHistoScaled; } + +//____________________________________________ +void EMCALCalibExtractor::checkMaskSM(o2::emcal::BadChannelMap& bcm) +{ + + for (unsigned int i = 0; i < mBadCellFracSM.size(); ++i) { + if (mGeometry->GetSMType(i) == o2::emcal::EMCALSMType::EMCAL_STANDARD) { + mBadCellFracSM[i] /= 1152.; + } else if (mGeometry->GetSMType(i) == o2::emcal::EMCALSMType::EMCAL_THIRD || mGeometry->GetSMType(i) == o2::emcal::EMCALSMType::DCAL_EXT) { + mBadCellFracSM[i] /= 384.; + } else if (mGeometry->GetSMType(i) == o2::emcal::EMCALSMType::DCAL_STANDARD) { + mBadCellFracSM[i] /= 768.; + } + } + for (unsigned int i = 0; i < mNcells; ++i) { + if (mBadCellFracSM[mGeometry->GetSuperModuleNumber(i)] > EMCALCalibParams::Instance().fracMaskSMFully_bc) { + if (bcm.getChannelStatus(i) == o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL) { // only mask good cells, to keep information about dead channels + bcm.addBadChannel(i, o2::emcal::BadChannelMap::MaskType_t::BAD_CELL); + } + } + } +} + +//____________________________________________ +void EMCALCalibExtractor::checkMaskFEC(o2::emcal::BadChannelMap& bcm) +{ + for (unsigned int iSM = 0; iSM < mBadCellFracFEC.size(); ++iSM) { + for (unsigned int iFEC = 0; iFEC < mBadCellFracFEC[iSM].size(); ++iFEC) { + mBadCellFracFEC[iSM][iFEC] /= 32.; // 32 channels per FEC + } + } + + for (unsigned int i = 0; i < mNcells; ++i) { + if (mBadCellFracFEC[mGeometry->GetSuperModuleNumber(i)][getFECNumberInSM(i)] > EMCALCalibParams::Instance().fracMaskFECFully_bc) { + if (bcm.getChannelStatus(i) == o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL) { // only mask good cells, to keep information about dead channels + bcm.addBadChannel(i, o2::emcal::BadChannelMap::MaskType_t::BAD_CELL); + } + } + } +} + //____________________________________________ +unsigned int EMCALCalibExtractor::getFECNumberInSM(int absCellID) const +{ + std::tuple RowCol = mGeometry->GlobalRowColFromIndex(absCellID); + std::tuple PosInSM = mGeometry->GetPositionInSupermoduleFromGlobalRowCol(std::get<0>(RowCol), std::get<1>(RowCol)); + int iSM = std::get<0>(PosInSM); + int col = std::get<1>(PosInSM); + int row = std::get<2>(PosInSM); + int FECid = static_cast(row / 4.) + 12 * static_cast(col / 8.); + LOG(debug) << "FECid " << FECid << " iSM " << iSM << " row " << row << " col " << col; + return FECid; +} } // end namespace emcal -} //end namespace o2 +} // end namespace o2 diff --git a/Detectors/EMCAL/calibration/src/EMCALCalibrationLinkDef.h b/Detectors/EMCAL/calibration/src/EMCALCalibrationLinkDef.h index 9964f61f095fe..0cc0451ed81b7 100644 --- a/Detectors/EMCAL/calibration/src/EMCALCalibrationLinkDef.h +++ b/Detectors/EMCAL/calibration/src/EMCALCalibrationLinkDef.h @@ -26,5 +26,6 @@ #pragma link C++ class o2::emcal::EMCALCalibParams + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::emcal::EMCALCalibParams> + ; #pragma link C++ class o2::emcal::EMCDCSProcessor + ; +#pragma link C++ class o2::emcal::PedestalProcessorData + ; #endif diff --git a/Detectors/EMCAL/calibration/src/EMCALChannelData.cxx b/Detectors/EMCAL/calibration/src/EMCALChannelData.cxx index 178f3e23390ba..81927ce497de6 100644 --- a/Detectors/EMCAL/calibration/src/EMCALChannelData.cxx +++ b/Detectors/EMCAL/calibration/src/EMCALChannelData.cxx @@ -23,6 +23,9 @@ // #include // #include // #include +#if (defined(WITH_OPENMP) && !defined(__CLING__)) +#include +#endif namespace o2 { @@ -36,7 +39,7 @@ using boost::histogram::indexed; //_____________________________________________ void EMCALChannelData::PrintStream(std::ostream& stream) const { - stream << "EMCAL Cell ID: " << mHisto << "\n"; + stream << "EMCAL Cell ID: " << mHisto[0] << "\n"; } //_____________________________________________ std::ostream& operator<<(std::ostream& stream, const EMCALChannelData& emcdata) @@ -47,19 +50,72 @@ std::ostream& operator<<(std::ostream& stream, const EMCALChannelData& emcdata) //_____________________________________________ void EMCALChannelData::fill(const gsl::span data) { - //the fill function is called once per event + // the fill function is called once per event mEvents++; - for (auto cell : data) { - double cellEnergy = cell.getEnergy(); - int id = cell.getTower(); - LOG(debug) << "inserting in cell ID " << id << ": energy = " << cellEnergy; - mHisto(cellEnergy, id); - mNEntriesInHisto++; - - if (cellEnergy > o2::emcal::EMCALCalibParams::Instance().minCellEnergyTime_bc) { - double cellTime = cell.getTimeStamp(); - LOG(debug) << "inserting in cell ID " << id << ": time = " << cellTime; - mHistoTime(cellTime, id); + + if (data.size() == 0) { + return; + } + + auto fillfunction = [this](int thread, const gsl::span& data, double minCellEnergy, double minCellEnergyTime) { + LOG(debug) << "filling in thread " << thread << " ncells = " << data.size(); + auto& mCurrentHist = mHisto[thread]; + auto& mCurrentHistTime = mHistoTime[thread]; + unsigned int nEntries = 0; // counter set inside function increases speed compared to simply using mVecNEntriesInHisto[thread]. Added to global counter at end of function + for (const auto& cell : data) { + int id = cell.getTower(); + double cellEnergy = cell.getEnergy(); + + if (mApplyGainCalib) { + LOG(debug) << " gain calib factor for cell " << id << " = " << mArrGainCalibFactors[id]; + cellEnergy *= mArrGainCalibFactors[id]; + } + + if (cellEnergy < minCellEnergy) { + LOG(debug) << "skipping cell ID " << id << ": with energy = " << cellEnergy << " below threshold of " << minCellEnergy; + continue; + } + + LOG(debug) << "inserting in cell ID " << id << ": energy = " << cellEnergy; + mCurrentHist(cellEnergy, id); + nEntries++; + + if (cellEnergy > minCellEnergyTime) { + double cellTime = cell.getTimeStamp(); + LOG(debug) << "inserting in cell ID " << id << ": time = " << cellTime; + mCurrentHistTime(cellTime, id); + } + } + mVecNEntriesInHisto[thread] += nEntries; + }; + + std::vector> ranges(mNThreads); + auto size_per_thread = static_cast(std::ceil((static_cast(data.size()) / mNThreads))); + unsigned int currentfirst = 0; + for (int ithread = 0; ithread < mNThreads; ithread++) { + unsigned int nelements = std::min(size_per_thread, static_cast(data.size() - 1 - currentfirst)); + ranges[ithread] = data.subspan(currentfirst, nelements); + currentfirst += nelements; + } + + double minCellEnergy = o2::emcal::EMCALCalibParams::Instance().minCellEnergy_bc; + double minCellEnergyTime = o2::emcal::EMCALCalibParams::Instance().minCellEnergyTime_bc; + +#if (defined(WITH_OPENMP) && !defined(__CLING__)) + LOG(debug) << "Number of threads that will be used = " << mNThreads; +#pragma omp parallel for num_threads(mNThreads) +#else + LOG(debug) << "OPEN MP will not be used for the bad channel calibration"; +#endif + for (int ithread = 0; ithread < mNThreads; ithread++) { + fillfunction(ithread, ranges[ithread], minCellEnergy, minCellEnergyTime); + } + + // only sum up entries if needed + if (!o2::emcal::EMCALCalibParams::Instance().useNEventsForCalib_bc) { + for (auto& nEntr : mVecNEntriesInHisto) { + mNEntriesInHisto += nEntr; + nEntr = 0; } } } @@ -69,12 +125,12 @@ void EMCALChannelData::print() LOG(debug) << *this; } //_____________________________________________ -void EMCALChannelData::merge(const EMCALChannelData* prev) +void EMCALChannelData::merge(EMCALChannelData* prev) { mEvents += prev->getNEvents(); mNEntriesInHisto += prev->getNEntriesInHisto(); - mHisto += prev->getHisto(); - mHistoTime += prev->getHistoTime(); + mHisto[0] += prev->getHisto(); + mHistoTime[0] += prev->getHisto(); } //_____________________________________________ @@ -82,10 +138,13 @@ bool EMCALChannelData::hasEnoughData() const { bool enough = false; - LOG(debug) << "mNEntriesInHisto: " << mNEntriesInHisto << " needed: " << EMCALCalibParams::Instance().minNEntries_bc << " mEvents = " << mEvents; - // use enrties in histogram for calibration - if (!EMCALCalibParams::Instance().useNEventsForCalib_bc && mNEntriesInHisto > EMCALCalibParams::Instance().minNEntries_bc) { - enough = true; + LOG(debug) << "mNEntriesInHisto: " << mNEntriesInHisto * mNThreads << " needed: " << EMCALCalibParams::Instance().minNEntries_bc << " mEvents = " << mEvents; + // use entries in histogram for calibration + if (!EMCALCalibParams::Instance().useNEventsForCalib_bc) { + long unsigned int nEntries = 0; + if (mNEntriesInHisto > EMCALCalibParams::Instance().minNEntries_bc) { + enough = true; + } } // use number of events (from emcal trigger record) for calibration if (EMCALCalibParams::Instance().useNEventsForCalib_bc && mEvents > EMCALCalibParams::Instance().minNEvents_bc) { @@ -98,7 +157,7 @@ bool EMCALChannelData::hasEnoughData() const //_____________________________________________ void EMCALChannelData::analyzeSlot() { - mOutputBCM = mCalibExtractor->calibrateBadChannels(mEsumHisto, mHistoTime); + mOutputBCM = mCalibExtractor->calibrateBadChannels(mEsumHisto, getHistoTime()); } //____________________________________________ diff --git a/Detectors/EMCAL/calibration/src/EMCALPedestalHelper.cxx b/Detectors/EMCAL/calibration/src/EMCALPedestalHelper.cxx new file mode 100644 index 0000000000000..38a6e386c9ca6 --- /dev/null +++ b/Detectors/EMCAL/calibration/src/EMCALPedestalHelper.cxx @@ -0,0 +1,199 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "EMCALCalibration/EMCALPedestalHelper.h" +using namespace o2::emcal; + +std::vector EMCALPedestalHelper::createPedestalInstruction(const Pedestal& obj, const int runNum) +{ + + setZero(); + + o2::emcal::Geometry* geo = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); + o2::emcal::MappingHandler mapper; + + for (int itower = 0; itower < 17664; itower++) { + auto [ddl, row, col] = geo->getOnlineID(itower); + auto [sm, mod, iphi, ieta] = geo->GetCellIndex(itower); + const auto& mapping = mapper.getMappingForDDL(ddl); + int ircu = ddl % 2; + auto addressLG = mapping.getHardwareAddress(row, col, o2::emcal::ChannelType_t::LOW_GAIN), + addressHG = mapping.getHardwareAddress(row, col, o2::emcal::ChannelType_t::HIGH_GAIN); + auto fecLG = o2::emcal::Channel::getFecIndexFromHwAddress(addressLG), + fecHG = o2::emcal::Channel::getFecIndexFromHwAddress(addressHG), + branchLG = o2::emcal::Channel::getBranchIndexFromHwAddress(addressLG), + branchHG = o2::emcal::Channel::getBranchIndexFromHwAddress(addressHG), + chipLG = o2::emcal::Channel::getAltroIndexFromHwAddress(addressLG), + chipHG = o2::emcal::Channel::getAltroIndexFromHwAddress(addressHG), + channelLG = o2::emcal::Channel::getChannelIndexFromHwAddress(addressLG), + channelHG = o2::emcal::Channel::getChannelIndexFromHwAddress(addressHG); + fMeanPed[sm][ircu][branchHG][fecHG][chipHG][channelHG] = obj.getPedestalValue(itower, false, false); + fMeanPed[sm][ircu][branchLG][fecLG][chipLG][channelLG] = obj.getPedestalValue(itower, true, false); + } + + for (int iledmon = 0; iledmon < 480; iledmon++) { + int sm = iledmon / 24, + col = iledmon % 24, + ircu = 0, // LEDMONS always on RCU 0 + iddl = 2 * sm + ircu; + const auto& mapping = mapper.getMappingForDDL(iddl); + auto addressLG = mapping.getHardwareAddress(0, col, o2::emcal::ChannelType_t::LEDMON), + addressHG = mapping.getHardwareAddress(1, col, o2::emcal::ChannelType_t::LEDMON); + auto fecLG = o2::emcal::Channel::getFecIndexFromHwAddress(addressLG), + fecHG = o2::emcal::Channel::getFecIndexFromHwAddress(addressHG), + branchLG = o2::emcal::Channel::getBranchIndexFromHwAddress(addressLG), + branchHG = o2::emcal::Channel::getBranchIndexFromHwAddress(addressHG), + chipLG = o2::emcal::Channel::getAltroIndexFromHwAddress(addressLG), + chipHG = o2::emcal::Channel::getAltroIndexFromHwAddress(addressHG), + channelLG = o2::emcal::Channel::getChannelIndexFromHwAddress(addressLG), + channelHG = o2::emcal::Channel::getChannelIndexFromHwAddress(addressHG); + fMeanPed[sm][ircu][branchHG][fecHG][chipHG][channelHG] = obj.getPedestalValue(iledmon, false, true); + fMeanPed[sm][ircu][branchLG][fecLG][chipLG][channelLG] = obj.getPedestalValue(iledmon, true, true); + } + + return createInstructionString(runNum); +} + +void EMCALPedestalHelper::setZero() +{ + for (int ism = 0; ism < kNSM; ism++) { + for (int ircu = 0; ircu < kNRCU; ircu++) { + for (int ibranch = 0; ibranch < kNBranch; ibranch++) { + for (int ifec = 0; ifec < kNFEC; ifec++) { + for (int ichip = 0; ichip < kNChip; ichip++) { + for (int ichan = 0; ichan < kNChan; ichan++) { + fMeanPed[ism][ircu][ibranch][ifec][ichip][ichan] = 0; + } + } + } + } + } + } +} + +std::vector EMCALPedestalHelper::createInstructionString(const int runNum) +{ + std::stringstream fout; + + if (runNum > 0) { + fout << runNum << std::endl; + } + + unsigned int lineValue = 0; + + const unsigned int FECheaderCode = 0xC0000000; + // const unsigned int FECwordCode = 0x80000000; + const unsigned int FEClineCode = 0x40000000; + + const unsigned int TrailerLineCode = 0xFFFFFFFF; + + short iSM = 0; + short iRCU = 0; + short ibranch = 0; + short iFEC = 0; + short ichip = 0; + short ichan = 0; + short Ped = 0; + short iDTC = 0; + + for (iSM = 0; iSM < kNSM; iSM++) { + int iside = iSM % 2; + int isect = iSM / 2; + if (iSM > 11) { + isect += 3; // skip non-installed sectors + } + + std::bitset activeDTC; + for (iDTC = 0; iDTC < kNDTC; iDTC++) { + if (iDTC == 10 || iDTC == 20 || iDTC == 30) { // skip TRU + activeDTC[iDTC] = 0; + } else { + if (iSM < 10) { // not special third SMs or DCal SMs + activeDTC[iDTC] = 1; + } else { + if (iSM == 10 || iSM == 19) { // SMA5 or SMC12 + if (iDTC < 14) { + activeDTC[iDTC] = 1; + } else { + activeDTC[iDTC] = 0; + } + } else if (iSM == 11 || iSM == 18) { // SMC5 or SMA12 + if (iDTC == 0 || iDTC >= 27) { + activeDTC[iDTC] = 1; + } else { + activeDTC[iDTC] = 0; + } + } else { + // DCal... no FECs in 9,11-13, 23-26, 36-39 + if ((iDTC >= 9 && iDTC <= 13) || (iDTC >= 23 && iDTC <= 26) || + (iDTC >= 36 && iDTC <= 39)) { + activeDTC[iDTC] = 0; + } else { + activeDTC[iDTC] = 1; + } + } // DCal + } // non-EMCal + } // non-TRU + } + + // OK, let's generate the files for all active FECs/DTCs + for (iDTC = 0; iDTC < kNDTC; iDTC++) { + if (activeDTC[iDTC] == 0) { + continue; + } + + lineValue = FECheaderCode | isect << 9 | iside << 8 | iDTC; + fout << lineValue << std::endl; + + iRCU = iDTC / 20; + ibranch = (iDTC % 20) / 10; + iFEC = iDTC % 10; + int ipos = iFEC + 10 * ibranch; + + int dtcselUpper = 0; + int dtcselLower = 0; + if (iRCU == 0) { + dtcselLower = (1 << ipos); + } else { // crate == 1 + dtcselUpper = (1 << ipos); + } + + for (ichip = 0; ichip < kNChip; ichip++) { // ALTRO 0,2,3,4 + if (ichip != 1) { + for (ichan = 0; ichan < kNChan; ichan++) { + if (iFEC != 0 || (ichan < 8 || ichan > 11)) { + Ped = fMeanPed[iSM][iRCU][ibranch][iFEC][ichip][ichan]; + int writeAddr = (ichip << 4) | ichan; + lineValue = FEClineCode | (writeAddr << 12) | Ped; + fout << lineValue << std::endl; + } + } + } + } // chip + + } // iDTC + } // iSM + + if (runNum > 0) { + fout << TrailerLineCode << std::endl; + } + + const std::string instructionString(fout.str()); + std::vector output(instructionString.begin(), instructionString.end()); + return output; +} + +void EMCALPedestalHelper::dumpInstructions(const std::string_view filename, const gsl::span& data) +{ + std::ofstream fout(filename.data()); + fout << data.data(); + fout.close(); +} \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx new file mode 100644 index 0000000000000..02e25696f161d --- /dev/null +++ b/Detectors/EMCAL/calibration/src/EMCALTempCalibExtractor.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "EMCALCalibration/EMCALTempCalibExtractor.h" +#include "EMCALCalib/CalibDB.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include + +namespace o2 +{ +namespace emcal +{ + +void EMCALTempCalibExtractor::InitializeFromCCDB(std::string path, uint64_t timestamp) +{ + + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t maxRunNr = 1000000; + if (timestamp < maxRunNr) { + LOG(info) << "assuming input is run " << timestamp << " will convert it to timstamp"; + auto [sor, eor] = ccdbMgr.getRunDuration(timestamp); + uint64_t sixtySec = 60000; + timestamp = eor - sixtySec; // safety margin of 1min at EOR + LOG(info) << "set timestamp to " << timestamp; + } + + o2::emcal::CalibDB calibdb("http://alice-ccdb.cern.ch"); + std::map metadata; + auto tempSensorData = calibdb.readTemperatureSensorData(timestamp, metadata); + + // also obtain cell dependent correction factors + TempCalibrationParams* params = ccdbMgr.getForTimeStamp(path, timestamp); + + std::map mapSMTemperature; + for (unsigned short i = 0; i < mNCells; ++i) { + const unsigned short iSM = mGeometry->GetSuperModuleNumber(i); + if (mapSMTemperature.count(iSM) == 0) { + mapSMTemperature[iSM] = getTemperatureForSM(iSM, tempSensorData); + } + float corrFac = params->getTempCalibParamA0(i) + params->getTempCalibParamSlope(i) * mapSMTemperature[iSM]; + mGainCalibFactors[i] = corrFac; + } +} + +float EMCALTempCalibExtractor::getTemperatureForSM(const unsigned short iSM, o2::emcal::ElmbData* ElmbData) const +{ + if (iSM < 0 || iSM > 20) { + LOG(error) << "SM " << iSM << "does not exist!"; // could be replaced with a proper exception + return 0.; + } + std::vector vecSensorID = getSensorsForSM(iSM); + + // Obtain temperature for these sensors + std::vector vecTemperature; + for (const auto& iSensor : vecSensorID) { + float temp = ElmbData->getMean(iSensor); + if (temp < mAcceptedTempRange[0] || temp > mAcceptedTempRange[1]) { + continue; + } + vecTemperature.push_back(temp); + } + + const unsigned int nEntries = vecTemperature.size(); + if (nEntries == 0) { + LOG(warning) << "No sensor data between " << mAcceptedTempRange[0] << " and " << mAcceptedTempRange[1] << "degree found... for SM " << iSM << " Setting to default 20 degree"; + return 20.; // + } + + // get median energy + float tempSM = 0.; + if (mUseMedian) { + std::sort(vecTemperature.begin(), vecTemperature.end()); + if (nEntries % 2 == 0) { + // even number of elements: average the two middle ones + tempSM = (vecTemperature[nEntries / 2 - 1] + vecTemperature[nEntries / 2]) / 2.0; + } else { + // odd number of elements: return the middle one + tempSM = vecTemperature[nEntries / 2]; + } + } else { // use Mean temperature + float sum = std::accumulate(vecTemperature.begin(), vecTemperature.end(), 0.0); + tempSM = sum / vecTemperature.size(); + } + return tempSM; +} + +float EMCALTempCalibExtractor::getGainCalibFactor(const unsigned short cellID) const +{ + if (cellID >= mNCells) { + LOG(error) << "cell ID" << cellID << " does not exist"; + return 1.; + } + return mGainCalibFactors[cellID]; +} + +std::vector EMCALTempCalibExtractor::getSensorsForSM(const unsigned short iSM) const +{ + unsigned short nSensors = 8; + if (iSM == 10 || iSM == 11 || iSM == 18 || iSM == 19) { // 1/3 SM of EMCal only have 4 sensors + nSensors = 4; + } + + std::vector vecSensorID; + for (unsigned short iELMBSensor = iSM * 8; iELMBSensor < iSM * 8 + nSensors; iELMBSensor++) { + vecSensorID.push_back(iELMBSensor); + } + return vecSensorID; +} + +void EMCALTempCalibExtractor::setAcceptedEnergyRange(float low, float high) +{ + mAcceptedTempRange[0] = low; + mAcceptedTempRange[1] = high; +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/EMCALTimeCalibData.cxx b/Detectors/EMCAL/calibration/src/EMCALTimeCalibData.cxx index 6c72e4cd5199f..c75bc1f1ac48a 100644 --- a/Detectors/EMCAL/calibration/src/EMCALTimeCalibData.cxx +++ b/Detectors/EMCAL/calibration/src/EMCALTimeCalibData.cxx @@ -36,7 +36,7 @@ using boost::histogram::indexed; //_____________________________________________ void EMCALTimeCalibData::PrintStream(std::ostream& stream) const { - stream << "EMCAL Cell ID: " << mTimeHisto << "\n"; + stream << "EMCAL Cell ID: " << mTimeHisto[0] << "\n"; } //_____________________________________________ void EMCALTimeCalibData::print() @@ -50,11 +50,11 @@ std::ostream& operator<<(std::ostream& stream, const EMCALTimeCalibData& emcdata return stream; } //_____________________________________________ -void EMCALTimeCalibData::merge(const EMCALTimeCalibData* prev) +void EMCALTimeCalibData::merge(EMCALTimeCalibData* prev) { mEvents += prev->getNEvents(); mNEntriesInHisto += prev->getNEntriesInHisto(); - mTimeHisto += prev->getHisto(); + mTimeHisto[0] += prev->getHisto(); } //_____________________________________________ bool EMCALTimeCalibData::hasEnoughData() const @@ -73,20 +73,64 @@ bool EMCALTimeCalibData::hasEnoughData() const return enough; } + //_____________________________________________ void EMCALTimeCalibData::fill(const gsl::span data) { // the fill function is called once per event mEvents++; - for (auto cell : data) { - double cellEnergy = cell.getEnergy(); - double cellTime = cell.getTimeStamp(); - int id = cell.getTower(); - if (cellEnergy > EMCALCalibParams::Instance().minCellEnergy_tc) { - LOG(debug) << "inserting in cell ID " << id << ": cellTime = " << cellTime; - mTimeHisto(cellTime, id); - mNEntriesInHisto++; + if (data.size() == 0) { + return; + } + auto fillfunction = [this](int thread, const gsl::span data, double minCellEnergy) { + LOG(debug) << "filling in thread " << thread << " ncells = " << data.size(); + auto& mCurrentHist = mTimeHisto[thread]; + unsigned int nEntries = 0; + for (auto cell : data) { + double cellEnergy = cell.getEnergy(); + double cellTime = cell.getTimeStamp(); + int id = cell.getTower(); + if (mApplyGainCalib) { + LOG(debug) << " gain calib factor for cell " << id << " = " << mGainCalibFactors->getGainCalibFactors(id); + cellEnergy *= mGainCalibFactors->getGainCalibFactors(id); + } + if (cellEnergy > minCellEnergy) { + LOG(debug) << "inserting in cell ID " << id << ": cellTime = " << cellTime; + mCurrentHist(cellTime, id); + nEntries++; + } + } + mVecNEntriesInHisto[thread] += nEntries; + }; + + std::vector> ranges(mNThreads); + auto size_per_thread = static_cast(std::ceil((static_cast(data.size()) / mNThreads))); + unsigned int currentfirst = 0; + for (int ithread = 0; ithread < mNThreads; ithread++) { + unsigned int nelements = std::min(size_per_thread, static_cast(data.size() - 1 - currentfirst)); + ranges[ithread] = data.subspan(currentfirst, nelements); + currentfirst += nelements; + LOG(debug) << "currentfirst " << currentfirst << " nelements " << nelements; + } + + double minCellEnergy = EMCALCalibParams::Instance().minCellEnergy_tc; + +#if (defined(WITH_OPENMP) && !defined(__CLING__)) + LOG(debug) << "Number of threads that will be used = " << mNThreads; +#pragma omp parallel for num_threads(mNThreads) +#else + LOG(debug) << "OPEN MP will not be used for the bad channel calibration"; +#endif + for (int ithread = 0; ithread < mNThreads; ithread++) { + fillfunction(ithread, ranges[ithread], minCellEnergy); + } + + // only sum up entries if needed + if (!o2::emcal::EMCALCalibParams::Instance().useNEventsForCalib_tc) { + for (auto& nEntr : mVecNEntriesInHisto) { + mNEntriesInHisto += nEntr; + nEntr = 0; } } } diff --git a/Detectors/EMCAL/calibration/src/PedestalCalibDevice.cxx b/Detectors/EMCAL/calibration/src/PedestalCalibDevice.cxx new file mode 100644 index 0000000000000..2f4c6ee204a31 --- /dev/null +++ b/Detectors/EMCAL/calibration/src/PedestalCalibDevice.cxx @@ -0,0 +1,116 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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/VerbosityConfig.h" +#include "DetectorsRaw/RDHUtils.h" +#include "EMCALBase/Geometry.h" +#include "Framework/TimingInfo.h" +#include "EMCALCalibration/PedestalCalibDevice.h" +#include "EMCALReconstruction/AltroDecoder.h" +#include "EMCALReconstruction/RawReaderMemory.h" +#include "DetectorsCalibration/Utils.h" +#include "EMCALCalib/CalibDB.h" +#include "Framework/ConcreteDataMatcher.h" +#include "CommonUtils/MemFileHelper.h" + +#include + +using namespace o2::emcal; + +void PedestalCalibDevice::init(o2::framework::InitContext& ctx) +{ + LOG(debug) << "[EMCALPedestalCalibDevice - init] Initialize converter "; + if (!mGeometry) { + mGeometry = Geometry::GetInstanceFromRunNumber(300000); + } + if (!mGeometry) { + LOG(error) << "Failure accessing geometry"; + } + + resetStartTS(); +} + +void PedestalCalibDevice::run(o2::framework::ProcessingContext& ctx) +{ + if (!mRun) { + const auto& tinfo = ctx.services().get(); + if (tinfo.runNumber != 0) { + mRun = tinfo.runNumber; + } + } + + constexpr auto originEMC = o2::header::gDataOriginEMC; + auto data = ctx.inputs().get(getPedDataBinding()); + LOG(debug) << "adding pedestal data"; + mPedestalData += data; +} + +//________________________________________________________________ +void PedestalCalibDevice::sendData(o2::framework::EndOfStreamContext& ec, const Pedestal& data) const +{ + LOG(info) << "sending pedestal data"; + constexpr auto originEMC = o2::header::gDataOriginEMC; + + std::map md; + auto clName = o2::utils::MemFileHelper::getClassName(data); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + o2::ccdb::CcdbObjectInfo objInfo(o2::emcal::CalibDB::getCDBPathChannelPedestals(), clName, flName, md, mStartTS, o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP, true); + + auto image = o2::ccdb::CcdbApi::createObjectImage(&data, &objInfo); + + ec.outputs().snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBPayload, "EMC_PEDCALIB", 0}, *image.get()); + ec.outputs().snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EMC_PEDCALIB", 0}, objInfo); + + // the following goes to the DCS ccdb + EMCALPedestalHelper helper; + std::vector vecPedData = helper.createPedestalInstruction(data, mAddRunNumber ? mRun : -1); + if (mDumpToFile) { + helper.dumpInstructions("EMCAL-Pedestals.txt", vecPedData); + } + + auto clNameDCS = o2::utils::MemFileHelper::getClassName(vecPedData); + auto flNameDCS = o2::ccdb::CcdbApi::generateFileName(clNameDCS); + o2::ccdb::CcdbObjectInfo objInfoDCS("EMC/Calib/PDData", clNameDCS, flNameDCS, md, mStartTS, o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP, true); + auto imageDCS = o2::ccdb::CcdbApi::createObjectImage(&vecPedData, &objInfoDCS); + + ec.outputs().snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBPayload, "EMC_PEDCALIBSTR", 1}, *imageDCS.get()); + ec.outputs().snapshot(o2::framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EMC_PEDCALIBSTR", 1}, objInfoDCS); +} + +//________________________________________________________________ +void PedestalCalibDevice::endOfStream(o2::framework::EndOfStreamContext& ec) +{ + + Pedestal pedObj = mCalibExtractor.extractPedestals(mPedestalData); + sendData(ec, pedObj); + // reset Timestamp (probably not needed as program will probably be terminated) + resetStartTS(); +} + +o2::framework::DataProcessorSpec o2::emcal::getPedestalCalibDevice(bool dumpToFile, bool addRunNum) +{ + + std::vector inputs; + inputs.emplace_back(o2::emcal::PedestalCalibDevice::getPedDataBinding(), o2::header::gDataOriginEMC, "PEDDATA", 0, o2::framework::Lifetime::Timeframe); + std::vector outputs; + outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EMC_PEDCALIB"}, o2::framework::Lifetime::Sporadic); + outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EMC_PEDCALIB"}, o2::framework::Lifetime::Sporadic); + + outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EMC_PEDCALIBSTR"}, o2::framework::Lifetime::Sporadic); + outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EMC_PEDCALIBSTR"}, o2::framework::Lifetime::Sporadic); + + return o2::framework::DataProcessorSpec{ + "PedestalCalibrator", + inputs, + outputs, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(dumpToFile, addRunNum)}, + o2::framework::Options{}}; +} \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/PedestalProcessorData.cxx b/Detectors/EMCAL/calibration/src/PedestalProcessorData.cxx new file mode 100644 index 0000000000000..33ef3319b2570 --- /dev/null +++ b/Detectors/EMCAL/calibration/src/PedestalProcessorData.cxx @@ -0,0 +1,122 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "EMCALCalibration/PedestalProcessorData.h" + +using namespace o2::emcal; + +PedestalProcessorData& PedestalProcessorData::operator+=(const PedestalProcessorData& other) +{ + for (std::size_t ichan{0}; ichan < mDataFECHG.size(); ++ichan) { + mDataFECHG[ichan] += other.mDataFECHG[ichan]; + mDataFECLG[ichan] += other.mDataFECLG[ichan]; + } + for (std::size_t ichan{0}; ichan < mDataLEDMONHG.size(); ++ichan) { + mDataLEDMONHG[ichan] += other.mDataLEDMONHG[ichan]; + mDataLEDMONLG[ichan] += other.mDataLEDMONLG[ichan]; + } + return *this; +} + +void PedestalProcessorData::fillADC(unsigned short adc, unsigned short tower, bool lowGain, bool LEDMON) +{ + auto maxentries = LEDMON ? mDataLEDMONHG.size() : mDataFECHG.size(); + if (tower >= maxentries) { + throw ChannelIndexException(tower, maxentries); + } + if (LEDMON) { + if (lowGain) { + mDataLEDMONLG[tower].add(adc); + } else { + mDataLEDMONHG[tower].add(adc); + } + } else { + if (lowGain) { + mDataFECLG[tower].add(adc); + } else { + mDataFECHG[tower].add(adc); + } + } +} + +PedestalProcessorData::PedestalValue PedestalProcessorData::getValue(unsigned short tower, bool lowGain, bool LEDMON) const +{ + auto maxentries = LEDMON ? mDataLEDMONHG.size() : mDataFECHG.size(); + if (tower >= maxentries) { + throw ChannelIndexException(tower, maxentries); + } + float mean, rms; + if (LEDMON) { + if (lowGain) { + return mDataLEDMONLG[tower].getMeanRMS2(); + } else { + return mDataLEDMONHG[tower].getMeanRMS2(); + } + } else { + if (lowGain) { + return mDataFECLG[tower].getMeanRMS2(); + } else { + return mDataFECHG[tower].getMeanRMS2(); + } + } +} + +int PedestalProcessorData::getEntriesForChannel(unsigned short tower, bool lowGain, bool LEDMON) const +{ + auto maxentries = LEDMON ? mDataLEDMONHG.size() : mDataFECHG.size(); + if (tower >= maxentries) { + throw ChannelIndexException(tower, maxentries); + } + float mean, rms; + if (LEDMON) { + if (lowGain) { + return mDataLEDMONLG[tower].n; + } else { + return mDataLEDMONHG[tower].n; + } + } else { + if (lowGain) { + return mDataFECLG[tower].n; + } else { + return mDataFECHG[tower].n; + } + } +} + +void PedestalProcessorData::reset() +{ + for (auto& acc : mDataFECHG) { + acc.clear(); + } + for (auto& acc : mDataFECLG) { + acc.clear(); + } + for (auto& acc : mDataLEDMONHG) { + acc.clear(); + } + for (auto& acc : mDataLEDMONLG) { + acc.clear(); + } +} + +PedestalProcessorData o2::emcal::operator+(const PedestalProcessorData& lhs, const PedestalProcessorData& rhs) +{ + PedestalProcessorData result(lhs); + result += rhs; + return result; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const PedestalProcessorData::ChannelIndexException& ex) +{ + stream << ex.what(); + return stream; +} \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/src/PedestalProcessorDevice.cxx b/Detectors/EMCAL/calibration/src/PedestalProcessorDevice.cxx new file mode 100644 index 0000000000000..90ddba0ffb75a --- /dev/null +++ b/Detectors/EMCAL/calibration/src/PedestalProcessorDevice.cxx @@ -0,0 +1,228 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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/VerbosityConfig.h" +#include "DetectorsRaw/RDHUtils.h" +#include "EMCALBase/Geometry.h" +#include "EMCALCalibration/PedestalProcessorDevice.h" +#include "EMCALReconstruction/AltroDecoder.h" +#include "EMCALReconstruction/RawReaderMemory.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/InputRecordWalker.h" + +#include + +using namespace o2::emcal; + +void PedestalProcessorDevice::init(o2::framework::InitContext& ctx) +{ + LOG(debug) << "[EMCALRawToCellConverter - init] Initialize converter "; + if (!mGeometry) { + mGeometry = Geometry::GetInstanceFromRunNumber(300000); + } + if (!mGeometry) { + LOG(error) << "Failure accessing geometry"; + } + + if (!mMapper) { + mMapper = std::unique_ptr(new o2::emcal::MappingHandler); + } + if (!mMapper) { + LOG(error) << "Failed to initialize mapper"; + } +} + +void PedestalProcessorDevice::run(o2::framework::ProcessingContext& ctx) +{ + constexpr auto originEMC = o2::header::gDataOriginEMC; + constexpr auto descRaw = o2::header::gDataDescriptionRawData; + + mPedestalData.reset(); + + if (isLostTimeframe(ctx)) { + sendData(ctx, mPedestalData); + return; + } + + std::vector filter{{"filter", framework::ConcreteDataTypeMatcher(originEMC, descRaw)}}; + for (const auto& rawData : framework::InputRecordWalker(ctx.inputs(), filter)) { + // Skip SOX headers + auto rdhblock = reinterpret_cast(rawData.payload); + if (o2::raw::RDHUtils::getHeaderSize(rdhblock) == static_cast(o2::framework::DataRefUtils::getPayloadSize(rawData))) { + continue; + } + + o2::emcal::RawReaderMemory rawreader(framework::DataRefUtils::as(rawData)); + rawreader.setRangeSRUDDLs(0, 39); + + // loop over all the DMA pages + while (rawreader.hasNext()) { + try { + rawreader.next(); + } catch (RawDecodingError& e) { + LOG(error) << e.what(); + if (e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_DECODING || e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_INVALID) { + // We must break in case of header decoding as the offset to the next payload is lost + // consequently the parser does not know where to continue leading to an infinity loop + break; + } + // We must skip the page as payload is not consistent + // otherwise the next functions will rethrow the exceptions as + // the page format does not follow the expected format + continue; + } + for (const auto& e : rawreader.getMinorErrors()) { + LOG(warning) << "Found minor Raw decoder error in FEE " << e.getFEEID() << ": " << RawDecodingError::getErrorCodeTitles(RawDecodingError::ErrorTypeToInt(e.getErrorType())); + } + + auto& header = rawreader.getRawHeader(); + auto feeID = raw::RDHUtils::getFEEID(header); + auto triggerbits = raw::RDHUtils::getTriggerType(header); + + if (feeID >= 40) { + continue; // skip STU ddl + } + + // use the altro decoder to decode the raw data, and extract the RCU trailer + AltroDecoder decoder(rawreader); + // check the words of the payload exception in altrodecoder + try { + decoder.decode(); + } catch (AltroDecoderError& e) { + LOG(error) << e.what(); + continue; + } + for (const auto& e : decoder.getMinorDecodingErrors()) { + LOG(warning) << e.what(); + } + + try { + + const auto& map = mMapper->getMappingForDDL(feeID); + uint16_t iSM = feeID / 2; + + // Loop over all the channels + int nBunchesNotOK = 0; + for (auto& chan : decoder.getChannels()) { + 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) { + LOG(error) << ex.what(); + continue; + } + + if (!(chantype == o2::emcal::ChannelType_t::HIGH_GAIN || chantype == o2::emcal::ChannelType_t::LOW_GAIN || chantype == o2::emcal::ChannelType_t::LEDMON)) { + continue; + } + + int CellID = -1; + bool isLowGain = false; + try { + if (chantype == o2::emcal::ChannelType_t::HIGH_GAIN || chantype == o2::emcal::ChannelType_t::LOW_GAIN) { + // high- / low-gain cell + CellID = getCellAbsID(iSM, iCol, iRow); + isLowGain = chantype == o2::emcal::ChannelType_t::LOW_GAIN; + } else { + CellID = geLEDMONAbsID(iSM, iCol); // Module index encoded in colum for LEDMONs + isLowGain = iRow == 0; // For LEDMONs gain type is encoded in the row (0 - low gain, 1 - high gain) + } + } catch (ModuleIndexException& e) { + LOG(error) << e.what(); + continue; + } + + // Fill pedestal object + for (auto& bunch : chan.getBunches()) { + for (auto e : bunch.getADC()) { + mPedestalData.fillADC(e, CellID, isLowGain, chantype == o2::emcal::ChannelType_t::LEDMON); + } + } + } + } catch (o2::emcal::MappingHandler::DDLInvalid& ddlerror) { + // Unable to catch mapping + LOG(error) << ddlerror.what(); + } + } + } + + sendData(ctx, mPedestalData); +} + +int PedestalProcessorDevice::getCellAbsID(int supermoduleID, int column, int row) const +{ + auto [phishift, etashift] = mGeometry->ShiftOnlineToOfflineCellIndexes(supermoduleID, row, column); + int cellID = mGeometry->GetAbsCellIdFromCellIndexes(supermoduleID, phishift, etashift); + if (cellID > 17664 || cellID < 0) { + throw ModuleIndexException(cellID, column, row, etashift, phishift); + } + return cellID; +} + +int PedestalProcessorDevice::geLEDMONAbsID(int supermoduleID, int moduleID) const +{ + if (moduleID >= o2::emcal::EMCAL_LEDREFS || moduleID < 0) { + throw ModuleIndexException(moduleID); + } + return supermoduleID * o2::emcal::EMCAL_LEDREFS + moduleID; +} + +bool PedestalProcessorDevice::isLostTimeframe(framework::ProcessingContext& ctx) const +{ + constexpr auto originEMC = header::gDataOriginEMC; + o2::framework::InputSpec dummy{"dummy", + framework::ConcreteDataMatcher{originEMC, + header::gDataDescriptionRawData, + 0xDEADBEEF}}; + static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously + for (const auto& ref : o2::framework::InputRecordWalker(ctx.inputs(), {dummy})) { + const auto dh = o2::framework::DataRefUtils::getHeader(ref); + auto payloadSize = o2::framework::DataRefUtils::getPayloadSize(ref); + if (payloadSize == 0) { + auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; + if (++contDeadBeef <= maxWarn) { + LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, + contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); + } + return true; + } + } + contDeadBeef = 0; // if good data, reset the counter + return false; +} + +void PedestalProcessorDevice::sendData(framework::ProcessingContext& ctx, const PedestalProcessorData& data) const +{ + constexpr auto originEMC = o2::header::gDataOriginEMC; + ctx.outputs().snapshot(framework::Output{originEMC, "PEDDATA", 0}, data); +} + +o2::framework::DataProcessorSpec o2::emcal::getPedestalProcessorDevice(bool askDistSTF) +{ + constexpr auto originEMC = o2::header::gDataOriginEMC; + std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originEMC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Timeframe}}; + std::vector outputs; + outputs.emplace_back(originEMC, "PEDDATA", 0, o2::framework::Lifetime::Timeframe); + if (askDistSTF) { + inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); + } + + return o2::framework::DataProcessorSpec{ + "PedestalProcessor", + inputs, + outputs, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask()}, + o2::framework::Options{}}; +} \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/test/testPedestalProcessorData.cxx b/Detectors/EMCAL/calibration/test/testPedestalProcessorData.cxx new file mode 100644 index 0000000000000..e591249fcea5b --- /dev/null +++ b/Detectors/EMCAL/calibration/test/testPedestalProcessorData.cxx @@ -0,0 +1,155 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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_EMCAL_Calibration +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include +#include "EMCALCalibration/PedestalProcessorData.h" +#include +#include +#include + +namespace o2 +{ + +namespace emcal +{ + +const int NUMBERFECCHANNELS = 17664, + NUMBERLEDMONCHANNELS = 480; + +void compare(const PedestalProcessorData& testobject, const TProfile& refFECHG, const TProfile& refFECLG, const TProfile& refLEDMONHG, const TProfile& refLEDMONLG, bool testCount, bool verbose) +{ + const double PRECISION = FLT_EPSILON; + const double PRECISIONMEAN = FLT_EPSILON; + for (std::size_t ichan = 0; ichan < NUMBERFECCHANNELS; ichan++) { + auto [meanHG, rmsHG] = testobject.getValue(ichan, false, false); + auto [meanLG, rmsLG] = testobject.getValue(ichan, true, false); + if (verbose) { + std::cout << "Channel " << ichan << ", mean HG " << meanHG << ", RMS HG " << std::sqrt(rmsHG) << ", entries HG " << testobject.getEntriesForChannel(ichan, false, false) << " ref(mean " << refFECHG.GetBinContent(ichan + 1) << ", RMS " << refFECHG.GetBinError(ichan + 1) << ", entries " << refFECHG.GetBinEntries(ichan + 1) << ")" << std::endl; + std::cout << "Channel " << ichan << ", mean LG " << meanLG << ", RMS LG " << std::sqrt(rmsLG) << ", entries LG " << testobject.getEntriesForChannel(ichan, true, false) << " ref(mean " << refFECLG.GetBinContent(ichan + 1) << ", RMS " << refFECLG.GetBinError(ichan + 1) << ", entries " << refFECLG.GetBinEntries(ichan + 1) << ")" << std::endl; + } + BOOST_CHECK_LE(std::abs(meanHG - refFECHG.GetBinContent(ichan + 1)), PRECISIONMEAN); + BOOST_CHECK_LE(std::abs(meanLG - refFECLG.GetBinContent(ichan + 1)), PRECISIONMEAN); + BOOST_CHECK_LE(std::abs(std::sqrt(rmsHG) - refFECHG.GetBinError(ichan + 1)), PRECISION); + BOOST_CHECK_LE(std::abs(std::sqrt(rmsLG) - refFECLG.GetBinError(ichan + 1)), PRECISION); + if (testCount) { + BOOST_CHECK_EQUAL(testobject.getEntriesForChannel(ichan, false, false), refFECHG.GetBinEntries(ichan + 1)); + BOOST_CHECK_EQUAL(testobject.getEntriesForChannel(ichan, true, false), refFECLG.GetBinEntries(ichan + 1)); + } + } + for (std::size_t ichan = 0; ichan < NUMBERLEDMONCHANNELS; ichan++) { + auto [meanHG, rmsHG] = testobject.getValue(ichan, false, true); + auto [meanLG, rmsLG] = testobject.getValue(ichan, true, true); + if (verbose) { + std::cout << "LEDMON " << ichan << ", mean HG " << meanHG << ", RMS HG " << std::sqrt(rmsHG) << ", entries HG " << testobject.getEntriesForChannel(ichan, false, true) << " ref(mean " << refLEDMONHG.GetBinContent(ichan + 1) << ", RMS " << refLEDMONHG.GetBinError(ichan + 1) << ", entries " << refLEDMONHG.GetBinEntries(ichan + 1) << ")" << std::endl; + std::cout << "LEDMON " << ichan << ", mean LG " << meanLG << ", RMS LG " << std::sqrt(rmsLG) << ", entries LG " << testobject.getEntriesForChannel(ichan, true, true) << " ref(mean " << refLEDMONLG.GetBinContent(ichan + 1) << ", RMS " << refLEDMONLG.GetBinError(ichan + 1) << ", entries " << refLEDMONLG.GetBinEntries(ichan + 1) << ")" << std::endl; + } + BOOST_CHECK_LE(std::abs(meanHG - refLEDMONHG.GetBinContent(ichan + 1)), PRECISIONMEAN); + BOOST_CHECK_LE(std::abs(meanLG - refLEDMONLG.GetBinContent(ichan + 1)), PRECISIONMEAN); + BOOST_CHECK_LE(std::abs(std::sqrt(rmsHG) - refLEDMONHG.GetBinError(ichan + 1)), PRECISION); + BOOST_CHECK_LE(std::abs(std::sqrt(rmsLG) - refLEDMONLG.GetBinError(ichan + 1)), PRECISION); + if (testCount) { + BOOST_CHECK_EQUAL(testobject.getEntriesForChannel(ichan, false, true), refLEDMONHG.GetBinEntries(ichan + 1)); + BOOST_CHECK_EQUAL(testobject.getEntriesForChannel(ichan, true, true), refLEDMONLG.GetBinEntries(ichan + 1)); + } + } +} + +BOOST_AUTO_TEST_CASE(testPedestalProcessorData) +{ + // we compare with RMS, so the + TProfile refFECHG("refFECHG", "Reference FEC HG", NUMBERFECCHANNELS, -0.5, NUMBERFECCHANNELS - 0.5, "s"), + refFECLG("refFECLG", "Reference FEC LG", NUMBERFECCHANNELS, -0.5, NUMBERFECCHANNELS - 0.5, "s"), + refLEDMONHG("refLEDMONHG", "Reference LEDMON HG", NUMBERLEDMONCHANNELS, -0.5, NUMBERLEDMONCHANNELS - 0.5, "s"), + refLEDMONLG("refLEDMONLG", "Reference LEDMON LG", NUMBERLEDMONCHANNELS, -0.5, NUMBERLEDMONCHANNELS - 0.5, "s"); + refFECHG.Sumw2(); + refFECLG.Sumw2(); + refLEDMONHG.Sumw2(); + refLEDMONLG.Sumw2(); + + PedestalProcessorData testobject; + + const int NUMBERALTROSAMPLES = 15; + for (auto iev = 0; iev < 1000; iev++) { + for (int ichan = 0; ichan < NUMBERFECCHANNELS; ichan++) { + for (int isample = 0; isample < 15; isample++) { + // short adc_hg = static_cast(adcGeneratorHG(gen)), + // adc_lg = static_cast(adcGeneratorLG(gen)); + auto raw_hg = gRandom->Gaus(40, 6), + raw_lg = gRandom->Gaus(36, 10); + unsigned short adc_hg = static_cast(raw_hg > 0 ? raw_hg : 0), + adc_lg = static_cast(raw_lg > 0 ? raw_lg : 0); + refFECHG.Fill(ichan, adc_hg); + refFECLG.Fill(ichan, adc_lg); + testobject.fillADC(adc_hg, ichan, false, false); + testobject.fillADC(adc_lg, ichan, true, false); + } + } + } + for (auto iev = 0; iev < 1000; iev++) { + for (int ichan = 0; ichan < NUMBERLEDMONCHANNELS; ichan++) { + for (int isample = 0; isample < 15; isample++) { + // short adc_hg = static_cast(adcGeneratorLEDMONHG(gen)), + // adc_lg = static_cast(adcGeneratorLEDMONLG(gen)); + auto raw_hg = gRandom->Gaus(40, 6), + raw_lg = gRandom->Gaus(36, 10); + unsigned short adc_hg = static_cast(raw_hg > 0 ? raw_hg : 0), + adc_lg = static_cast(raw_lg > 0 ? raw_lg : 0); + refLEDMONHG.Fill(ichan, adc_hg); + refLEDMONLG.Fill(ichan, adc_lg); + testobject.fillADC(adc_hg, ichan, false, true); + testobject.fillADC(adc_lg, ichan, true, true); + } + } + } + + // Compare channels + compare(testobject, refFECHG, refFECLG, refLEDMONHG, refLEDMONLG, true, false); + + // Test operator+ + auto testdoubled = testobject + testobject; + TProfile refFECHGdouble(refFECHG), + refFECLGdouble(refFECLG), + refLEDMONHGdouble(refLEDMONHG), + refLEDMONLGdouble(refLEDMONLG); + refFECHGdouble.Add(&refFECHG); + refFECLGdouble.Add(&refFECLG); + refLEDMONHGdouble.Add(&refLEDMONHG); + refLEDMONLGdouble.Add(&refLEDMONLG); + compare(testdoubled, refFECHGdouble, refFECLGdouble, refLEDMONHGdouble, refLEDMONLGdouble, false, false); + + // Test reset function + auto testreset = testobject; + testreset.reset(); + for (std::size_t ichan = 0; ichan < NUMBERFECCHANNELS; ichan++) { + auto [meanHG, rmsHG] = testreset.getValue(ichan, false, false); + auto [meanLG, rmsLG] = testreset.getValue(ichan, true, false); + BOOST_CHECK_EQUAL(meanHG, 0.); + BOOST_CHECK_EQUAL(meanLG, 0.); + BOOST_CHECK_EQUAL(testreset.getEntriesForChannel(ichan, false, false), 0); + BOOST_CHECK_EQUAL(testreset.getEntriesForChannel(ichan, true, false), 0); + } + for (std::size_t ichan = 0; ichan < NUMBERLEDMONCHANNELS; ichan++) { + auto [meanHG, rmsHG] = testreset.getValue(ichan, false, true); + auto [meanLG, rmsLG] = testreset.getValue(ichan, true, true); + BOOST_CHECK_EQUAL(meanHG, 0.); + BOOST_CHECK_EQUAL(meanLG, 0.); + BOOST_CHECK_EQUAL(testreset.getEntriesForChannel(ichan, false, true), 0); + BOOST_CHECK_EQUAL(testreset.getEntriesForChannel(ichan, true, true), 0); + } +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/calibration/testWorkflow/EMCALChannelCalibratorSpec.h b/Detectors/EMCAL/calibration/testWorkflow/EMCALChannelCalibratorSpec.h index 8a97c0eca1ad2..2d6a9dc6c9d01 100644 --- a/Detectors/EMCAL/calibration/testWorkflow/EMCALChannelCalibratorSpec.h +++ b/Detectors/EMCAL/calibration/testWorkflow/EMCALChannelCalibratorSpec.h @@ -33,9 +33,13 @@ #include "CommonUtils/NameConf.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/CCDBParamSpec.h" +#include "DataFormatsCTP/Digits.h" +#include "DataFormatsCTP/Configuration.h" // for time measurements #include +#include +#include using namespace o2::framework; @@ -44,13 +48,38 @@ namespace o2 namespace calibration { +class CalibInputDownsampler +{ + public: + CalibInputDownsampler() { initSeed(); } + ~CalibInputDownsampler() = default; + + void setSamplingFraction(float samplingFraction) { mSamplingFraction = samplingFraction; } + bool acceptEvent() + { + auto rnr = mSampler(mRandomGenerator); + return rnr < mSamplingFraction; + } + + private: + void initSeed() + { + auto now = std::chrono::system_clock::now().time_since_epoch().count(); + std::seed_seq randomseed{uint32_t(now & 0xffffffff), uint32_t(now >> 32)}; + mRandomGenerator.seed(randomseed); + } + std::mt19937_64 mRandomGenerator; + std::uniform_real_distribution mSampler{0, 1}; + float mSamplingFraction = 1; +}; + class EMCALChannelCalibDevice : public o2::framework::Task { using EMCALCalibParams = o2::emcal::EMCALCalibParams; public: - EMCALChannelCalibDevice(std::shared_ptr req, bool params, std::string calibType, bool rejCalibTrg) : mCCDBRequest(req), mLoadCalibParamsFromCCDB(params), mCalibType(calibType), mRejectCalibTriggers(rejCalibTrg) {} + EMCALChannelCalibDevice(std::shared_ptr req, bool params, std::string calibType, bool rejCalibTrg, bool rejL0Trig, bool applyGainCalib) : mCCDBRequest(req), mLoadCalibParamsFromCCDB(params), mCalibType(calibType), mRejectCalibTriggers(rejCalibTrg), mRejectL0Triggers(rejL0Trig), mApplyGainCalib(applyGainCalib) {} void init(o2::framework::InitContext& ic) final { @@ -87,7 +116,6 @@ class EMCALChannelCalibDevice : public o2::framework::Task if (matcher == ConcreteDataMatcher("EMC", "EMCALCALIBPARAM", 0)) { LOG(info) << "EMCal CalibParams updated"; EMCALCalibParams::Instance().printKeyValues(true, true); - return; } if (matcher == ConcreteDataMatcher("EMC", "SCALEFACTORS", 0)) { if (mBadChannelCalibrator && EMCALCalibParams::Instance().useScaledHisto_bc) { @@ -96,6 +124,45 @@ class EMCALChannelCalibDevice : public o2::framework::Task mScaleFactorsInitialized = true; } } + if (mApplyGainCalib && matcher == ConcreteDataMatcher("EMC", "EMCGAINCALIB", 0)) { + if (mBadChannelCalibrator) { + LOG(info) << "Configuring gain calib factors for bad channel"; + if (mBadChannelCalibrator->setGainCalibrationFactors(reinterpret_cast(obj))) { + mGainCalibFactorsInitialized = true; + } + } + if (mTimeCalibrator) { + LOG(info) << "Configuring gain calib factors for time calib"; + if (mTimeCalibrator->setGainCalibrationFactors(reinterpret_cast(obj))) { + mGainCalibFactorsInitialized = true; + } + } + } + if (mRejectL0Triggers && matcher == ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) { + // clear current class mask and prepare to fill in the updated values + // The trigger names are seperated by a ":" in one string in the calib params + mSelectedClassMasks.clear(); + std::string strSelClassMasks = EMCALCalibParams::Instance().selectedClassMasks; + std::string delimiter = ":"; + size_t pos = 0; + std::vector vSelMasks; + while ((pos = strSelClassMasks.find(delimiter)) != std::string::npos) { + vSelMasks.push_back(strSelClassMasks.substr(0, pos)); + strSelClassMasks.erase(0, pos + delimiter.length()); + } + vSelMasks.push_back(strSelClassMasks); + + auto ctpconf = reinterpret_cast(obj); + + for (auto& cls : ctpconf->getCTPClasses()) { + LOG(debug) << "CTP class: " << cls.name << "\t " << cls.classMask; + + if (std::find(vSelMasks.begin(), vSelMasks.end(), cls.name) != vSelMasks.end()) { + mSelectedClassMasks.push_back(cls.classMask); + LOG(info) << "Setting selected class mask " << cls.name << " to bit " << cls.classMask; + } + } + } } //_________________________________________________________________ @@ -105,6 +172,14 @@ class EMCALChannelCalibDevice : public o2::framework::Task timeMeas[0] = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); } + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // new run is starting + mRunStopRequested = false; + } + if (mRunStopRequested) { + return; + } + o2::base::GRPGeomHelper::instance().checkUpdates(pc); if (mTimeCalibrator) { o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mTimeCalibrator->getCurrentTFInfo()); @@ -122,12 +197,45 @@ class EMCALChannelCalibDevice : public o2::framework::Task pc.inputs().get("EMC_Scalefactors"); } + // prepare CTPConfiguration such that it can be loaded in finalise ccdb + if (mRejectL0Triggers) { + pc.inputs().get(getCTPConfigBinding()); + } + if (!mIsConfigured) { // configure calibrators (after calib params are loaded from the CCDB) - configureCalibrators(); + // long tsMS = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS() + pc.services().get().firstTForbit * o2::constants::lhc::LHCOrbitMUS / 1000; // this reads the ts from the data. + long tsMS = o2::ccdb::getCurrentTimestamp(); + configureCalibrators(tsMS); mIsConfigured = true; } + if (mApplyGainCalib && !mGainCalibFactorsInitialized) { + // process dummy data with no cells to create a slot + std::vector cellDummyData(0); + if (isBadChannelCalib) { + mBadChannelCalibrator->process(cellDummyData); + } else { + mTimeCalibrator->process(cellDummyData); + } + // for reading the calib objects from the CCDB + pc.inputs().get(getGainCalibBinding()); + } + + float samplingFraction = isBadChannelCalib ? EMCALCalibParams::Instance().fractionEvents_bc : EMCALCalibParams::Instance().fractionEvents_tc; + if (samplingFraction < 1) { + if (!mDownsampler) { + mDownsampler = std::make_unique(); + } + mDownsampler->setSamplingFraction(samplingFraction); + } + + using ctpDigitsType = std::decay_t>(getCTPDigitsBinding()))>; + std::optional ctpDigits; + if (mRejectL0Triggers) { + ctpDigits = pc.inputs().get>(getCTPDigitsBinding()); + } + // reset EOR behaviour if (mTimeCalibrator) { if (mTimeCalibrator->getSaveAtEOR()) @@ -137,6 +245,23 @@ class EMCALChannelCalibDevice : public o2::framework::Task mBadChannelCalibrator->setSaveAtEOR(false); } + // prepare CTP information to reject EMCal triggers + uint64_t classMaskCTP = 0; + std::unordered_set numBCAccepted; + if (mRejectL0Triggers) { + for (auto& ctpDigit : *ctpDigits) { + // obtain trigger mask that belongs to the selected bc + classMaskCTP = ctpDigit.CTPClassMask.to_ulong(); + // now check if min bias trigger is not in mask + for (const uint64_t& selectedClassMask : mSelectedClassMasks) { + if ((classMaskCTP & selectedClassMask) != 0) { + LOG(debug) << "classmask " << selectedClassMask << " added for the bc " << ctpDigit.intRecord.toLong() + EMCALCalibParams::Instance().bcShiftCTP; + numBCAccepted.insert(ctpDigit.intRecord.toLong() + EMCALCalibParams::Instance().bcShiftCTP); + } + } + } + } + auto tfcounter = o2::header::get(pc.inputs().get(getCellBinding()).header)->startTime; auto data = pc.inputs().get>(getCellBinding()); @@ -160,6 +285,17 @@ class EMCALChannelCalibDevice : public o2::framework::Task } } + // reject all triggers that are not included in the classMask (typically only EMC min. bias should be accepted) + if (mRejectL0Triggers) { + if (numBCAccepted.find(trg.getBCData().toLong()) == numBCAccepted.end()) { + LOG(debug) << "correct trigger not found, rejecting event"; + continue; + } + } + + if (mDownsampler && !mDownsampler->acceptEvent()) { + continue; + } gsl::span eventData(data.data() + trg.getFirstEntry(), trg.getNumberOfObjects()); // fast calibration @@ -186,7 +322,7 @@ class EMCALChannelCalibDevice : public o2::framework::Task if (pc.transitionState() == TransitionHandlingState::Requested) { LOG(debug) << "Run stop requested, finalizing"; - // mRunStopRequested = true; + mRunStopRequested = true; if (isBadChannelCalib) { mBadChannelCalibrator->setSaveAtEOR(true); mBadChannelCalibrator->checkSlotsToFinalize(o2::calibration::INFINITE_TF); @@ -209,6 +345,9 @@ class EMCALChannelCalibDevice : public o2::framework::Task void endOfStream(o2::framework::EndOfStreamContext& ec) final { + if (mRunStopRequested) { + return; + } if (isBadChannelCalib) { mBadChannelCalibrator->setSaveAtEOR(true); mBadChannelCalibrator->checkSlotsToFinalize(o2::calibration::INFINITE_TF); @@ -218,10 +357,14 @@ class EMCALChannelCalibDevice : public o2::framework::Task mTimeCalibrator->checkSlotsToFinalize(o2::calibration::INFINITE_TF); sendOutput(ec.outputs()); } + mRunStopRequested = true; } static const char* getCellBinding() { return "EMCCells"; } static const char* getCellTriggerRecordBinding() { return "EMCCellsTrgR"; } + static const char* getCTPDigitsBinding() { return "CTPDigits"; } + static const char* getCTPConfigBinding() { return "CTPConfig"; } + static const char* getGainCalibBinding() { return "EMCGainCalib"; } private: std::unique_ptr> mBadChannelCalibrator; ///< Bad channel calibrator @@ -234,7 +377,13 @@ class EMCALChannelCalibDevice : public o2::framework::Task bool isBadChannelCalib = true; ///< Calibration mode bad channel calib (false := time calib) bool mLoadCalibParamsFromCCDB = true; ///< Switch for loading calib params from the CCDB bool mRejectCalibTriggers = true; ///! reject calibration triggers in the online calibration - std::array timeMeas; + bool mRejectL0Triggers = true; ///! reject EMCal Gamma and Jet triggers in the online calibration + bool mApplyGainCalib = true; ///! switch if gain calibration should be applied during filling of histograms or not + bool mGainCalibFactorsInitialized = false; ///! Gain calibration init status + bool mRunStopRequested = false; ///< flag that the run has stopped + std::array timeMeas; ///! Used for time measurement and holds the start and end time in the run function + std::vector mSelectedClassMasks = {}; ///! EMCal minimum bias trigger bit. Only this bit will be used for calibration + std::unique_ptr mDownsampler; //________________________________________________________________ template @@ -281,32 +430,52 @@ class EMCALChannelCalibDevice : public o2::framework::Task } /// \brief Configure calibrators from the calib params - void configureCalibrators() + void configureCalibrators(long ts) { + auto currFill = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getFillNumber(); + auto runtype = o2::base::GRPGeomHelper::instance().getGRPECS()->getRunType(); + LOG(debug) << "currFill " << currFill << " runtype " << runtype; + if (mTimeCalibrator) { LOG(info) << "Configuring time calibrator"; mTimeCalibrator->setSlotLength(EMCALCalibParams::Instance().slotLength_tc); + if (EMCALCalibParams::Instance().slotLength_tc == 0) { + // for infinite slot length do not check if there is enough statistics after every TF + mTimeCalibrator->setCheckIntervalInfiniteSlot(1e5); + } if (EMCALCalibParams::Instance().UpdateAtEndOfRunOnly_tc) { - mBadChannelCalibrator->setUpdateAtTheEndOfRunOnly(); + mTimeCalibrator->setUpdateAtTheEndOfRunOnly(); } + mTimeCalibrator->setSaveFileName("emc-time-calib-" + std::to_string(runtype) + ".root"); + mTimeCalibrator->setFillNr(currFill); + mTimeCalibrator->setRunType(runtype); + mTimeCalibrator->setCurrTSInHours(static_cast(ts / o2::ccdb::CcdbObjectInfo::HOUR)); } if (mBadChannelCalibrator) { LOG(info) << "Configuring bad channel calibrator"; mBadChannelCalibrator->setSlotLength(EMCALCalibParams::Instance().slotLength_bc); + if (EMCALCalibParams::Instance().slotLength_bc == 0) { + // for infinite slot length do not check if there is enough statistics after every TF + mBadChannelCalibrator->setCheckIntervalInfiniteSlot(1e5); + } if (EMCALCalibParams::Instance().UpdateAtEndOfRunOnly_bc) { mBadChannelCalibrator->setUpdateAtTheEndOfRunOnly(); } mBadChannelCalibrator->setIsTest(EMCALCalibParams::Instance().enableTestMode_bc); + mBadChannelCalibrator->setFillNr(currFill); + mBadChannelCalibrator->setRunType(runtype); + mBadChannelCalibrator->setSaveFileName("emc-channel-calib-" + std::to_string(runtype) + ".root"); + mBadChannelCalibrator->setCurrTSInHours(static_cast(ts / o2::ccdb::CcdbObjectInfo::HOUR)); } } -}; +}; // namespace calibration } // namespace calibration namespace framework { -DataProcessorSpec getEMCALChannelCalibDeviceSpec(const std::string calibType, const bool loadCalibParamsFromCCDB, const bool rejectCalibTrigger) +DataProcessorSpec getEMCALChannelCalibDeviceSpec(const std::string calibType, const bool loadCalibParamsFromCCDB, const bool rejectCalibTrigger, const bool rejectL0Trigger, const bool ctpcfgperrun, const bool applyGainCalib) { using device = o2::calibration::EMCALChannelCalibDevice; using clbUtils = o2::calibration::Utils; @@ -328,6 +497,7 @@ DataProcessorSpec getEMCALChannelCalibDeviceSpec(const std::string calibType, co std::vector inputs; inputs.emplace_back(device::getCellBinding(), o2::header::gDataOriginEMC, "CELLS", 0, o2::framework::Lifetime::Timeframe); inputs.emplace_back(device::getCellTriggerRecordBinding(), o2::header::gDataOriginEMC, "CELLSTRGR", 0, o2::framework::Lifetime::Timeframe); + // inputs.emplace_back("EMCTriggers", "EMC", "CELLSTRGR", 0, Lifetime::Timeframe) // for loading the channelCalibParams from the ccdb if (loadCalibParamsFromCCDB) { inputs.emplace_back("EMC_CalibParam", o2::header::gDataOriginEMC, "EMCALCALIBPARAM", 0, Lifetime::Condition, ccdbParamSpec("EMC/Config/CalibParam")); @@ -335,22 +505,33 @@ DataProcessorSpec getEMCALChannelCalibDeviceSpec(const std::string calibType, co if (calibType.find("badchannel") != std::string::npos) { inputs.emplace_back("EMC_Scalefactors", o2::header::gDataOriginEMC, "SCALEFACTORS", 0, Lifetime::Condition, ccdbParamSpec(CalibDB::getCDBPathChannelScaleFactors())); } + if (applyGainCalib) { + inputs.emplace_back(device::getGainCalibBinding(), o2::header::gDataOriginEMC, "EMCGAINCALIB", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/GainCalibFactors")); + } + + // data request needed for rejection of EMCal trigger + if (rejectL0Trigger) { + inputs.emplace_back(device::getCTPConfigBinding(), "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", ctpcfgperrun)); + inputs.emplace_back(device::getCTPDigitsBinding(), "CTP", "DIGITS", 0, Lifetime::Timeframe); + } + auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true - false, // GRPLHCIF + true, // GRPLHCIF false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs); + return DataProcessorSpec{ processorName, inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest, loadCalibParamsFromCCDB, calibType, rejectCalibTrigger)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, loadCalibParamsFromCCDB, calibType, rejectCalibTrigger, rejectL0Trigger, applyGainCalib)}, Options{}}; } } // namespace framework } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/EMCAL/calibration/testWorkflow/emc-channel-calib-workflow.cxx b/Detectors/EMCAL/calibration/testWorkflow/emc-channel-calib-workflow.cxx index 59a9676784b22..2eb67276905aa 100644 --- a/Detectors/EMCAL/calibration/testWorkflow/emc-channel-calib-workflow.cxx +++ b/Detectors/EMCAL/calibration/testWorkflow/emc-channel-calib-workflow.cxx @@ -39,7 +39,10 @@ void customize(std::vector& workflowOptions) {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"calibType", VariantType::String, "time", {"choose which calibration should be performed: time for tiem calibration, badchannel for bad channel calibration"}}, {"no-loadCalibParamsFromCCDB", VariantType::Bool, false, {"disabled by default such that calib params are taken from the ccdb. If enabled, calib params are taken from EMCALCalibParams.h directly"}}, - {"no-rejectCalibTrigger", VariantType::Bool, false, {"disabled by default such that calib triggers are rejected. If enabled, calibration triggers (LED events etc.) also enter the calibration"}}}; + {"no-rejectCalibTrigger", VariantType::Bool, false, {"disabled by default such that calib triggers are rejected. If enabled, calibration triggers (LED events etc.) also enter the calibration"}}, + {"no-rejectL0Trigger", VariantType::Bool, false, {"disabled by default such that L0 triggers are rejected. If enabled, L0 triggers (Gamma trigger and jet trigger) also enter the calibration"}}, + {"no-applyGainCalib", VariantType::Bool, false, {"if appplication of gain calibration should be disabled"}}, + {"ctpconfig-run-independent", VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}}; std::swap(workflowOptions, options); } @@ -53,11 +56,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) std::string calibType = cfgc.options().get("calibType"); bool loadCalibParamsFromCCDB = !cfgc.options().get("no-loadCalibParamsFromCCDB"); bool rejectCalibTrigger = !cfgc.options().get("no-rejectCalibTrigger"); + bool rejectL0Trigger = !cfgc.options().get("no-rejectL0Trigger"); + bool ctpcfgperrun = !cfgc.options().get("ctpconfig-run-independent"); + bool applyGainCalib = !cfgc.options().get("no-applyGainCalib"); WorkflowSpec specs; - specs.emplace_back(getEMCALChannelCalibDeviceSpec(calibType, loadCalibParamsFromCCDB, rejectCalibTrigger)); + specs.emplace_back(getEMCALChannelCalibDeviceSpec(calibType, loadCalibParamsFromCCDB, rejectCalibTrigger, rejectL0Trigger, ctpcfgperrun, applyGainCalib)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit // o2::raw::HBFUtilsInitializer hbfIni(cfgc, specs); return specs; -} \ No newline at end of file +} diff --git a/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-calib-workflow.cxx b/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-calib-workflow.cxx new file mode 100644 index 0000000000000..c624dbaa89784 --- /dev/null +++ b/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-calib-workflow.cxx @@ -0,0 +1,53 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file emc-channel-calib-workflow.cxx +/// @author Joshua Koenig +/// @since 2024-03-25 +/// @brief Basic workflow for EMCAL pedestal calibration + +#include "CommonUtils/ConfigurableParam.h" +#include "EMCALCalibration/PedestalCalibDevice.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" +#include "Framework/WorkflowSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +#include + +using namespace o2::framework; +using namespace o2::emcal; + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"dumpToFile", VariantType::Bool, false, {"if output (that goes to DCS ccdb) should be stored in txt file for local debugging"}}, + {"addRunNumber", VariantType::Bool, false, {"if true, run number will be added to ccdb file as first element of the string"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + bool dumpToFile = cfgc.options().get("dumpToFile"); + bool addRunNumber = cfgc.options().get("addRunNumber"); + + WorkflowSpec specs; + specs.emplace_back(o2::emcal::getPedestalCalibDevice(dumpToFile, addRunNumber)); + + return specs; +} diff --git a/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-processor-workflow.cxx b/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-processor-workflow.cxx new file mode 100644 index 0000000000000..efa4dc3f4afda --- /dev/null +++ b/Detectors/EMCAL/calibration/testWorkflow/emc-pedestal-processor-workflow.cxx @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file emc-channel-calib-workflow.cxx +/// @author Hannah Bossi +/// @since 2020-12-01 +/// @brief Basic workflow for EMCAL bad channel calibration (adapted from tof-calib-workflow.cxx) + +#include "CommonUtils/ConfigurableParam.h" +#include "EMCALCalibration/PedestalProcessorDevice.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" +#include "Framework/WorkflowSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +#include + +using namespace o2::framework; +using namespace o2::emcal; + +// add workflow options, note that customization needs to be declared before +// including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"no-askdiststf", VariantType::Bool, false, {"Don't subscribe to FLP/DISTSUBTIMEFRAME (local testing)"}}}; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" // the main driver + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + bool askdiststf = !cfgc.options().get("no-askdiststf"); + + WorkflowSpec specs; + specs.emplace_back(getPedestalProcessorDevice(askdiststf)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + // o2::raw::HBFUtilsInitializer hbfIni(cfgc, specs); + return specs; +} diff --git a/Detectors/EMCAL/doxymodules.h b/Detectors/EMCAL/doxymodules.h index 9c2df257fc25c..90c822264f6cd 100644 --- a/Detectors/EMCAL/doxymodules.h +++ b/Detectors/EMCAL/doxymodules.h @@ -35,8 +35,13 @@ * EMCAL calibration objects for * - Bad channel map * - Time calibration + * - Time slewing parameters * - Gain calibration * - Temperature calibration + * - FEE DCS parameters + * - Pedestal data + * In addition providing an interface convenient CCDB access and methods + * to recalibrate add cell level. */ /** @@ -44,8 +49,10 @@ * @brief EMCAL bad channel calibration * @ingroup DetectorEMCAL * - * Performs the EMCal bad channel calibration. - * + * EMCAL calibrator performing + * - Bad channel calibration + * - Time calibration + * and corresponding workflows for calibration tasks. */ /** diff --git a/Detectors/EMCAL/reconstruction/CMakeLists.txt b/Detectors/EMCAL/reconstruction/CMakeLists.txt index 8fda46bd94326..32aa4ccca67a5 100644 --- a/Detectors/EMCAL/reconstruction/CMakeLists.txt +++ b/Detectors/EMCAL/reconstruction/CMakeLists.txt @@ -16,8 +16,10 @@ o2_add_library(EMCALReconstruction src/AltroDecoder.cxx src/Bunch.cxx src/Channel.cxx + src/FastORTimeSeries.cxx src/RecoParam.cxx src/RawDecodingError.cxx + src/STUDecoderError.cxx src/ReconstructionErrors.cxx src/RecoContainer.cxx src/CaloFitResults.cxx @@ -30,6 +32,8 @@ o2_add_library(EMCALReconstruction src/DigitReader.cxx src/CTFCoder.cxx src/CTFHelper.cxx + src/StuDecoder.cxx + src/TRUDataHandler.cxx PUBLIC_LINK_LIBRARIES O2::Headers AliceO2::InfoLogger O2::DataFormatsEMCAL @@ -47,6 +51,7 @@ o2_target_root_dictionary( include/EMCALReconstruction/RawPayload.h include/EMCALReconstruction/Bunch.h include/EMCALReconstruction/Channel.h + include/EMCALReconstruction/FastORTimeSeries.h include/EMCALReconstruction/CaloFitResults.h include/EMCALReconstruction/CaloRawFitter.h include/EMCALReconstruction/CaloRawFitterStandard.h @@ -56,6 +61,9 @@ o2_target_root_dictionary( include/EMCALReconstruction/ClusterizerTask.h include/EMCALReconstruction/DigitReader.h include/EMCALReconstruction/RecoParam.h + include/EMCALReconstruction/StuDecoder.h + include/EMCALReconstruction/TRUDataHandler.h + include/EMCALReconstruction/TRUDecodingErrors.h ) o2_add_executable(rawreader-file @@ -93,6 +101,18 @@ o2_add_test(RecoContainer COMPONENT_NAME emcal LABELS emcal) +o2_add_test(TRUDataHandler + SOURCES test/testTRUDataHandler.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALReconstruction + COMPONENT_NAME emcal + LABELS emcal) + +o2_add_test(FastORTimeSeries + SOURCES test/testFastORTimeSeries.cxx + PUBLIC_LINK_LIBRARIES O2::EMCALReconstruction + COMPONENT_NAME emcal + LABELS emcal) + o2_add_test_root_macro(macros/RawFitterTESTs.C PUBLIC_LINK_LIBRARIES O2::EMCALReconstruction O2::Headers LABELS emcal COMPILE_ONLY) diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h index e46c466356545..be661a105395d 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h @@ -11,6 +11,7 @@ #ifndef ALICEO2_EMCAL_ALTRODECODER_H #define ALICEO2_EMCAL_ALTRODECODER_H +#include #include #include #include @@ -155,9 +156,13 @@ class MinorAltroDecodingError /// \brief Error codes connected with the ALTRO decoding enum class ErrorType_t { BUNCH_HEADER_NULL, ///< Bunch header is 0 + CHANNEL_HEADER, ///< Channel header corruption CHANNEL_END_PAYLOAD_UNEXPECT, ///< Unexpected end of payload (channel or trailer word in bunch words) CHANNEL_PAYLOAD_EXCEED, ///< Exceeding channel payload block - BUNCH_LENGTH_EXCEED ///< Bunch length exceeding channel payload size + CHANNEL_ORDER, ///< Channels not in increasing order + BUNCH_LENGTH_EXCEED, ///< Bunch length exceeding channel payload size + BUNCH_LENGTH_ALLOW_EXCEED, ///< Exceeds maximum allowed bunch length + BUNCH_STARTTIME ///< Bunch start time exceeding }; /// \brief Dummy constructor @@ -206,7 +211,7 @@ class MinorAltroDecodingError /// \brief Get the number of error types handled by the AltroDecoderError /// \return Number of error types - static constexpr int getNumberOfErrorTypes() noexcept { return 4; } + static constexpr int getNumberOfErrorTypes() noexcept { return 8; } /// \brief Get the name connected to the error type /// @@ -302,6 +307,13 @@ class AltroDecoder /// \brief Destructor ~AltroDecoder() = default; + /// \brief Set the max. allowed bunch length + /// \param maxBunchLength Max. allowed bunch lengths + /// + /// Rejects bunches in case the decoded bunch length or the + /// decoded start time exceeds the maximum allowed bunch length. + void setMaxBunchLength(int maxBunchLength) { mMaxBunchLength = maxBunchLength; } + /// \brief Decode the ALTRO stream /// \throw AltroDecoderError if the RCUTrailer or ALTRO payload cannot be decoded /// @@ -341,11 +353,17 @@ class AltroDecoder /// In case of failure an exception is thrown. void checkRCUTrailer(); + /// \brief Check hardware address in channel header for consistency + /// \param hwaddress Hardware address to check + /// \return True if the channel is consistent (branch, FEC and Altro in expected range) - false otherwise + bool checkChannelHWAddress(int hwaddress); + RawReaderMemory& mRawReader; ///< underlying raw reader RCUTrailer mRCUTrailer; ///< RCU trailer std::vector mChannels; ///< vector of channels in the raw stream std::vector mMinorDecodingErrors; ///< Container for minor (non-crashing) errors bool mChannelsInitialized = false; ///< check whether the channels are initialized + unsigned int mMaxBunchLength = UINT_MAX; ///< Max bunch length ClassDefNV(AltroDecoder, 1); }; diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index c5a596b8612e6..706b9bf8138a6 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -23,7 +23,6 @@ #include "DataFormatsEMCAL/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "EMCALReconstruction/CTFHelper.h" class TTree; @@ -33,10 +32,10 @@ namespace o2 namespace emcal { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -54,6 +53,7 @@ class CTFCoder : public o2::ctf::CTFCoderBase o2::ctf::CTFIOSize encode_impl(VEC& buff, const gsl::span& trigData, const gsl::span& cellData); void appendToTree(TTree& tree, CTF& ec); void readFromTree(TTree& tree, int entry, std::vector& trigVec, std::vector& cellVec); + void assignDictVersion(o2::ctf::CTFDictHeader& h) const final; std::vector mTrgDataFilt; std::vector mCellDataFilt; }; @@ -85,15 +85,15 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader()); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEEMC(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEEMC(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEEMC(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODEEMC(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -137,12 +136,13 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& c const auto& header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc, entries, energy, cellTime, tower, trigger; - std::vector orbitInc; + std::vector orbitInc; + std::vector bcInc; + std::vector entries, energy, cellTime, tower, trigger; std::vector status; o2::ctf::CTFIOSize iosize; -#define DECODEEMCAL(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEEMCAL(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEEMCAL(bcInc, CTF::BLC_bcIncTrig); iosize += DECODEEMCAL(orbitInc, CTF::BLC_orbitIncTrig); @@ -166,10 +166,17 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& c trigVec.reserve(header.nTriggers); status.reserve(header.nCells); + Cell::EncoderVersion encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV0; + if (header.majorVersion == 1 && header.minorVersion == 1) { + encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV1; + } else if (header.majorVersion == 1 && header.minorVersion == 2) { + encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV2; + } + uint32_t firstEntry = 0, cellCount = 0; o2::InteractionRecord ir(header.firstBC, header.firstOrbit); bool checkIROK = (mBCShift == 0); // need to check if CTP offset correction does not make the local time negative ? - Cell cell; + // Cell cell; TriggerRecord trg; for (uint32_t itrig = 0; itrig < header.nTriggers; itrig++) { // restore TrigRecord @@ -188,8 +195,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& c 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); + cellVec.emplace_back(tower[cellCount], energy[cellCount], cellTime[cellCount], status[cellCount], encodingversion); cellCount++; } trg.setBCData(ir - mBCShift); diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h index e044c3d2802f4..b75dac976b47d 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFHelper.h @@ -50,7 +50,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -59,91 +59,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I& operator--() + inline I operator++(int) { - mIndex--; - return (I&)(*this); - } - const I operator++(int) - { - auto res = *this; + I res = *(static_cast(this)); ++mIndex; return res; } - const I operator--(int) + inline I& operator--() noexcept + { + mIndex--; + return static_cast(*this); + } + + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } - - difference_type operator-(size_t idx) const { return mIndex - idx; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].getBCData().orbit == mData[mIndex - 1].getBCData().orbit) { - return mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc - mData[mIndex - 1].getBCData().bc); } else { - return mData[mIndex].getBCData().bc; + return value_type(mData[mIndex].getBCData().bc); } } return 0; @@ -153,9 +148,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].getBCData().orbit == mData[id - 1].getBCData().orbit) { - return mData[id].getBCData().bc - mData[id - 1].getBCData().bc; + return value_type(mData[id].getBCData().bc - mData[id - 1].getBCData().bc); } else { - return mData[id].getBCData().bc; + return value_type(mData[id].getBCData().bc); } } return 0; @@ -164,15 +159,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].getBCData().orbit - mData[mIndex - 1].getBCData().orbit : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0; + return value_type(id ? mData[id].getBCData().orbit - mData[id - 1].getBCData().orbit : 0); } }; @@ -201,8 +196,8 @@ class CTFHelper { public: using _Iter::_Iter; - value_type operator*() const { return mData[mIndex].getPackedTowerID(); } - value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedTowerID(); } + value_type operator*() const { return mData[mIndex].getTowerIDEncoded(); } + value_type operator[](difference_type i) const { return mData[mIndex + i].getTowerIDEncoded(); } }; //_______________________________________________ @@ -210,8 +205,8 @@ class CTFHelper { public: using _Iter::_Iter; - value_type operator*() const { return mData[mIndex].getPackedTime(); } - value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedTime(); } + value_type operator*() const { return mData[mIndex].getTimeStampEncoded(); } + value_type operator[](difference_type i) const { return mData[mIndex + i].getTimeStampEncoded(); } }; //_______________________________________________ @@ -219,8 +214,8 @@ class CTFHelper { public: using _Iter::_Iter; - value_type operator*() const { return mData[mIndex].getPackedEnergy(); } - value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedEnergy(); } + value_type operator*() const { return mData[mIndex].getEnergyEncoded(); } + value_type operator[](difference_type i) const { return mData[mIndex + i].getEnergyEncoded(); } }; //_______________________________________________ @@ -228,8 +223,8 @@ class CTFHelper { public: using _Iter::_Iter; - value_type operator*() const { return mData[mIndex].getPackedCellStatus(); } - value_type operator[](difference_type i) const { return mData[mIndex + i].getPackedCellStatus(); } + value_type operator*() const { return mData[mIndex].getCellTypeEncoded(); } + value_type operator[](difference_type i) const { return mData[mIndex + i].getCellTypeEncoded(); } }; //<<< =========================== ITERATORS ======================================== diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/FastORTimeSeries.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/FastORTimeSeries.h new file mode 100644 index 0000000000000..0918fced0f999 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/FastORTimeSeries.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_EMCAL_FASTORTIMESERIES_H +#define ALICEO2_EMCAL_FASTORTIMESERIES_H + +#include +#include +#include "Rtypes.h" + +namespace o2::emcal +{ + +/// \class FastORTimeSeries +/// \brief Container for FastOR time series +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALreconstruction +/// \since April 19, 2024 +/// +/// Time series are encoded in bunches in the raw data, which are usually time-reversed. +/// The FastORTimeSeries handles the time series of all bunches in the readout window, +/// in proper future-direction time order, correcting the time-reversal from the Fake-ALTRO. +/// Consequently the ADC samples are expected in time-reversed format. The function +/// calculateL1TimeSum calculates the timesum of the timeseries as 4-integral with respect to +/// a given L0 time, which is expected at the end of the time integration range. +class FastORTimeSeries +{ + public: + /// @brief Dummy constructor + FastORTimeSeries() = default; + + /// \brief Construcor + /// \param maxsamples Maximum number of time samples + /// \param timesamples Time-reversed raw ADC samples + /// \param starttime Start time + FastORTimeSeries(int maxsamples, const gsl::span timesamples, uint8_t starttime) + { + setSize(maxsamples); + fillReversed(timesamples, starttime); + } + + /// \brief Destructor + ~FastORTimeSeries() = default; + + void setTimeSamples(const gsl::span timesamples, uint8_t starttime) { fillReversed(timesamples, starttime); } + + /// \brief Calculate L0 timesum (4-integral of the ADC series) with respect to a given L0 time + /// \param l0time L0 time (end of the time series) + /// \return Timesum of the time series + uint16_t calculateL1TimeSum(uint8_t l0time) const; + + /// \brief Access raw ADC values (in forward time order) + /// \return ADC values of the time series in forward time order + const gsl::span getADCs() const { return mTimeSamples; } + + /// \brief Clear ADC samples in the time series + void clear(); + + private: + /// \brief Set the container size for the ADC samples + /// \param maxsamples Max. amount of samples to be handled + void setSize(int maxsamples); + + /// \brief Fill the internal time samples in proper time order + /// \param timesamples Time-reversed time samples + /// \param starttime Start time + void fillReversed(const gsl::span timesamples, uint8_t starttime); + + std::vector mTimeSamples; ///< Raw ADC time samples (in forward time order) + + ClassDef(FastORTimeSeries, 1); +}; + +} // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h index 58f57d53a4441..7d68b7bb7a03f 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h @@ -44,7 +44,8 @@ class RawDecodingError : public std::exception HEADER_INVALID, ///< Header in memory not belonging to requested superpage PAGE_START_INVALID, ///< Page position starting outside payload size PAYLOAD_INVALID, ///< Payload in memory not belonging to requested superpage - TRAILER_DECODING ///< Inconsistent trailer in memory (several trailer words missing the trailer marker) + TRAILER_DECODING, ///< Inconsistent trailer in memory (several trailer words missing the trailer marker) + TRAILER_INCOMPLETE ///< Incomplete trailer words (i.e. registers) }; /// \brief Constructor @@ -93,6 +94,8 @@ class RawDecodingError : public std::exception return 5; case ErrorType_t::TRAILER_DECODING: return 6; + case ErrorType_t::TRAILER_INCOMPLETE: + return 7; }; // can never reach this, due to enum class // just to make Werror happy @@ -101,7 +104,7 @@ class RawDecodingError : public std::exception /// \brief Get the number of error codes /// \return Number of error codes - static constexpr int getNumberOfErrorTypes() { return 7; } + static constexpr int getNumberOfErrorTypes() { return 8; } static ErrorType_t intToErrorType(unsigned int errortype) { @@ -109,7 +112,7 @@ class RawDecodingError : public std::exception static constexpr std::array errortypes = {{ErrorType_t::PAGE_NOTFOUND, ErrorType_t::HEADER_DECODING, ErrorType_t::PAYLOAD_DECODING, ErrorType_t::HEADER_INVALID, ErrorType_t::PAGE_START_INVALID, ErrorType_t::PAYLOAD_INVALID, - ErrorType_t::TRAILER_DECODING}}; + ErrorType_t::TRAILER_DECODING, ErrorType_t::TRAILER_INCOMPLETE}}; return errortypes[errortype]; } @@ -137,6 +140,8 @@ class RawDecodingError : public std::exception return "PayloadCorruption"; case ErrorType_t::TRAILER_DECODING: return "TrailerDecoding"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "TrailerIncomplete"; }; return "Undefined error"; } @@ -177,6 +182,8 @@ class RawDecodingError : public std::exception return "Payload corruption"; case ErrorType_t::TRAILER_DECODING: return "Trailer decoding"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "Trailer incomplete"; }; return "Undefined error"; } @@ -217,6 +224,8 @@ class RawDecodingError : public std::exception return "Access to payload not belonging to requested superpage"; case ErrorType_t::TRAILER_DECODING: return "Inconsistent trailer in memory"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "Incomplete trailer"; }; return "Undefined error"; } diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h index 843bda5f8398b..f726d1307fb90 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h @@ -11,11 +11,13 @@ #ifndef ALICEO2_EMCAL_RAWREADERMEMORY_H #define ALICEO2_EMCAL_RAWREADERMEMORY_H +#include #include #include #include "EMCALBase/RCUTrailer.h" #include "EMCALReconstruction/RawBuffer.h" +#include "EMCALReconstruction/RawDecodingError.h" #include "EMCALReconstruction/RawPayload.h" #include "Headers/RAWDataHeader.h" #include "Headers/RDHAny.h" @@ -36,6 +38,46 @@ namespace emcal class RawReaderMemory { public: + /// \class MinorError + /// \brief Minor (non-crashing) raw decoding errors + /// + /// Minor errors share the same codes as major raw decoding errors, + /// however are not crashing. + class MinorError + { + public: + /// \brief Dummy constructor + MinorError() = default; + + /// \brief Main constructor + /// \param errortype Type of the error + /// \param feeID ID of the FEE equipment + MinorError(RawDecodingError::ErrorType_t errortype, int feeID) : mErrorType(errortype), mFEEID(feeID) {} + + /// \brief Destructor + ~MinorError() = default; + + /// \brief Set the type of the error + /// \param errortype Type of the error + void setErrorType(RawDecodingError::ErrorType_t errortype) { mErrorType = errortype; } + + /// \brief Set the ID of the FEE equipment + /// \param feeID ID of the FEE + void setFEEID(int feeID) { mFEEID = feeID; } + + /// \brief Get type of the error + /// \return Type of the error + RawDecodingError::ErrorType_t getErrorType() const { return mErrorType; } + + /// \brief Get ID of the FEE + /// \return ID of the FEE + int getFEEID() const { return mFEEID; } + + private: + RawDecodingError::ErrorType_t mErrorType; ///< Type of the error + int mFEEID; ///< ID of the FEC responsible for the ERROR + }; + /// \brief Constructor RawReaderMemory(const gsl::span rawmemory); @@ -58,8 +100,7 @@ class RawReaderMemory /// \brief Read next payload from the stream /// /// Read the next pages until the stop bit is found. - void - next(); + void next(); /// \brief Read the next page from the stream (single DMA page) /// \param resetPayload If true the raw payload is reset @@ -85,6 +126,10 @@ class RawReaderMemory /// \return Raw Payload of the data until the stop bit is received. const RawPayload& getPayload() const { return mRawPayload; } + /// \brief Get minor (non-crashing) raw decoding errors + /// \return Minor raw decoding errors + gsl::span getMinorErrors() const { return mMinorErrors; } + /// \brief Return size of the payload /// \return size of the payload int getPayloadSize() const { return mRawPayload.getPayloadSize(); } @@ -103,11 +148,10 @@ class RawReaderMemory /// Rewind stream to the first entry void init(); - /// \brief Check whether the current page is accepted - /// \param page Raw page to check - /// \return True if the page is accepted, false otherwise - bool acceptPage(const char* page) const; - + /// \brief Decode raw header words + /// \param headerwords Headerwords + /// \return Decoded RDH + /// \throw RawDecodingError with code HEADER_DECODING if the payload does not correspond to an expected header o2::header::RDHAny decodeRawHeader(const void* headerwords); private: @@ -123,6 +167,7 @@ class RawReaderMemory int mCurrentFEE = -1; ///< Current FEE in the data stream bool mRawHeaderInitialized = false; ///< RDH for current page initialized bool mPayloadInitialized = false; ///< Payload for current page initialized + std::vector mMinorErrors; ///< Minor raw decoding errors ClassDefNV(RawReaderMemory, 1); }; diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoContainer.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoContainer.h index c1a9fc151a1d5..dc3821810c4a5 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoContainer.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoContainer.h @@ -14,8 +14,10 @@ /// \author Markus Fasel , Oak Ridge National Laboratory /// \since May 30, 2023 +#include #include #include +#include #include #include #include @@ -24,6 +26,12 @@ #include #include #include +#include +#include +#include + +#ifndef ALICEO2_EMCAL_RECOCONTAINER_H +#define ALICEO2_EMCAL_RECOCONTAINER_H namespace o2::emcal { @@ -49,10 +57,52 @@ struct RecCellInfo { /// \class EventContainer /// \brief Containter of cells for a given event -/// \ingroup EMCALReconstruction +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALreconstruction +/// \since May 30, 2023 +/// +/// The EventContainer is part of the reco container and keeps the cell / LEDMON / TRU +/// data of a given event (trigger) defined by the BC and orbit in the raw data header. +/// In case the gain type merging is activated the event container performs on-the-fly +/// merging of high- and low-gain data for cells and LEDMONS keeping only high- or +/// low-gain and preferring the high-gain due to better resolution if not saturated. +/// +/// Error handling: +/// The EventContainer can raise a TRUIndexException in case TRU information with an +/// unexpected index (>= 52) is added / requested. class EventContainer { public: + /// \class TRUIndexException + /// \brief Handler for access of TRU data with invalid TRU index + /// \ingroup EMCALreconstruction + class TRUIndexException final : public std::exception + { + public: + /// \brief Constructor + /// \param index TRU index raising the exception + TRUIndexException(std::size_t index); + + /// \brief Destructor + ~TRUIndexException() noexcept final = default; + + /// \brief Get the error message of the exception + /// \return Error message + const char* what() const noexcept final { return mMessage.data(); } + + /// \brief Get the TRU index raising the exception + /// \return TRU index + std::size_t getIndex() const { return mIndex; } + + /// \brief Print error message on stream + /// \param stream Stream to print on + void printStream(std::ostream& stream) const; + + private: + std::size_t mIndex; ///< TRU index raising the exception + std::string mMessage; ///< Buffer for error message + }; + /// \brief Constructor EventContainer() = default; @@ -95,6 +145,22 @@ class EventContainer /// \return Number of LEDMONs int getNumberOfLEDMONs() const { return mLEDMons.size(); } + /// \brief Read and write access TRU data of a given TRU + /// \param truIndex Index of the TRU + /// \return TRU data handler for the TRU + /// \throw TRUIndexException in case the TRU index is invalid (>= 52) + TRUDataHandler& getTRUData(std::size_t truIndex); + + /// \brief Read-only access TRU data of a given TRU + /// \param truIndex Index of the TRU + /// \return TRU data handler for the TRU + /// \throw TRUIndexException in case the TRU index is invalid (>= 52) + const TRUDataHandler& readTRUData(std::size_t truIndex) const; + + /// \brief Access to container with FastOR time series + /// \return Container with time series + const std::unordered_map& getTimeSeriesContainer() const { return mL0FastORs; } + /// \brief Add cell information to the event container /// \param tower Tower ID /// \param energy Cell energy @@ -129,6 +195,16 @@ class EventContainer setCellCommon(tower, energy, time, celltype, true, hwaddress, ddlID, doMergeHGLG); } + /// \brief Add bunch of time series to the container + /// \param fastORAbsID Absolute ID of the FastOR + /// \param starttime Start time of the bunch + /// \param timesamples Time samples of the bunch in time-reversed format + /// + /// In case a TimeSeries is already present for the given FastOR abs. ID in the container + /// the bunch is added to this, otherwise a new TimeSeries is added with the ADCs of the + /// bunch. + void setFastOR(uint16_t fastORAbsID, uint8_t starttime, const gsl::span timesamples); + /// \brief Sort Cells / LEDMONs in container according to tower / module ID /// \param isLEDmon Switch between Cell and LEDMON void sortCells(bool isLEDmon); @@ -148,21 +224,37 @@ class EventContainer /// \return True if the energy is in the saturation region, false otherwise bool isCellSaturated(double energy) const; - o2::InteractionRecord mInteractionRecord; - uint64_t mTriggerBits = 0; ///< Trigger bits of the event - std::vector mCells; ///< Container of cells in event - std::vector mLEDMons; ///< Container of LEDMONs in event + /// \brief Initialize the TRU handlers + void initTRUs(); + + o2::InteractionRecord mInteractionRecord; ///< Interaction record of the event + uint64_t mTriggerBits = 0; ///< Trigger bits of the event + std::vector mCells; ///< Container of cells in event + std::vector mLEDMons; ///< Container of LEDMONs in event + std::array mTRUData; ///< TRU status + std::unordered_map mL0FastORs; ///< L0 FastOR time series }; /// \class RecoContainer -/// \brief Handler for cells in -/// \ingroup EMCALReconstruction +/// \brief Handler for cells/LEDMONS/Trigger data in timeframes +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALreconstruction +/// \since May 30, 2023 +/// +/// The RecoContainer handles the cell/LEDMON/trigger data of all events of a given +/// timeframe during the reconstruction. Event data are handled internally via the +/// EventContainer, where the RecoContainer provides access to. +/// +/// Error handling: +/// The RecoContainer can raise an InteractionNotFoundException in case read access +/// is requested for an interaction based on the o2::InteractionRecord which is not +/// for which no data was inserted into the container. class RecoContainer { public: /// \class InteractionNotFoundException /// \brief Handling of access to trigger interaction record not present in container - class InteractionNotFoundException : public std::exception + class InteractionNotFoundException final : public std::exception { public: /// \brief Constructor @@ -225,7 +317,17 @@ class RecoContainer /// \class RecoContainerReader /// \brief Iterator over reco containers -/// \ingroup EMCALReconstruction +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALreconstruction +/// \since May 30, 2023 +/// +/// The RecoContainerReader iterates over the events stored in the RecoContainer in +/// a time-ordered sequence. The function hasNext checks whether there are more +/// events to iterate over, while nextEvent provides access to the next event. +/// +/// Error handling: +/// The RecoContainerReader can raise an InvalidAccessException in case access is tried to +/// invalid data. class RecoContainerReader { public: @@ -273,3 +375,5 @@ class RecoContainerReader }; } // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h index 98217825ab1ce..c675f57b3506a 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h @@ -27,22 +27,46 @@ namespace emcal class RecoParam : public o2::conf::ConfigurableParamHelper { public: + /// \brief Destructor ~RecoParam() override = default; + /// \brief Get the average cell time shift + /// \return Average cell time shift double getCellTimeShiftNanoSec() const { return mCellTimeShiftNanoSec; } + + /// \brief Get noise threshold for LGnoHG error + /// \return Noise threshold double getNoiseThresholdLGnoHG() const { return mNoiseThresholdLGnoHG; } + + /// \brief Get the BC phase + /// \return BC phase int getPhaseBCmod4() const { return mPhaseBCmod4; } + /// \brief Get the max. allowed bunch length + /// \return Max. allowed bunch length + /// + /// In case of max. bunch length 0 the max. bunch length is auto-determined from + /// the RCU trailer + int getMaxAllowedBunchLength() const { return mMaxBunchLength; } + + /// \brief Print current reconstruction parameters to stream + /// \param stream Stream to print on void PrintStream(std::ostream& stream) const; private: double mNoiseThresholdLGnoHG = 10.; ///< Noise threshold applied to suppress LGnoHG error double mCellTimeShiftNanoSec = 470.; ///< Time shift applied on the cell time to center trigger peak around 0 int mPhaseBCmod4 = 1; ///< Rollback of the BC ID in the correction of the cell time for the BC mod 4 + unsigned int mMaxBunchLength = 15; ///< Max. allowed bunch length O2ParamDef(RecoParam, "EMCRecoParam"); }; -std::ostream& operator<<(std::ostream& stream, const RecoParam& s); + +/// \brief Streaming operator for the reconstruction parameters +/// \param stream Stream to print on +/// \param par RecoParams to be printed +/// \return Stream after printing the reco params +std::ostream& operator<<(std::ostream& stream, const RecoParam& par); } // namespace emcal namespace framework diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/ReconstructionErrors.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/ReconstructionErrors.h index 49dd4d34b6937..c46ef63b6f3ac 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/ReconstructionErrors.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/ReconstructionErrors.h @@ -163,18 +163,18 @@ GainError_t getGainErrorFromErrorCode(unsigned int errorcode); /// Name is a short single word descriptor used i.e. in /// object names. /// -/// \param errortype Error type of the gain error -/// \return Name connected to gain error type +/// \param errortype Error type of the gain type error +/// \return Name connected to gain type error const char* getGainErrorName(GainError_t errortype); /// \brief Get name of a given gain error code /// /// Name is a short single word descriptor used i.e. in /// object names. Attention: Error code must be a valid -/// geomentry error code. +/// gain type error code. /// -/// \param errorcode Error code of the gain error -/// \return Name connected to gain error type +/// \param errorcode Error code of the gain type error +/// \return Name connected to gain type error const char* getGainErrorName(unsigned int errorcode); /// \brief Get title of a given gain error type @@ -182,37 +182,140 @@ const char* getGainErrorName(unsigned int errorcode); /// Title is a short descriptor used i.e. in /// histogram titles. /// -/// \param errortype Error type of the gain error -/// \return Title connected to gain error type +/// \param errortype Error type of the gain type error +/// \return Title connected to gain type error const char* getGainErrorTitle(GainError_t errortype); /// \brief Get title of a given gain error type /// /// Title is a short descriptor used i.e. in /// histogram titles. Attention: Error code must -/// be a valid geomentry error code. +/// be a valid gain type error code. /// -/// \param errorcode Error code of the gain error -/// \return Title connected to gain error type +/// \param errorcode Error code of the gain type error +/// \return Title connected to gain type error const char* getGainErrorTitle(unsigned int errorcode); /// \brief Get detailed description of a given gain error type /// /// Provides a long description to be used i.e. in error messages. /// -/// \param errortype Error type of the gain error -/// \return Detaied description connected to gain error type +/// \param errortype Error type of the gain type error +/// \return Detaied description connected to gain type error const char* getGainErrorDescription(GainError_t errortype); /// \brief Get detailed description of a given gain error type /// /// Provides a long description to be used i.e. in error messages. -/// Attention: Error code must be a valid gain error code. +/// Attention: Error code must be a valid gain type error code. /// -/// \param errortype Error type of the geometry error -/// \return Detaied description connected to gain error type +/// \param errortype Error type of the gain type error +/// \return Detaied description connected to gain type error const char* getGainErrorDescription(unsigned int errorcode); +/// \enum TRUDecodingError_t +/// \brief Errors related to TRU data decoding +/// \ingroup EMCALReconstruction +/// +/// TRU decoding errors appear during the decoding of the TRU data. +/// They can be raised by the trigger mapping, in case an invalid +/// FastOR index or TRU index is called, or by the TRU data handler if +/// the patch index is outside range. +enum class TRUDecodingError_t { + TRU_INDEX_INVALID, ///< TRU index invalid + PATCH_INDEX_INVALID, ///< Patch index outside range + FASTOR_INDEX_INVALID, ///< FastOR index unknown + FASTOR_STARTTIME_INVALID, ///< FastOr stattime is larger than 14 + UNKNOWN_ERROR ///< Unknown error type +}; + +/// \brief Get the number of TRU error codes +/// \return Number of TRU error codes +constexpr int getNumberOfTRUErrorCodes() { return 3; } + +/// \brief Convert error code to TRU decoding error type +/// +/// Attention: Error code must be a valid error code, handled +/// internally via assert. +/// +/// \param errorcode Error code to be converted +/// \return Error type connected to error code +TRUDecodingError_t getTRUDecodingErrorFromErrorCode(unsigned int errorcode); + +/// \brief Convert TRU decoding error type into numberic representation +/// \param errortype TRU decoding error type +/// \return Error code connected to error type +constexpr int getErrorCodeFromTRUDecodingError(TRUDecodingError_t error) +{ + switch (error) { + case TRUDecodingError_t::TRU_INDEX_INVALID: + return 0; + case TRUDecodingError_t::PATCH_INDEX_INVALID: + return 1; + case TRUDecodingError_t::FASTOR_INDEX_INVALID: + return 2; + case TRUDecodingError_t::FASTOR_STARTTIME_INVALID: + return 3; + case TRUDecodingError_t::UNKNOWN_ERROR: + return -1; + } +} + +/// \brief Get name of a given TRU decoding error type +/// +/// Name is a short single word descriptor used i.e. in +/// object names. +/// +/// \param errortype Error type of the TRU decoding error +/// \return Name connected to TRU decoding error type +const char* getTRUDecodingErrorName(TRUDecodingError_t errortype); + +/// \brief Get name of a given TRU decoding error code +/// +/// Name is a short single word descriptor used i.e. in +/// object names. Attention: Error code must be a valid +/// TRU decoding error code. +/// +/// \param errorcode Error code of the TRU decoding error +/// \return Name connected to TRU decoding error type +const char* getTRUDecodingErrorName(unsigned int errorcode); + +/// \brief Get title of a given TRU decoding error type +/// +/// Title is a short descriptor used i.e. in +/// histogram titles. +/// +/// \param errortype Error type of the TRU decoding error +/// \return Title connected to TRU decoding error +const char* getTRUDecodingErrorTitle(TRUDecodingError_t errortype); + +/// \brief Get title of a given TRU decoding error type +/// +/// Title is a short descriptor used i.e. in +/// histogram titles. Attention: Error code must +/// be a valid TRU decoding error code. +/// +/// \param errorcode Error code of the TRU decoding error +/// \return Title connected to TRU decoding error +const char* getTRUDecodingErrorTitle(unsigned int errorcode); + +/// \brief Get detailed description of a given TRU decoding error type +/// +/// Provides a long description to be used i.e. in error messages. +/// +/// \param errortype Error type of the TRU decoding error +/// \return Detaied description connected to TRU decoding error +const char* getTRUDecodingErrorErrorDescription(TRUDecodingError_t errortype); + +/// \brief Get detailed description of a given TRU decoding error type +/// +/// Provides a long description to be used i.e. in error messages. +/// Attention: Error code must be a valid TRU decoding error code. +/// +/// \param errortype Error type of the TRU decoding error +/// \return Detaied description connected to TRU decoding error +const char* getTRUDecodingErrorErrorDescription(unsigned int errorcode); + } // namespace reconstructionerrors } // namespace emcal diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/STUDecoderError.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/STUDecoderError.h new file mode 100644 index 0000000000000..1bebb3db4bf43 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/STUDecoderError.h @@ -0,0 +1,117 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_EMCAL_STUDECODERERROR_H +#define ALICEO2_EMCAL_STUDECODERERROR_H + +#include +#include +#include + +namespace o2::emcal +{ + +/// \class STUDecoderError +/// \brief Handling of STU reconstruction errors +/// \ingroup EMCALreconstruction +/// \since April 25, 2023 +/// +/// In order to distinguish different error types the STUDecoder error +/// carries an error code which can be uniquely identified with a +/// condition raising the excpetion. Source is always the DDL of the +/// STU raising the exception. +class STUDecoderError final : public std::exception +{ + public: + /// \enum ErrorCode_t + /// \brief Error codes of STU decoding + enum class ErrorCode_t { + PAGE_ERROR, ///< Page decoding failed (missing header) + WORD_UNEXPECTED, ///< Word unexpected + INDEX_UNEXPECTED, ///< Patch index unexpected + ADC_OVERFLOW, ///< ADC overflow + FEEID_UNEXPECTED, ///< FeeID index unexpected + OLD_PAYLOAD_VERSION, ///< unsupported old payload version + FULL_PAYLOAD_SIZE_UNEXPECTED, ///< full payload size unexpected + SHORT_PAYLOAD_SIZE_UNEXPECTED, ///< short payload size unexpected + UNKNOWN ///< Unknown error code (needed for conversion to int) + }; + + /// \brief Get integer representation of error code + /// \param errorcode Error code + /// \return Integer representation + static int errorCodeToInt(ErrorCode_t errorcode); + + /// \brief Convert integer to error code + /// \param errorcode Error code as integer + /// \return Error code (symbolic) - UNKNOWN for invalid integer error codes + static ErrorCode_t intToErrorCode(int errorcode); + + /// \brief Get the name of the error code + /// \param errorcode Error code + /// \return Name of the error code + static std::string getErrorCodeName(ErrorCode_t errorcode); + + /// \brief Get the name of the error code + /// \param errorcode Error code (integer representation) + /// \return Name of the error code + static std::string getErrorCodeName(int errorcode) { return getErrorCodeName(intToErrorCode(errorcode)); } + + /// \brief Get the title of the error code + /// \param errorcode Error code + /// \return Title of the error code + static std::string getErrorCodeTitle(ErrorCode_t errorcode); + + /// \brief Get the title of the error code + /// \param errorcode Error code (integer representation) + /// \return Title of the error code + static std::string getErrorCodeTitle(int errorcode) { return getErrorCodeTitle(intToErrorCode(errorcode)); } + + /// \brief Constructor + /// \param ddlID ID of the DDL for which the exception is raised + /// \param errcode Error code of the exception + STUDecoderError(int ddlID, ErrorCode_t errcode); + + /// \brief Destructor + ~STUDecoderError() noexcept final = default; + + /// \brief Access to error message + /// \return Error message + const char* what() const noexcept final { return mMessage.data(); } + + /// \brief Get the ID of the DDL in which the exception is raised + /// \return ID of the DDL + int getDDLID() const noexcept { return mDDLId; } + + /// \brief Get error code of the exception + /// \return Error code + ErrorCode_t getErrorCode() const noexcept { return mErrorCode; } + + /// \brief Print details of the error on the stream + /// \param stream Stream to print on + /// + /// Helper function for streaming operator + void printStream(std::ostream& stream) const; + + private: + int mDDLId; ///< ID of the DDL raising the exception + ErrorCode_t mErrorCode; ///< Error code of the exception + std::string mMessage; ///< Message buffer +}; + +/// \brief Streaming operator of STU decoding errors +/// \param stream Stream to print the error on +/// \param error Error to be streamed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const STUDecoderError& error); + +} // namespace o2::emcal + +#endif \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/StuDecoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/StuDecoder.h new file mode 100755 index 0000000000000..2e26ff434b531 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/StuDecoder.h @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_EMCAL_STUDECODER_H +#define ALICEO2_EMCAL_STUDECODER_H + +#include +#include +#include +#include "EMCALReconstruction/RawReaderMemory.h" +#include "DataFormatsEMCAL/Constants.h" + +namespace o2 +{ +namespace emcal +{ +/// \class StuDecoder +/// \brief Decoder of the EMCAL/DCAL STU data +/// \ingroup EMCALreconstruction +/// \author Martin Poghosyan , Oak Ridge National Laboratory +/// \since Apr. 27, 2022 +/// + +class StuDecoder +{ + public: + /// \brief Constructor + /// \param reader Raw reader instance to be decoded + StuDecoder(RawReaderMemory& reader); + + /// \brief Destructor + ~StuDecoder() = default; + + /// \brief Decode the STU stream + /// \throw StuDecoderError if the STU payload cannot be decoded + void decode(); + + int32_t getL1GammaHighThreshold() const { return mL1GammaHighThreshold; } + int32_t getL1JetHighThreshold() const { return mL1JetHighThreshold; } + int32_t getL1GammaLowThreshold() const { return mL1GammaLowThreshold; } + int32_t getL1JetLowThreshold() const { return mL1JetLowThreshold; } + int32_t getRho() const { return (mCFGword13 & 0x3FFFF); } + int32_t getFrameReceivedSTU() const { return ((mCFGword13 >> 18) & 0x3); } + int32_t getRegionEnable() const { return mRegionEnable; } + int32_t getFrameReceived() const { return mFrameReceived; } + int32_t getParchSize() const { return ((mCFGword16 >> 16) & 0xFFFF); } + int32_t getFWversion() const { return (mCFGword16 & 0xFFFF); } + + STUtype_t getSTUtype() const { return mSTU; } + int getFeeID() const { return STUparam::FeeID[mSTU]; } + int getNumberOfTRUs() const { return STUparam::NTRU[mSTU]; } + + std::vector getL1JetHighPatchIndices() const { return mL1JetHighPatchIndex; } + std::vector getL1JetLowPatchIndices() const { return mL1JetLowPatchIndex; } + std::vector getL1GammaHighPatchIndices() const { return mL1GammaHighPatchIndex; } + std::vector getL1GammaLowPatchIndics() const { return mL1GammaLowPatchIndex; } + std::vector getFastOrADCs() const { return mFastOrADC; } + + int getNumberOfL1JetHighPatches() const { return mL1JetHighPatchIndex.size(); } + int getNumberOfL1JetLowPatches() const { return mL1JetLowPatchIndex.size(); } + int getNumberOfL1GammaHighPatches() const { return mL1GammaHighPatchIndex.size(); } + int getNumberOfL1GammaLowPatches() const { return mL1GammaLowPatchIndex.size(); } + + int16_t getIndexOfL1JetHighPatch(int id) const { return mL1JetHighPatchIndex[id]; } + int16_t getIndexOfL1JetLowPatch(int id) const { return mL1JetLowPatchIndex[id]; } + int16_t getIndexOfL1GammaHighPatch(int id) const { return mL1GammaHighPatchIndex[id]; } + int16_t getIndexOfL1GammaLowPatch(int id) const { return mL1GammaLowPatchIndex[id]; } + + int16_t getFastOrADC(int iTRU, int iCh) const { return mFastOrADC[iTRU + getNumberOfTRUs() * iCh]; } + + std::tuple getL1GammaMaxPatch() const // std::tuple + { + return std::make_tuple(((mCFGWord0 >> 9) & 0x1F), ((mCFGWord0 >> 4) & 0x1F), (mCFGWord0 & 0xF)); + } + + bool isFullPayload() const { return mIsFullPayload; } + bool isL1GammaLowFired() const { return ((mCFGWord0 >> 16) & 0x1); } + bool isL1GammaHighFired() const { return ((mCFGWord0 >> 17) & 0x1); } + bool isL1JetLowFired() const { return ((mCFGWord0 >> 18) & 0x1); } + bool isL1JetHighFired() const { return ((mCFGWord0 >> 19) & 0x1); } + bool isMedianMode() const { return ((mCFGWord0 >> 20) & 0x1); } + + void dumpSTUcfg() const; + + int mDebug = -3; + + private: + RawReaderMemory& mRawReader; ///< underlying raw reader + + std::vector mL1JetHighPatchIndex; + std::vector mL1JetLowPatchIndex; + std::vector mL1GammaHighPatchIndex; + std::vector mL1GammaLowPatchIndex; + std::vector mFastOrADC; + + // data from payload + int32_t mCFGWord0 = 0; ///< + int32_t mCFGWord1 = 0; ///< + int32_t mL0mask = 0; ///< + int32_t mL1GammaHighThreshold = 0; ///< + int32_t mShortPayloadRate = 0; ///< + int32_t mL0bits = 0; ///< + int32_t mL1JetHighThreshold = 0; ///< + int32_t mL1GammaLowThreshold = 0; ///< + int32_t mL1JetLowThreshold = 0; ///< + int32_t mCFGword13 = 0; ///< + int32_t mRegionEnable = 0; ///< + int32_t mFrameReceived = 0; ///< + int32_t mCFGword16 = 0; ///< + + STUtype_t mSTU = STUtype_t::ESTU; + bool mIsFullPayload = true; ///< + + void init(); + void decodeL1JetPatchIndices(const uint32_t* buffer); + void decodeL1GammaPatchIndices(const uint32_t* buffer); + void decodeFastOrADC(const uint32_t* buffer); + + int getCFGWords() { return STUparam::CFG_nWords[mSTU]; } + int getL1JetIndexWords() { return STUparam::L1JetIndex_nWords[mSTU]; } + int getL0indexWords() { return STUparam::L0index_nWords[mSTU]; } + int getL1GammaIndexWords() { return STUparam::L1GammaIndex_nWords[mSTU]; } + int getRawWords() { return STUparam::Raw_nWords[mSTU]; } + int getSubregionsEta() { return STUparam::SubregionsEta[mSTU]; } + int getSubregionsPhi() { return STUparam::SubregionsPhi[mSTU]; } + int getPaloadSizeFull() { return STUparam::PaloadSizeFull[mSTU]; } + int getPaloadSizeShort() { return STUparam::PaloadSizeShort[mSTU]; } + + ClassDefNV(StuDecoder, 1); +}; + +} // namespace emcal +} // namespace o2 + +#endif diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h new file mode 100644 index 0000000000000..811faf13a05ff --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDataHandler.h @@ -0,0 +1,185 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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_TRUDataHandler_H +#define ALICEO2_EMCAL_TRUDataHandler_H + +#include +#include +#include +#include +#include +#include + +#include "Rtypes.h" + +#include "EMCALBase/TriggerMappingV2.h" + +namespace o2::emcal +{ + +/// \class TRUDataHandler +/// \brief Helper class to handle decoded TRU data during the reconstruction +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALreconstruction +/// \since April 19, 2024 +/// +/// The decoded TRU data contains the following information +/// - Index of the TRU +/// - Trigger time of the TRU +/// - Fired or not +/// - Index of fired patches with time the patch has fired +/// The information is decoded in columns 96 to 105 of the FakeALTRO data. The +/// class does not handle FastOR timesums (colums 0-96), they are handled by a +/// separate class FastORTimeSeries. +class TRUDataHandler +{ + public: + /// \class PatchIndexException + /// \brief Handler of errors related to invalid trigger patch IDs + /// \ingroup EMCALreconstruction + class PatchIndexException final : public std::exception + { + public: + /// \brief Constructor + /// \param index Patch index raising the exception + PatchIndexException(int8_t index); + + /// \brief Destructor + ~PatchIndexException() noexcept final = default; + + /// \brief Get patch index raising the exception + /// \return Patch index + int8_t getIndex() const { return mIndex; } + + /// \brief Access Error message + /// \return Error message + const char* what() const noexcept final + { + return mMessage.data(); + } + + /// \brief Print error on output stream + /// \param stream Stream to be printed to + void printStream(std::ostream& stream) const; + + private: + int8_t mIndex = -1; ///< Patch index rainsing the exception + std::string mMessage; ///< Buffer for error message + }; + + /// \brief Constructor + TRUDataHandler(); + + /// \brief Destructor + ~TRUDataHandler() = default; + + /// \brief Reset handler + void reset(); + + /// \brief Set reconstructed trigger patch + /// \param index Index of the trigger patch in the TRU + /// \param time Decoded time of the patch + /// \throw PatchIndexException in case the patch index is invalid (>= 77) + void setPatch(unsigned int index, unsigned int time) + { + checkPatchIndex(index); + mPatchTimes[index] = time; + } + + /// \brief Mark TRU as fired (containing at least one patch above threshold) + /// \param fired + void setFired(bool fired) { mL0Fired = fired; } + + /// \brief Set the L0 time of the TRU + /// \param l0time L0 time of the TRU + void setL0time(int l0time) { mL0Time = l0time; } + + /// \brief Set the index of the TRU (in global STU indexing scheme) + /// \param index Index of the TRU + void setTRUIndex(int index) { mTRUIndex = index; } + + /// \brief Check whether the TRU was fired (at least one patch above threshold) + /// \return True if the TRU was fired, false otherwise + bool isFired() const { return mL0Fired; } + + int8_t getL0time() const { return mL0Time; } + + /// \brief Check whehther the patch at the given index has fired + /// \param index Index of the patch + /// \return True if the patch has fired, false otherwise + /// \throw PatchIndexException in case the patch index is invalid (>= 77) + bool hasPatch(unsigned int index) const + { + checkPatchIndex(index); + return mPatchTimes[index] < UCHAR_MAX; + } + + /// \brief Get the trigger time of the trigger patch at a given index + /// \param index Index of the trigger patch + /// \return Reconstructed patch time (UCHAR_MAX in case the patch has not fired) + /// \throw PatchIndexException in case the patch index is invalid (>= 77) + uint8_t getPatchTime(unsigned int index) const + { + checkPatchIndex(index); + return mPatchTimes[index]; + } + + /// \brief Check whether the TRU has any patch fired + /// \return True if at least one fired patch was found, false otherwise + bool hasAnyPatch() const + { + for (int ipatch = 0; ipatch < mPatchTimes.size(); ipatch++) { + if (hasPatch(ipatch)) { + return true; + } + } + return false; + } + + /// \brief Get the index of the TRU in global (STU) index schemes + /// \return Index of the TRU + int getTRUIndex() const { return mTRUIndex; } + + /// \brief Print TRU information to an output stream + /// \param stream Stream to print on + void printStream(std::ostream& stream) const; + + private: + /// \brief Check whether the patch index is valid + /// \throw PatchIndexException in case the patch index is invalid (>= 77) + void checkPatchIndex(unsigned int index) const + { + if (index >= mPatchTimes.size()) { + throw PatchIndexException(index); + } + } + + std::array mPatchTimes; ///< Patch times: In case the patch time is smaller than UCHAR_MAX then the patch has fired + bool mL0Fired = false; ///< TRU has fired + int8_t mL0Time = -1; ///< L0 time of the TRU + int8_t mTRUIndex = -1; ///< Index of the TRU + ClassDefNV(TRUDataHandler, 1); +}; + +/// \brief Output stream operator for the TRU data handler +/// \param stream Stream to print on +/// \param data TRU data to be streamed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const TRUDataHandler& data); + +/// \brief Output stream operator of the PatchIndexException +/// \param stream Stream to print on +/// \param error Error to be streamed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const TRUDataHandler::PatchIndexException& error); + +} // namespace o2::emcal +#endif diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDecodingErrors.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDecodingErrors.h new file mode 100644 index 0000000000000..084465f84944f --- /dev/null +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/TRUDecodingErrors.h @@ -0,0 +1,60 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_EMCAL_TRUDECODINGERRORS_H +#define ALICEO2_EMCAL_TRUDECODINGERRORS_H + +#include +#include + +namespace o2 +{ + +namespace emcal +{ + +/// \class FastOrStartTimeInvalidException +/// \brief Handling of error if starttime is to large (>=14). This is most likely caused by a corrupted channel header where a FEC channel is identified as a TRU channel +/// \ingroup EMCALbase +class FastOrStartTimeInvalidException : public std::exception +{ + public: + /// \brief Constructor + /// \param l0size Size of the L0 patch + FastOrStartTimeInvalidException(int time) : std::exception(), mErrorMessage(), mStartTime(time) + { + mErrorMessage = "FastOr starttime invalid: " + std::to_string(time); + } + + /// \brief Destructor + ~FastOrStartTimeInvalidException() noexcept final = default; + + /// \brief Access to error message + /// \return Error message + const char* what() const noexcept final + { + return mErrorMessage.data(); + } + + /// \brief Get the size of the L0 patch + /// \return Size of the L0 patch + int getStartTime() const noexcept { return mStartTime; } + + private: + std::string mErrorMessage; ///< Buffer for error message + int mStartTime; ///< Size of the L0 patch +}; + +} // namespace emcal + +} // namespace o2 + +#endif // ALICEO2_EMCAL_TRUDECODINGERRORS_H \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx b/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx deleted file mode 100644 index 6fc119dc69521..0000000000000 --- a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file rawReaderFileNew.cxx -/// \author Markus Fasel , Oak Ridge National Laboratory - -#include -#include - -#include - -#include "DetectorsRaw/RawFileReader.h" -#include "DetectorsRaw/RDHUtils.h" -#include "EMCALBase/Mapper.h" -#include "EMCALBase/TriggerMappingV2.h" -#include "EMCALReconstruction/AltroDecoder.h" -#include "EMCALReconstruction/RawReaderMemory.h" -#include - -namespace bpo = boost::program_options; -// 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]) + - " \n" - " Tool will decode the DDLx data for EMCAL 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()->default_value(0), "Select verbosity level [0 = no output]"); - add_option("version", "Print version information"); - add_option("input-file,i", bpo::value()->required(), "Specifies input file."); - add_option("debug,d", bpo::value()->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(); - - 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(); - - o2::emcal::MappingHandler mapper; - o2::emcal::TriggerMappingV2 triggermapping; - - std::unique_ptr treefile(TFile::Open("trudata.root", "RECREATE")); - TTree trudata("trudata", "Tree with TRU data"); - // branches in tree - struct collisiontrigger { - unsigned long bc; - unsigned long orbit; - } mycollision; - int absFastOR; - int starttime; - std::vector timesamples; - tree->Branch(&mycollision, "collisiontrigger", "bc,orbit/l"); - tree->Branch(&starttime, "starttime", "starttime/i"); - tree->Branch(×amples, "timesamples", ""); // @todo check how to write std::vector to tree; - - while (1) { - int tfID = reader.getNextTFToRead(); - if (tfID >= reader.getNTimeFrames()) { - LOG(info) << "nothing left to read after " << tfID << " TFs read"; - break; - } - std::vector 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(); - auto rdh = parser.getRawHeader(); - auto ddl = o2::raw::RDHUtils::getFEEID(parser.getRawHeader()); - // Exclude STU DDLs - if (ddl >= 40) { - continue; - } - - mycollision.bc = o2::raw::RDHUtils::getTriggerBC(rdh); - mycollision.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - - o2::emcal::AltroDecoder decoder(parser); - decoder.decode(); - auto& ddlmapping = mapper.getMappingForDDL(ddl); - - std::cout << decoder.getRCUTrailer() << std::endl; - for (auto& chan : decoder.getChannels()) { - if (ddlmapping.getChannelType(chan.getHardwareAddress) != o2::emcal::ChannelType_t::TRU) { - continue; - } - std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; - // Get absolute FastOR index - this will tell us where on the EMCAL surface the FastOR is - // TRU index is encoded in column, needs to be converted to an absoluted FastOR ID via the - // trigger mapping. The absoluted FastOR ID can be connected via the geometry to tower IDs - // from the FEC data. - // we are only interested in the FastORs for now, skip patches starting from 96 - auto fastorInTRU = ddlmapping.getColumn(chan.getHardwareAddress()); - if (fastorInTRU >= 96) { - // indices starting from 96 encode patches, not FastORs - continue; - } - auto truindex = triggermapping.getTRUIndexFromOnlineHardareAddree(chan.getHardwareAddress(), ddl, ddl / 2); - auto absFastOrID = triggermapping.getAbsFastORIndexFromIndexInTRU(truindex, fastorInTRU); - - for (auto& bunch : chan.getBunches()) { - std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; - auto adcs = bunch.getADC(); - int time = bunch.getStartTime(); - starttime = time; - timesamples.clear(); - timesamples.resize(adcs.size()); - std::copy(adcs.begin(), adcs.end(), timesamples.begin()); - trudata.Fill(); - for (int i = adcs.size() - 1; i >= 0; i--) { - std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; - time--; - } - } - } - } - } - reader.setNextTFToRead(++tfID); - } -} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx index 2df0890c2ef5b..d555b4523cc49 100644 --- a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx +++ b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx @@ -42,7 +42,6 @@ void AltroDecoder::readRCUTrailer() gsl::span payloadwords(payloadwordsOrig.data(), payloadwordsOrig.size()); mRCUTrailer.constructFromRawPayload(payloadwords); } catch (RCUTrailer::Error& e) { - LOG(error) << "Error while decoding RCU trailer: " << e.what(); throw AltroDecoderError(AltroDecoderError::ErrorType_t::RCU_TRAILER_ERROR, fmt::format("{} {}", AltroDecoderError::getErrorTypeDescription(AltroDecoderError::ErrorType_t::RCU_TRAILER_ERROR), e.what())); } } @@ -63,6 +62,7 @@ void AltroDecoder::readChannels() int currentpos = 0; auto& buffer = mRawReader.getPayload().getPayloadWords(); auto maxpayloadsize = buffer.size() - mRCUTrailer.getTrailerSize(); + int lastFEC = -1; while (currentpos < maxpayloadsize) { auto currentword = buffer[currentpos++]; if (currentword >> 30 != 1) { @@ -75,6 +75,23 @@ void AltroDecoder::readChannels() uint16_t payloadsize = (channelheader >> 16) & 0x3FF; bool badchannel = (channelheader >> 29) & 0x1; + // check hardware address for consistency + if (!checkChannelHWAddress(hwaddress)) { + // Inconsistent HW address information, channel header corrupted, payload must be skipped + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, channelheader, currentword); + continue; + } + + int currentfec = 10 * Channel::getBranchIndexFromHwAddress(hwaddress) + Channel::getFecIndexFromHwAddress(hwaddress); + // std::cout << "Branch: " << Channel::getBranchIndexFromHwAddress(hwaddress) << ", FEC " << Channel::getFecIndexFromHwAddress(hwaddress) << " -> " << currentfec << " (channel " << Channel::getChannelIndexFromHwAddress(hwaddress) << " )" << std::endl; + if (currentfec < lastFEC) { + // FECs are ordered by the SRU, breaking the order is a clear sign of data corruption + // std::cout << "Error Fec, current " << currentfec << ", last " << lastFEC << std::endl; + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, channelheader, currentword); + continue; + } + lastFEC = currentfec; + /// decode all words for channel bool foundChannelError = false; int numberofwords = (payloadsize + 2) / 3; @@ -122,6 +139,18 @@ void AltroDecoder::readChannels() // we must break here as well, the bunch is cut and the pointer would be set to invalid memory break; } + // Raise minor decoding error in case the bunch length exceeds the maximum possible amount of samples + if ((unsigned int)bunchlength > mMaxBunchLength) { + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_ALLOW_EXCEED, channelheader, 0); + // Same as above: if the bunch length exceeds the maximum possible bunch length it will for sure conflict with the next bunch + break; + } + // Raise minor decoding error in case the start timne the maximum possible amount of samples (resulting in negative first sample index) + if ((unsigned int)starttime > mMaxBunchLength) { + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::BUNCH_STARTTIME, channelheader, 0); + // Also here we must break, out-of-bounds start time will create troubles in the raw fit + break; + } if (bunchlength == 0) { // skip bunches with bunch size 0, they don't contain any payload // Map error type to NULL header since header word is null @@ -138,6 +167,25 @@ void AltroDecoder::readChannels() mChannelsInitialized = true; } +bool AltroDecoder::checkChannelHWAddress(int hwaddress) +{ + unsigned int branch = Channel::getBranchIndexFromHwAddress(hwaddress), + fec = Channel::getFecIndexFromHwAddress(hwaddress), + altro = Channel::getAltroIndexFromHwAddress(hwaddress); + if (branch > 1) { + return false; + } + if (fec > 9) { + return false; + } + if (fec > 0) { // fec == 0 corresponds to trigger, which only has a fake altro information. Hence it's not checked + if (!(altro == 0 || altro == 2 || altro == 3 || altro == 4)) { + return false; + } + } + return true; +} + const RCUTrailer& AltroDecoder::getRCUTrailer() const { if (!mRCUTrailer.isInitialized()) { @@ -186,6 +234,7 @@ int AltroDecoderError::errorTypeToInt(AltroErrType errortype) case AltroErrType::CHANNEL_ERROR: errorNumber = 7; break; + default: break; } @@ -328,12 +377,24 @@ int MinorAltroDecodingError::errorTypeToInt(MinorAltroErrType errortype) case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: errorNumber = 1; break; - case MinorAltroErrType::BUNCH_HEADER_NULL: + case MinorAltroErrType::CHANNEL_ORDER: errorNumber = 2; break; - case MinorAltroErrType::BUNCH_LENGTH_EXCEED: + case MinorAltroErrType::CHANNEL_HEADER: errorNumber = 3; break; + case MinorAltroErrType::BUNCH_HEADER_NULL: + errorNumber = 4; + break; + case MinorAltroErrType::BUNCH_LENGTH_EXCEED: + errorNumber = 5; + break; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + errorNumber = 6; + break; + case MinorAltroErrType::BUNCH_STARTTIME: + errorNumber = 7; + break; }; return errorNumber; @@ -352,11 +413,23 @@ MinorAltroErrType MinorAltroDecodingError::intToErrorType(int errornumber) errorType = MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED; break; case 2: - errorType = MinorAltroErrType::BUNCH_HEADER_NULL; + errorType = MinorAltroErrType::CHANNEL_ORDER; break; case 3: + errorType = MinorAltroErrType::CHANNEL_HEADER; + break; + case 4: + errorType = MinorAltroErrType::BUNCH_HEADER_NULL; + break; + case 5: errorType = MinorAltroErrType::BUNCH_LENGTH_EXCEED; break; + case 6: + errorType = MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED; + break; + case 7: + errorType = MinorAltroErrType::BUNCH_STARTTIME; + break; default: break; } @@ -371,10 +444,18 @@ const char* MinorAltroDecodingError::getErrorTypeName(ErrorType_t errortype) return "ChannelEndPayloadUnexpected"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "ChannelPayloadExceed"; + case MinorAltroErrType::CHANNEL_ORDER: + return "ChannelOrderError"; + case MinorAltroErrType::CHANNEL_HEADER: + return "ChannelHeader"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "BunchHeaderNull"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "BunchLengthExceed"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "BunchLengthAllowExceed"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "BunchStarttimeExceed"; }; return ""; } @@ -386,10 +467,18 @@ const char* MinorAltroDecodingError::getErrorTypeTitle(ErrorType_t errortype) return "Channel end unexpected"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "Channel exceed"; + case MinorAltroErrType::CHANNEL_ORDER: + return "FEC order"; + case MinorAltroErrType::CHANNEL_HEADER: + return "Channel header invalid"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "Bunch header null"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "Bunch length exceed"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "Bunch length impossible"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "Bunch starttime exceed"; }; return ""; } @@ -401,10 +490,18 @@ const char* MinorAltroDecodingError::getErrorTypeDescription(ErrorType_t errorty return "Unexpected end of payload in altro channel payload!"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "Trying to access out-of-bound payload!"; + case MinorAltroErrType::CHANNEL_ORDER: + return "Invalid FEC order"; + case MinorAltroErrType::CHANNEL_HEADER: + return "Invalid channel header"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "Bunch header 0 or not configured!"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "Bunch length exceeding channel payload size!"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "Bunch length exceeding max. possible bunch size!"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "Bunch start time outside range!"; }; return ""; } diff --git a/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx index 6c63a6f26fd9e..5f3b04f600192 100644 --- a/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx +++ b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx @@ -41,10 +41,11 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc = 0, entries = 0, cellTime = 0, energy = 0, tower = 0, trigger = 0; - uint32_t orbitInc = 0; + int16_t bcInc = 0; + int32_t orbitInc = 0; + uint16_t entries = 0, cellTime = 0, energy = 0, tower = 0, trigger = 0; uint8_t status = 0; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); @@ -57,3 +58,14 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa MAKECODER(trigger, CTF::BLC_trigger); // clang-format on } + +///___________________________________________________________________________________ +void CTFCoder::assignDictVersion(o2::ctf::CTFDictHeader& h) const +{ + if (mExtHeader.isValidDictTimeStamp()) { + h = mExtHeader; + } else { + h.majorVersion = 1; + h.minorVersion = 2; + } +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx b/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx index cc5624eacba54..96541aaff09f4 100644 --- a/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx +++ b/Detectors/EMCAL/reconstruction/src/Clusterizer.cxx @@ -68,12 +68,13 @@ void Clusterizer::getClusterFromNeighbours(std::vectorgetEnergy() > mInputMap[row][column].mInput->getEnergy() + mGradientCut)) { - if (not(TMath::Abs(mInputMap[row + rowDiffs[dir]][column + colDiffs[dir]].mInput->getTimeStamp() - mInputMap[row][column].mInput->getTimeStamp()) > mTimeCut)) { - getClusterFromNeighbours(clusterInputs, row + rowDiffs[dir], column + colDiffs[dir]); - // Add the cell/digit to the current cluster -- if we end up here, the selected cluster fulfills the condition - clusterInputs.emplace_back(mInputMap[row + rowDiffs[dir]][column + colDiffs[dir]]); - } + if (mDoEnergyGradientCut && (mInputMap[row + rowDiffs[dir]][column + colDiffs[dir]].mInput->getEnergy() > mInputMap[row][column].mInput->getEnergy() + mGradientCut)) { + continue; + } + if (not(TMath::Abs(mInputMap[row + rowDiffs[dir]][column + colDiffs[dir]].mInput->getTimeStamp() - mInputMap[row][column].mInput->getTimeStamp()) > mTimeCut)) { + getClusterFromNeighbours(clusterInputs, row + rowDiffs[dir], column + colDiffs[dir]); + // Add the cell/digit to the current cluster -- if we end up here, the selected cluster fulfills the condition + clusterInputs.emplace_back(mInputMap[row + rowDiffs[dir]][column + colDiffs[dir]]); } } } diff --git a/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h b/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h index 419cfea051aa1..ee39958db5add 100644 --- a/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h +++ b/Detectors/EMCAL/reconstruction/src/EMCALReconstructionLinkDef.h @@ -24,6 +24,9 @@ #pragma link C++ class o2::emcal::CaloRawFitter + ; #pragma link C++ class o2::emcal::CaloRawFitterStandard + ; #pragma link C++ class o2::emcal::CaloRawFitterGamma2 + ; +#pragma link C++ class o2::emcal::StuDecoder + ; +#pragma link C++ class o2::emcal::FastORTimeSeries + ; +#pragma link C++ class o2::emcal::TRUDataHandler + ; #pragma link C++ class o2::emcal::RecoParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::emcal::RecoParam> + ; diff --git a/Detectors/EMCAL/reconstruction/src/FastORTimeSeries.cxx b/Detectors/EMCAL/reconstruction/src/FastORTimeSeries.cxx new file mode 100644 index 0000000000000..67ada70d6008c --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/FastORTimeSeries.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "EMCALReconstruction/FastORTimeSeries.h" +#include "EMCALReconstruction/TRUDecodingErrors.h" + +using namespace o2::emcal; + +void FastORTimeSeries::fillReversed(const gsl::span samples, uint8_t starttime) +{ + if (starttime >= 14) { + throw FastOrStartTimeInvalidException(starttime); + } + if (starttime + 1 < samples.size()) { + throw FastOrStartTimeInvalidException(static_cast(starttime) - static_cast(samples.size()) + 1); + } + for (std::size_t isample = 0; isample < samples.size(); isample++) { + mTimeSamples[starttime - isample] = samples[isample]; + } +} + +uint16_t FastORTimeSeries::calculateL1TimeSum(uint8_t l0time) const +{ + uint16_t timesum = 0; + int firstbin = l0time - 4; // Include sample before the L0 time + for (int isample = firstbin; isample < firstbin + 4; isample++) { + timesum += mTimeSamples[isample]; + } + return timesum; +} + +void FastORTimeSeries::setSize(int maxsamples) +{ + mTimeSamples.resize(maxsamples); +} + +void FastORTimeSeries::clear() +{ + std::fill(mTimeSamples.begin(), mTimeSamples.end(), 0); +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx index a9f15b3adeeb4..8ccbc5d63104a 100644 --- a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx +++ b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx @@ -8,12 +8,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. - +#include #include #include #include "EMCALReconstruction/RawReaderMemory.h" -#include "EMCALReconstruction/RawDecodingError.h" #include "DetectorsRaw/RDHUtils.h" +#include "Headers/RAWDataHeader.h" +#include "Headers/DAQID.h" using namespace o2::emcal; @@ -36,6 +37,16 @@ o2::header::RDHAny RawReaderMemory::decodeRawHeader(const void* payloadwords) if (headerversion < RDHDecoder::getVersion() || headerversion > RDHDecoder::getVersion()) { throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); } + if (!RDHDecoder::checkRDH(payloadwords, false, true)) { + // Header size != to 64 and 0 in reserved words indicate that the header is a fake header + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + if (RDHDecoder::getVersion(payloadwords) >= 6) { + // If header version is >= 6 check that the source ID is EMCAL + if (RDHDecoder::getSourceID(payloadwords) != o2::header::DAQID::EMC) { + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + } return {*reinterpret_cast(payloadwords)}; } @@ -51,6 +62,7 @@ void RawReaderMemory::next() { mRawPayload.reset(); mCurrentTrailer.reset(); + mMinorErrors.clear(); bool isDataTerminated = false; do { nextPage(false); @@ -117,9 +129,31 @@ void RawReaderMemory::nextPage(bool doResetPayload) } if (mCurrentPosition + RDHDecoder::getMemorySize(mRawHeader) > mRawMemoryBuffer.size()) { // Payload incomplete + // Set to offset to next or buffer size, whatever is smaller + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + if (mCurrentPosition + 64 < mRawMemoryBuffer.size()) { + // check if the page continues with a RDH + // if next page is not a header decodeRawHeader will throw a RawDecodingError + auto nextheader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + if (RDHDecoder::getVersion(nextheader) != RDHDecoder::getVersion(mRawHeader)) { + // Next header has different header version - clear indication that it is a fake header + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + } + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } + // RDHDecoder::printRDH(mRawHeader); throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_DECODING, mCurrentFEE); } else if (mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader) > mRawMemoryBuffer.size()) { // Start position of the payload is outside the payload range + // Set to offset to next or buffer size, whatever is smaller + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_START_INVALID, mCurrentFEE); } else { mRawBuffer.readFromMemoryBuffer(gsl::span(mRawMemoryBuffer.data() + mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader), RDHDecoder::getMemorySize(mRawHeader) - RDHDecoder::getHeaderSize(mRawHeader))); @@ -139,7 +173,7 @@ void RawReaderMemory::nextPage(bool doResetPayload) // The trailer is only decoded if the DDL is in the range of SRU DDLs. STU pages are propagated // 1-1 without trailer parsing auto lastword = *(mRawBuffer.getDataWords().rbegin()); - if (lastword >> 30 == 3) { + if (RCUTrailer::checkLastTrailerWord(lastword)) { // lastword is a trailer word // decode trailer and chop try { @@ -150,7 +184,17 @@ void RawReaderMemory::nextPage(bool doResetPayload) mCurrentTrailer.setPayloadSize(mCurrentTrailer.getPayloadSize() + trailer.getPayloadSize()); } payloadWithoutTrailer = gsl::span(mRawBuffer.getDataWords().data(), mRawBuffer.getDataWords().size() - trailer.getTrailerSize()); + if (trailer.getTrailerWordCorruptions()) { + mMinorErrors.emplace_back(RawDecodingError::ErrorType_t::TRAILER_INCOMPLETE, mCurrentFEE); + } } catch (RCUTrailer::Error& e) { + // We must forward the position in such cases in order to not end up in an infinity loop + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } + // Page is not consistent - must be skipped throw RawDecodingError(RawDecodingError::ErrorType_t::TRAILER_DECODING, mCurrentFEE); } } else { diff --git a/Detectors/EMCAL/reconstruction/src/RecoContainer.cxx b/Detectors/EMCAL/reconstruction/src/RecoContainer.cxx index 4ed49e5876c93..062887287f86b 100644 --- a/Detectors/EMCAL/reconstruction/src/RecoContainer.cxx +++ b/Detectors/EMCAL/reconstruction/src/RecoContainer.cxx @@ -16,7 +16,15 @@ using namespace o2::emcal; -EventContainer::EventContainer(const o2::InteractionRecord& currentIR) : mInteractionRecord(currentIR) {} +EventContainer::EventContainer(const o2::InteractionRecord& currentIR) : mInteractionRecord(currentIR) { initTRUs(); } + +void EventContainer::initTRUs() +{ + for (auto index = 0; index < mTRUData.size(); index++) { + mTRUData[index].setTRUIndex(index); + mTRUData[index].setL0time(INT8_MAX); + } +} void EventContainer::setCellCommon(int tower, double energy, double time, ChannelType_t celltype, bool isLEDmon, int hwaddress, int ddlID, bool doMergeHGLG) { @@ -83,6 +91,16 @@ void EventContainer::setCellCommon(int tower, double energy, double time, Channe } } +void EventContainer::setFastOR(uint16_t fastORAbsID, uint8_t starttime, const gsl::span timesamples) +{ + auto found = mL0FastORs.find(fastORAbsID); + if (found != mL0FastORs.end()) { + found->second.setTimeSamples(timesamples, starttime); + } else { + mL0FastORs[fastORAbsID] = FastORTimeSeries(14, timesamples, starttime); + } +} + void EventContainer::sortCells(bool isLEDmon) { auto& dataContainer = isLEDmon ? mLEDMons : mCells; @@ -94,6 +112,27 @@ bool EventContainer::isCellSaturated(double energy) const return energy / o2::emcal::constants::EMCAL_ADCENERGY > o2::emcal::constants::OVERFLOWCUT; } +TRUDataHandler& EventContainer::getTRUData(std::size_t index) +{ + if (index >= mTRUData.size()) { + throw TRUIndexException(index); + } + return mTRUData[index]; +} + +const TRUDataHandler& EventContainer::readTRUData(std::size_t index) const +{ + if (index >= mTRUData.size()) { + throw TRUIndexException(index); + } + return mTRUData[index]; +} + +EventContainer::TRUIndexException::TRUIndexException(std::size_t index) : mIndex(index), mMessage() +{ + mMessage = "Invalid TRU index " + std::to_string(index); +} + EventContainer& RecoContainer::getEventContainer(const o2::InteractionRecord& currentIR) { auto found = mEvents.find(currentIR); diff --git a/Detectors/EMCAL/reconstruction/src/RecoParam.cxx b/Detectors/EMCAL/reconstruction/src/RecoParam.cxx index 00f95b613d1e5..f80721ba9b88e 100644 --- a/Detectors/EMCAL/reconstruction/src/RecoParam.cxx +++ b/Detectors/EMCAL/reconstruction/src/RecoParam.cxx @@ -16,9 +16,9 @@ O2ParamImpl(o2::emcal::RecoParam); using namespace o2::emcal; -std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RecoParam& s) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RecoParam& par) { - s.PrintStream(stream); + par.PrintStream(stream); return stream; } @@ -28,5 +28,6 @@ void RecoParam::PrintStream(std::ostream& stream) const << "\n=============================================" << "\nTime offset (ns): " << mCellTimeShiftNanoSec << "\nNoise threshold HGLG suppression: " << mNoiseThresholdLGnoHG - << "\nPhase in BC mod 4 correction: " << mPhaseBCmod4; + << "\nPhase in BC mod 4 correction: " << mPhaseBCmod4 + << "\nMax number of ALTRO samples: " << mMaxBunchLength; } \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/ReconstructionErrors.cxx b/Detectors/EMCAL/reconstruction/src/ReconstructionErrors.cxx index 30f2a403daac0..6f1a5f5bd4f2d 100644 --- a/Detectors/EMCAL/reconstruction/src/ReconstructionErrors.cxx +++ b/Detectors/EMCAL/reconstruction/src/ReconstructionErrors.cxx @@ -147,6 +147,85 @@ const char* getGainErrorDescription(unsigned int errorcode) return getGainErrorDescription(getGainErrorFromErrorCode(errorcode)); } +TRUDecodingError_t getTRUDecodingErrorFromErrorCode(unsigned int errorcode) +{ + switch (errorcode) { + case 0: + return TRUDecodingError_t::TRU_INDEX_INVALID; + case 1: + return TRUDecodingError_t::PATCH_INDEX_INVALID; + case 2: + return TRUDecodingError_t::FASTOR_INDEX_INVALID; + case 3: + return TRUDecodingError_t::FASTOR_STARTTIME_INVALID; + default: + return TRUDecodingError_t::UNKNOWN_ERROR; + } +} + +const char* getTRUDecodingErrorName(TRUDecodingError_t errortype) +{ + switch (errortype) { + case TRUDecodingError_t::TRU_INDEX_INVALID: + return "TRUIndexInvalid"; + case TRUDecodingError_t::PATCH_INDEX_INVALID: + return "PatchIndexInvalid"; + case TRUDecodingError_t::FASTOR_INDEX_INVALID: + return "FastORIndexInvalid"; + case TRUDecodingError_t::FASTOR_STARTTIME_INVALID: + return "FastORStartTimeInvalid"; + default: + return "UnknownError"; + } +} + +const char* getTRUDecodingErrorName(unsigned int errorcode) +{ + return getTRUDecodingErrorName(getTRUDecodingErrorFromErrorCode(errorcode)); +} + +const char* getTRUDecodingErrorTitle(TRUDecodingError_t errortype) +{ + switch (errortype) { + case TRUDecodingError_t::TRU_INDEX_INVALID: + return "TRU index invalid"; + case TRUDecodingError_t::PATCH_INDEX_INVALID: + return "Patch index invalid"; + case TRUDecodingError_t::FASTOR_INDEX_INVALID: + return "FastOR index invalid"; + case TRUDecodingError_t::FASTOR_STARTTIME_INVALID: + return "FastOR starttime invalid"; + default: + return "Unknown error"; + } +} + +const char* getTRUDecodingErrorTitle(unsigned int errortype) +{ + return getTRUDecodingErrorTitle(getTRUDecodingErrorFromErrorCode(errortype)); +} + +const char* getTRUDecodingErrorErrorDescription(TRUDecodingError_t errortype) +{ + switch (errortype) { + case TRUDecodingError_t::TRU_INDEX_INVALID: + return "TRU index is invalid"; + case TRUDecodingError_t::PATCH_INDEX_INVALID: + return "Patch index is invalid"; + case TRUDecodingError_t::FASTOR_INDEX_INVALID: + return "FastOR index is invalid"; + case TRUDecodingError_t::FASTOR_STARTTIME_INVALID: + return "FastOR starttime is invalid"; + default: + return "Unknown error"; + } +} + +const char* getTRUDecodingErrorErrorDescription(unsigned int errorcode) +{ + return getTRUDecodingErrorErrorDescription(getTRUDecodingErrorFromErrorCode(errorcode)); +} + } // namespace reconstructionerrors } // namespace emcal diff --git a/Detectors/EMCAL/reconstruction/src/STUDecoderError.cxx b/Detectors/EMCAL/reconstruction/src/STUDecoderError.cxx new file mode 100644 index 0000000000000..bc88dc02199fa --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/STUDecoderError.cxx @@ -0,0 +1,110 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include +#include "EMCALReconstruction/STUDecoderError.h" + +using namespace o2::emcal; + +STUDecoderError::STUDecoderError(int ddlID, ErrorCode_t errcode) : mDDLId(ddlID), + mErrorCode(errcode) +{ + mMessage = "STU decoding error of type " + getErrorCodeTitle(mErrorCode) + " in DDL " + std::to_string(mDDLId); +} + +void STUDecoderError::printStream(std::ostream& stream) const +{ +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const STUDecoderError& error) +{ + error.printStream(stream); + return stream; +} + +int STUDecoderError::errorCodeToInt(ErrorCode_t errorcode) +{ + switch (errorcode) { + case ErrorCode_t::PAGE_ERROR: + return 0; + case ErrorCode_t::WORD_UNEXPECTED: + return 1; + case ErrorCode_t::INDEX_UNEXPECTED: + return 2; + case ErrorCode_t::ADC_OVERFLOW: + return 3; + case ErrorCode_t::FEEID_UNEXPECTED: + return 4; + case ErrorCode_t::OLD_PAYLOAD_VERSION: + return 5; + case ErrorCode_t::FULL_PAYLOAD_SIZE_UNEXPECTED: + return 6; + case ErrorCode_t::SHORT_PAYLOAD_SIZE_UNEXPECTED: + return 7; + default: + return -1; + } +} +STUDecoderError::ErrorCode_t STUDecoderError::intToErrorCode(int errorcode) +{ + static constexpr std::size_t NUMERRORCODES = 8; + static constexpr std::array errorcodes = {{ErrorCode_t::PAGE_ERROR, ErrorCode_t::WORD_UNEXPECTED, ErrorCode_t::INDEX_UNEXPECTED, ErrorCode_t::ADC_OVERFLOW, ErrorCode_t::FEEID_UNEXPECTED, ErrorCode_t::OLD_PAYLOAD_VERSION, ErrorCode_t::FULL_PAYLOAD_SIZE_UNEXPECTED, ErrorCode_t::SHORT_PAYLOAD_SIZE_UNEXPECTED}}; + if (errorcode < 0 || errorcode >= NUMERRORCODES) { + return ErrorCode_t::UNKNOWN; + } + return errorcodes[errorcode]; +} +std::string STUDecoderError::getErrorCodeName(ErrorCode_t errorcode) +{ + switch (errorcode) { + case ErrorCode_t::PAGE_ERROR: + return "PageDecoding"; + case ErrorCode_t::WORD_UNEXPECTED: + return "WordUnexpected"; + case ErrorCode_t::INDEX_UNEXPECTED: + return "IndexUnexpected"; + case ErrorCode_t::ADC_OVERFLOW: + return "ADCOverflow"; + case ErrorCode_t::FEEID_UNEXPECTED: + return "FeeIdUnexpected"; + case ErrorCode_t::OLD_PAYLOAD_VERSION: + return "OldPayloadVersion"; + case ErrorCode_t::FULL_PAYLOAD_SIZE_UNEXPECTED: + return "FullPayloadSizeUnexpected"; + case ErrorCode_t::SHORT_PAYLOAD_SIZE_UNEXPECTED: + return "ShortPayloadSizeUnexpected"; + default: + return "Unknown"; + } +} +std::string STUDecoderError::getErrorCodeTitle(ErrorCode_t errorcode) +{ + switch (errorcode) { + case ErrorCode_t::PAGE_ERROR: + return "page decoding"; + case ErrorCode_t::WORD_UNEXPECTED: + return "unexpected word"; + case ErrorCode_t::INDEX_UNEXPECTED: + return "invalid patch index"; + case ErrorCode_t::ADC_OVERFLOW: + return "ADC overflow"; + case ErrorCode_t::FEEID_UNEXPECTED: + return "invalid FeeID"; + case ErrorCode_t::OLD_PAYLOAD_VERSION: + return "unsupported old payload version"; + case ErrorCode_t::FULL_PAYLOAD_SIZE_UNEXPECTED: + return "unexpected full payload size"; + case ErrorCode_t::SHORT_PAYLOAD_SIZE_UNEXPECTED: + return "unexpected short payload size"; + default: + return "Unknown"; + } +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/StuDecoder.cxx b/Detectors/EMCAL/reconstruction/src/StuDecoder.cxx new file mode 100755 index 0000000000000..c0bd615a03c56 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/StuDecoder.cxx @@ -0,0 +1,245 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include +#include +#include +#include "InfoLogger/InfoLogger.hxx" +#include "DetectorsRaw/RDHUtils.h" +#include "EMCALReconstruction/RawReaderMemory.h" +#include "EMCALReconstruction/StuDecoder.h" +#include "EMCALReconstruction/STUDecoderError.h" + +using namespace o2::emcal; +using namespace o2::emcal::STUparam; + +StuDecoder::StuDecoder(RawReaderMemory& reader) : mRawReader(reader) +{ +} + +void StuDecoder::init() +{ + + auto& header = mRawReader.getRawHeader(); + auto feeID = o2::raw::RDHUtils::getFEEID(header); + + if (!((feeID == FeeID[STUtype_t::ESTU]) || (feeID == FeeID[STUtype_t::DSTU]))) { + throw STUDecoderError(feeID, STUDecoderError::ErrorCode_t::FEEID_UNEXPECTED); + } + + mSTU = STUtype_t::ESTU; + if (feeID == FeeID[STUtype_t::DSTU]) { + mSTU = STUtype_t::DSTU; + } + + // STU payload structure is described in JIRA-EMCAL-562 + auto& buffer = mRawReader.getPayload().getPayloadWords(); + bool vbit = ((buffer[0] >> 31) & 0x1); // payload version control bit + if (!vbit) { // old payloads version cannot be decoded with this method; discard 2022 early data + throw STUDecoderError(feeID, STUDecoderError::ErrorCode_t::OLD_PAYLOAD_VERSION); + } + + mIsFullPayload = (buffer[1] & 0x1); + + auto payloadsize = mRawReader.getPayload().getPayloadWords().size(); + + if (mIsFullPayload) { + if (payloadsize != getPaloadSizeFull()) { + // std::cout << "ERROR: Wrong payload size (=" << std::dec << payloadsize << ") for FullPaload \n"; + throw STUDecoderError(feeID, STUDecoderError::ErrorCode_t::FULL_PAYLOAD_SIZE_UNEXPECTED); + } + } + + else { + if (payloadsize != getPaloadSizeShort()) { + // std::cout << "ERROR: Wrong payload size (=" << std::dec << payloadsize << ") for ShortPaload \n"; + throw STUDecoderError(feeID, STUDecoderError::ErrorCode_t::SHORT_PAYLOAD_SIZE_UNEXPECTED); + } + } + + if (mDebug >= 3) { + std::cout << "==== paylaod size ===\n"; + int currentpos = 0; + while ((currentpos < buffer.size())) { + auto currentword = buffer[currentpos++]; + std::cout << std::dec << (currentpos - 1) << ") 0x" << std::hex << currentword << std::endl; + } + } +} + +void StuDecoder::decode() +{ + + init(); + + auto& buffer = mRawReader.getPayload().getPayloadWords(); + + mCFGWord0 = buffer[0]; + mCFGWord1 = buffer[1]; + mL0mask = buffer[2]; + mL1GammaHighThreshold = buffer[3]; + mShortPayloadRate = buffer[4]; + mL0bits = buffer[5]; + mL1JetHighThreshold = buffer[6]; + mL1GammaLowThreshold = buffer[9]; + mL1JetLowThreshold = buffer[12]; + mCFGword13 = buffer[13]; + mRegionEnable = buffer[14]; + mFrameReceived = buffer[15]; + mCFGword16 = buffer[16]; + + // decode L1Jet High/Low indices + int offset = getCFGWords(); + decodeL1JetPatchIndices(&buffer[offset]); + + // decode L1Gamma High/Low indices + offset += (2 * getL1JetIndexWords() + getL0indexWords()); + decodeL1GammaPatchIndices(&buffer[offset]); + + if (!isFullPayload()) { + return; + } + + // decode FastOR data + offset += (2 * getL1GammaIndexWords()); + decodeFastOrADC(&buffer[offset]); + + // std::cout << "lastWord = 0x" << std::hex << buffer[offset + getRawWords()] << std::endl; + return; +} + +void StuDecoder::decodeL1JetPatchIndices(const uint32_t* buffer) +{ + int offset = getL1JetIndexWords(); // offset for Jet Low threshold + + if (mDebug >= 2) { + for (int i = 0; i < offset; i++) { + std::cout << std::dec << i << ") 0x" << std::hex << buffer[i] << " <- Jet-high-word\n"; + } + for (int i = 0; i < offset; i++) { + std::cout << std::dec << i << ") 0x" << std::hex << buffer[offset + i] << " <- Jet-low-word\n"; + } + } + + int nSubregionEta = getSubregionsEta(); + int nSubregionPhi = getSubregionsPhi(); + bool isFired = false; + + int jetSize = 2 + getParchSize(); // 2 for 2x2-patch, 4 for 4x4=patch + + for (Int_t ix = 0; ix < nSubregionEta - (jetSize - 1); ix++) { + uint32_t row_JetHigh = buffer[ix]; + uint32_t row_JetLow = buffer[ix + offset]; + for (Int_t iy = 0; iy < nSubregionPhi - (jetSize - 1); iy++) { + isFired = (mSTU == STUtype_t::ESTU) ? (row_JetHigh & (1 << (nSubregionPhi - jetSize - iy))) : (row_JetHigh & (1 << iy)); + if (isFired) { + mL1JetHighPatchIndex.push_back(((ix << 8) & 0xFF00) | (iy & 0xFF)); + } + isFired = (mSTU == STUtype_t::ESTU) ? (row_JetLow & (1 << (nSubregionPhi - jetSize - iy))) : (row_JetLow & (1 << iy)); + if (isFired) { + mL1JetLowPatchIndex.push_back(((ix << 8) & 0xFF00) | (iy & 0xFF)); + } + } + } + + return; +} + +void StuDecoder::decodeL1GammaPatchIndices(const uint32_t* buffer) +{ + const int offset = getL1GammaIndexWords(); // offset for Gamma Low threshold + + if (mDebug >= 2) { + for (int i = 0; i < offset; i++) { + std::cout << std::dec << i << ") 0x" << std::hex << buffer[i] << " <- Gamma-high-word\n"; + } + for (int i = 0; i < offset; i++) { + std::cout << std::dec << i << ") 0x" << std::hex << buffer[offset + i] << " <- Gamma-low-word\n"; + } + } + + const int nTRU = getNumberOfTRUs(); + const int nTRU2 = getNumberOfTRUs() / 2; + + unsigned short gammaHigh[nTRU][TRUparam::NchannelsOverPhi]; + unsigned short gammaLow[nTRU][TRUparam::NchannelsOverPhi]; + + for (Int_t iphi = 0; iphi < TRUparam::NchannelsOverPhi / 2; iphi++) { + for (Int_t itru = 0; itru < nTRU2; itru++) { + gammaHigh[2 * itru][2 * iphi] = (buffer[iphi * nTRU2 + itru] >> 0 & 0xFF); + gammaHigh[2 * itru][2 * iphi + 1] = (buffer[iphi * nTRU2 + itru] >> 8 & 0xFF); + gammaHigh[2 * itru + 1][2 * iphi] = (buffer[iphi * nTRU2 + itru] >> 16 & 0xFF); + gammaHigh[2 * itru + 1][2 * iphi + 1] = (buffer[iphi * nTRU2 + itru] >> 24 & 0xFF); + + gammaLow[2 * itru][2 * iphi] = (buffer[offset + iphi * nTRU2 + itru] >> 0 & 0xFF); + gammaLow[2 * itru][2 * iphi + 1] = (buffer[offset + iphi * nTRU2 + itru] >> 8 & 0xFF); + gammaLow[2 * itru + 1][2 * iphi] = (buffer[offset + iphi * nTRU2 + itru] >> 16 & 0xFF); + gammaLow[2 * itru + 1][2 * iphi + 1] = (buffer[offset + iphi * nTRU2 + itru] >> 24 & 0xFF); + } + } + + for (Int_t iphi = 0; iphi < TRUparam::NchannelsOverPhi; iphi++) { + for (Int_t ieta = 0; ieta < TRUparam::NchannelsOverEta; ieta++) { + // loop over TRUs of Full or 2/3-size SMs + for (Int_t itru = 0; itru < (nTRU - 2); itru++) { + if ((gammaHigh[itru][iphi] >> ieta) & 0x1) { + mL1GammaHighPatchIndex.push_back(((iphi << 10) & 0x7C00) | ((ieta << 5) & 0x03E0) | ((itru << 0) & 0x001F)); + } + if ((gammaLow[itru][iphi] >> ieta) & 0x1) { + mL1GammaLowPatchIndex.push_back(((iphi << 10) & 0x7C00) | ((ieta << 5) & 0x03E0) | ((itru << 0) & 0x001F)); + } + } + // loop over TRUs of 1/3-size SMs + for (Int_t itru = (nTRU - 2); itru < nTRU; itru++) { + short iphi_tmp = (iphi % 2 + 2 * (int)(iphi / 6)); + short ieta_tmp = (ieta + 8 * ((int)(iphi / 2) % 3)); + if ((gammaHigh[itru][iphi] >> ieta) & 0x1) { + mL1GammaHighPatchIndex.push_back(((iphi_tmp << 10) & 0x7C00) | ((ieta_tmp << 5) & 0x03E0) | ((itru << 0) & 0x001F)); + } + if ((gammaLow[itru][iphi] >> ieta) & 0x1) { + mL1GammaLowPatchIndex.push_back(((iphi_tmp << 10) & 0x7C00) | ((ieta_tmp << 5) & 0x03E0) | ((itru << 0) & 0x001F)); + } + } + } + } + + return; +} + +void StuDecoder::decodeFastOrADC(const uint32_t* buffer) +{ + for (int i = 0; i < getRawWords(); i++) { + if (mDebug >= 2) { + std::cout << std::dec << i << ") 0x" << std::hex << buffer[i] << " <- FasrOR-word\n"; + } + mFastOrADC.push_back(int16_t(buffer[i] & 0xFFFF)); + mFastOrADC.push_back(int16_t(buffer[i] >> 16) & 0xFFFF); + } + + return; +} + +void StuDecoder::dumpSTUcfg() const +{ + + std::cout << "L1GammaHighThreshold:" << std::dec << getL1GammaHighThreshold() << std::endl; + std::cout << "L1JetHighThreshold :" << std::dec << getL1JetHighThreshold() << std::endl; + std::cout << "L1GammaLowThreshold :" << std::dec << getL1GammaLowThreshold() << std::endl; + std::cout << "L1JetLowThreshold :" << std::dec << getL1JetLowThreshold() << std::endl; + std::cout << "Rho :" << std::dec << getRho() << std::endl; + std::cout << "FrameReceivedSTU :" << std::dec << getFrameReceivedSTU() << std::endl; + std::cout << "RegionEnable :" << std::hex << "0x" << getRegionEnable() << std::endl; + std::cout << "FrameReceived :" << std::dec << getFrameReceived() << std::endl; + std::cout << "ParchSize :" << std::dec << getParchSize() << std::endl; + std::cout << "FWversion :" << std::hex << "0x" << getFWversion() << std::endl; + + std::cout << "\n"; +} diff --git a/Detectors/EMCAL/reconstruction/src/TRUDataHandler.cxx b/Detectors/EMCAL/reconstruction/src/TRUDataHandler.cxx new file mode 100644 index 0000000000000..d6d9bd3535d5c --- /dev/null +++ b/Detectors/EMCAL/reconstruction/src/TRUDataHandler.cxx @@ -0,0 +1,67 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "EMCALReconstruction/TRUDataHandler.h" + +using namespace o2::emcal; + +TRUDataHandler::TRUDataHandler() +{ + reset(); +} + +void TRUDataHandler::reset() +{ + mTRUIndex = -1; + mL0Fired = false; + mL0Time = -1; + std::fill(mPatchTimes.begin(), mPatchTimes.end(), UCHAR_MAX); +} + +void TRUDataHandler::printStream(std::ostream& stream) const +{ + std::string patchstring; + for (auto index = 0; index < mPatchTimes.size(); index++) { + if (hasPatch(index)) { + if (patchstring.length()) { + patchstring += ", "; + } + patchstring += std::to_string(index); + } + } + if (!patchstring.length()) { + patchstring = "-"; + } + stream << "TRU: " << static_cast(mTRUIndex) << ", time " << static_cast(mL0Time) << ", fired: " << (mL0Fired ? "yes" : "no") << ", patches: " << patchstring; +} + +TRUDataHandler::PatchIndexException::PatchIndexException(int8_t index) : mIndex(index), mMessage() +{ + mMessage = "Invalid patch index " + std::to_string(index); +} + +void TRUDataHandler::PatchIndexException::printStream(std::ostream& stream) const +{ + stream << what(); +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const TRUDataHandler& data) +{ + data.printStream(stream); + return stream; +} + +std::ostream& o2::emcal::operator<<(std::ostream& stream, const TRUDataHandler::PatchIndexException& error) +{ + error.printStream(stream); + return stream; +} \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/test/testAltroDecoderError.cxx b/Detectors/EMCAL/reconstruction/test/testAltroDecoderError.cxx index 811685f821009..ad68cf171e6f8 100644 --- a/Detectors/EMCAL/reconstruction/test/testAltroDecoderError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testAltroDecoderError.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test EMCAL Calib +#define BOOST_TEST_MODULETest EMCAL Reconstruction #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include diff --git a/Detectors/EMCAL/reconstruction/test/testCaloRawFitterError.cxx b/Detectors/EMCAL/reconstruction/test/testCaloRawFitterError.cxx index 33fe6862a84e8..1094f419e92ff 100644 --- a/Detectors/EMCAL/reconstruction/test/testCaloRawFitterError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testCaloRawFitterError.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test EMCAL Calib +#define BOOST_TEST_MODULE Test EMCAL Reconstruction #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include diff --git a/Detectors/EMCAL/reconstruction/test/testFastORTimeSeries.cxx b/Detectors/EMCAL/reconstruction/test/testFastORTimeSeries.cxx new file mode 100644 index 0000000000000..4144273e1eb31 --- /dev/null +++ b/Detectors/EMCAL/reconstruction/test/testFastORTimeSeries.cxx @@ -0,0 +1,175 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#define BOOST_TEST_MODULE Test EMCAL Reconstruction +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include +#include +#include "EMCALReconstruction/TRUDecodingErrors.h" + +namespace o2 +{ + +namespace emcal +{ + +void printBunch(const gsl::span adcs) +{ + bool first = true; + for (auto& adc : adcs) { + if (!first) { + std::cout << ", "; + } else { + first = false; + } + std::cout << adc; + } + std::cout << " (size " << adcs.size() << ")" << std::endl; +} + +std::vector getReversed(const std::vector& original) +{ + std::vector reversed(13); + for (std::size_t sample = 0; sample < 13; sample++) { + reversed[12 - sample] = original[sample]; + } + return reversed; +} + +std::tuple, std::vector> generatePulseTimeReversed() +{ + std::vector pulse(13); + std::fill(pulse.begin(), pulse.end(), 0); + // calculate forward pulse + auto peak_signal = static_cast(gRandom->Uniform(0, 1024)); + pulse[4] = peak_signal; + auto last = peak_signal; + for (std::size_t sample = 5; sample < 13; sample++) { + if (last == 0) { + break; + } + auto current = static_cast(gRandom->Uniform(0, last)); + pulse[sample] = current; + last = current; + } + last = peak_signal; + for (std::size_t sample = 3; sample > 0; sample--) { + if (last == 0) { + break; + } + auto current = static_cast(gRandom->Uniform(0, last)); + pulse[sample] = current; + last = current; + } + // find start time + uint8_t starttime = 12; + for (std::size_t currenttime = 12; currenttime > 0; currenttime--) { + starttime = currenttime; + if (pulse[currenttime]) { + break; + } + } + // time-reverse pulse + auto reversed = getReversed(pulse); + // zero-suppress time series + std::vector zerosuppressed; + bool bunchstart = false; + for (std::size_t sample = 0; sample < 13; sample++) { + if (reversed[sample] == 0) { + if (!bunchstart) { + continue; + } + break; + } + bunchstart = true; + zerosuppressed.push_back(reversed[sample]); + } + return std::make_tuple(starttime, zerosuppressed, pulse); +} + +uint16_t calculateTimesum(const std::vector samplesOrdered, uint8_t l0time) +{ + uint16_t timesum = 0; + uint8_t starttime = l0time - 4; + for (uint8_t sample = starttime; sample < starttime + 4; sample++) { + timesum += samplesOrdered[sample]; + } + return timesum; +} + +std::vector generateSmallBunch(uint8_t bunchlength) +{ + std::vector bunch(bunchlength); + auto peak_signal = static_cast(gRandom->Uniform(0, 1024)); + bunch[bunchlength - 2] = peak_signal; + bunch[bunchlength - 1] = static_cast(gRandom->Uniform(0, peak_signal)); + auto last = peak_signal; + for (int sample = bunchlength - 3; sample >= 0; sample--) { + auto current = static_cast(gRandom->Uniform(0, last)); + bunch[sample] = current; + last = current; + } + return bunch; +} + +void add_bunch_to_buffer(std::vector& buffer, const std::vector& bunch, uint8_t starttime) +{ + for (int sample = 0; sample < bunch.size(); sample++) { + buffer[starttime - sample] = bunch[bunch.size() - 1 - sample]; + } +} + +BOOST_AUTO_TEST_CASE(FastORTimeSeries_test) +{ + // test fill and integral + for (int itest = 0; itest < 500; itest++) { + auto [starttime, zerosuppressed, reference] = generatePulseTimeReversed(); + FastORTimeSeries testcase(13, zerosuppressed, starttime); + auto adcs = testcase.getADCs(); + BOOST_CHECK_EQUAL_COLLECTIONS(adcs.begin(), adcs.end(), reference.begin(), reference.end()); + BOOST_CHECK_EQUAL(testcase.calculateL1TimeSum(8), calculateTimesum(reference, 8)); + } + + // test case where a normal FEC channel is identified as TRU channel. FEC channel can have lenght of 15 and would therefore cause an overflow in the FEC channel (max lenght 14) + auto starttime = 14; + auto bunch = generateSmallBunch(14); + BOOST_CHECK_EXCEPTION(FastORTimeSeries(14, bunch, starttime), FastOrStartTimeInvalidException, [starttime](const FastOrStartTimeInvalidException& e) { return e.getStartTime() == starttime; }); + + return; + + // test adding 2 bunches + for (int itest = 0; itest < 500; itest++) { + auto length_bunch1 = static_cast(gRandom->Uniform(3, 5)), + length_bunch2 = static_cast(gRandom->Uniform(3, 5)); + auto sumbunchlength = length_bunch1 + length_bunch2; + auto offset_bunch1 = static_cast(gRandom->Uniform(0, 13 - sumbunchlength)), + offset_bunch2 = static_cast(gRandom->Uniform(0, 13 - sumbunchlength - offset_bunch1)); + auto bunch1 = generateSmallBunch(length_bunch1), + bunch2 = generateSmallBunch(length_bunch2); + auto starttime_bunch1 = offset_bunch1 + length_bunch1, + starttime_bunch2 = starttime_bunch1 + offset_bunch2 + length_bunch2; + std::vector buffer_reversed{13}; + add_bunch_to_buffer(buffer_reversed, bunch2, starttime_bunch2); + add_bunch_to_buffer(buffer_reversed, bunch1, starttime_bunch1); + FastORTimeSeries testcase(13, bunch2, starttime_bunch2); + testcase.setTimeSamples(bunch1, starttime_bunch1); + auto adcs_timeordered = getReversed(buffer_reversed); + auto adcs_timeseries_reversed = testcase.getADCs(); + BOOST_CHECK_EQUAL_COLLECTIONS(adcs_timeseries_reversed.begin(), adcs_timeseries_reversed.end(), adcs_timeordered.begin(), adcs_timeordered.end()); + } +} + +} // namespace emcal + +} // namespace o2 diff --git a/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx b/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx index 785ace30e9307..9fc088ebc6a3e 100644 --- a/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test EMCAL Calib +#define BOOST_TEST_MODULE Test EMCAL Reconstruction #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include @@ -23,23 +23,39 @@ namespace emcal BOOST_AUTO_TEST_CASE(MinorAltroDecodingError_test) { - BOOST_CHECK_EQUAL(MinorAltroDecodingError::getNumberOfErrorTypes(), 4); - std::array errornames = {{"ChannelEndPayloadUnexpected", + BOOST_CHECK_EQUAL(MinorAltroDecodingError::getNumberOfErrorTypes(), 8); + std::array errornames = {{"ChannelEndPayloadUnexpected", "ChannelPayloadExceed", + "ChannelOrderError", + "ChannelHeader", "BunchHeaderNull", - "BunchLengthExceed"}}, + "BunchLengthExceed", + "BunchLengthAllowExceed", + "BunchStarttimeExceed"}}, errortitles = {{"Channel end unexpected", "Channel exceed", + "FEC order", + "Channel header invalid", "Bunch header null", - "Bunch length exceed"}}, + "Bunch length exceed", + "Bunch length impossible", + "Bunch starttime exceed"}}, errordescriptions = {{"Unexpected end of payload in altro channel payload!", "Trying to access out-of-bound payload!", + "Invalid FEC order", + "Invalid channel header", "Bunch header 0 or not configured!", - "Bunch length exceeding channel payload size!"}}; - std::array errortypes = {{MinorAltroDecodingError::ErrorType_t::CHANNEL_END_PAYLOAD_UNEXPECT, + "Bunch length exceeding channel payload size!", + "Bunch length exceeding max. possible bunch size!", + "Bunch start time outside range!"}}; + std::array errortypes = {{MinorAltroDecodingError::ErrorType_t::CHANNEL_END_PAYLOAD_UNEXPECT, MinorAltroDecodingError::ErrorType_t::CHANNEL_PAYLOAD_EXCEED, + MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, + MinorAltroDecodingError::ErrorType_t::CHANNEL_HEADER, MinorAltroDecodingError::ErrorType_t::BUNCH_HEADER_NULL, - MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_EXCEED}}; + MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_EXCEED, + MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_ALLOW_EXCEED, + MinorAltroDecodingError::ErrorType_t::BUNCH_STARTTIME}}; for (int errortype = 0; errortype < MinorAltroDecodingError::getNumberOfErrorTypes(); errortype++) { BOOST_CHECK_EQUAL(MinorAltroDecodingError::errorTypeToInt(errortypes[errortype]), errortype); BOOST_CHECK_EQUAL(MinorAltroDecodingError::intToErrorType(errortype), errortypes[errortype]); diff --git a/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx b/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx index 87004f88c09a1..f701531dc9545 100644 --- a/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test EMCAL Calib +#define BOOST_TEST_MODULE Test EMCAL Reconstruction #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include @@ -28,37 +28,39 @@ void testThrow(RawDecodingError::ErrorType_t errtype, unsigned int feeID) BOOST_AUTO_TEST_CASE(RawDecodingError_test) { - BOOST_CHECK_EQUAL(RawDecodingError::getNumberOfErrorTypes(), 7); - std::array errornames = {{"PageNotFound", + BOOST_CHECK_EQUAL(RawDecodingError::getNumberOfErrorTypes(), 8); + std::array errornames = {{"PageNotFound", "HeaderDecoding", "PayloadDecoding", "HeaderCorruption", "PageStartInvalid", "PayloadCorruption", - "TrailerDecoding"}}, + "TrailerDecoding", + "TrailerIncomplete"}}, errortitles = {{"Page not found", "Header decoding", "Payload decoding", "Header corruption", "Page start invalid", "Payload corruption", - "Trailer decoding"}}, + "Trailer decoding", + "Trailer incomplete"}}, errordescriptions = {{"Page with requested index not found", "RDH of page cannot be decoded", "Payload of page cannot be decoded", "Access to header not belonging to requested superpage", "Page decoding starting outside payload size", "Access to payload not belonging to requested superpage", - "Inconsistent trailer in memory"}}; - std::array errortypes = {{ - RawDecodingError::ErrorType_t::PAGE_NOTFOUND, - RawDecodingError::ErrorType_t::HEADER_DECODING, - RawDecodingError::ErrorType_t::PAYLOAD_DECODING, - RawDecodingError::ErrorType_t::HEADER_INVALID, - RawDecodingError::ErrorType_t::PAGE_START_INVALID, - RawDecodingError::ErrorType_t::PAYLOAD_INVALID, - RawDecodingError::ErrorType_t::TRAILER_DECODING, - }}; + "Inconsistent trailer in memory", + "Incomplete trailer"}}; + std::array errortypes = {{RawDecodingError::ErrorType_t::PAGE_NOTFOUND, + RawDecodingError::ErrorType_t::HEADER_DECODING, + RawDecodingError::ErrorType_t::PAYLOAD_DECODING, + RawDecodingError::ErrorType_t::HEADER_INVALID, + RawDecodingError::ErrorType_t::PAGE_START_INVALID, + RawDecodingError::ErrorType_t::PAYLOAD_INVALID, + RawDecodingError::ErrorType_t::TRAILER_DECODING, + RawDecodingError::ErrorType_t::TRAILER_INCOMPLETE}}; for (int errortype = 0; errortype < RawDecodingError::getNumberOfErrorTypes(); errortype++) { BOOST_CHECK_EQUAL(RawDecodingError::ErrorTypeToInt(errortypes[errortype]), errortype); BOOST_CHECK_EQUAL(RawDecodingError::intToErrorType(errortype), errortypes[errortype]); diff --git a/Detectors/EMCAL/reconstruction/test/testRecoContainer.cxx b/Detectors/EMCAL/reconstruction/test/testRecoContainer.cxx index f440d05920053..182c7ceacb654 100644 --- a/Detectors/EMCAL/reconstruction/test/testRecoContainer.cxx +++ b/Detectors/EMCAL/reconstruction/test/testRecoContainer.cxx @@ -8,7 +8,7 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#define BOOST_TEST_MODULE Test EMCAL Calib +#define BOOST_TEST_MODULE Test EMCAL Reconstruction #define BOOST_TEST_MAIN #define BOOST_TEST_DYN_LINK #include diff --git a/Detectors/EMCAL/reconstruction/test/testTRUDataHandler.cxx b/Detectors/EMCAL/reconstruction/test/testTRUDataHandler.cxx new file mode 100644 index 0000000000000..5bab7bf04cc1f --- /dev/null +++ b/Detectors/EMCAL/reconstruction/test/testTRUDataHandler.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#define BOOST_TEST_MODULE Test EMCAL Reconstruction +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace emcal +{ + +BOOST_AUTO_TEST_CASE(TRUDataHandler_test) +{ + o2::emcal::TRUDataHandler testhandler; + + // no patch set + BOOST_CHECK_EQUAL(testhandler.hasAnyPatch(), false); + for (int ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + BOOST_CHECK_EQUAL(testhandler.hasPatch(ipatch), false); + } + + // set all patches with L0 time 8 + for (int ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + testhandler.setPatch(ipatch, 8); + } + BOOST_CHECK_EQUAL(testhandler.hasAnyPatch(), true); + for (int ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + BOOST_CHECK_EQUAL(testhandler.hasPatch(ipatch), true); + BOOST_CHECK_EQUAL(testhandler.getPatchTime(ipatch), 8); + } + + // test reset + testhandler.reset(); + BOOST_CHECK_EQUAL(testhandler.hasAnyPatch(), false); + for (int ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + BOOST_CHECK_EQUAL(testhandler.hasPatch(ipatch), false); + } + + // test error handling + for (int8_t index = o2::emcal::TriggerMappingV2::PATCHESINTRU; index < INT8_MAX; index++) { + BOOST_CHECK_EXCEPTION(testhandler.hasPatch(index), o2::emcal::TRUDataHandler::PatchIndexException, [index](const o2::emcal::TRUDataHandler::PatchIndexException& e) { return e.getIndex() == index; }); + BOOST_CHECK_EXCEPTION(testhandler.setPatch(index, 8), o2::emcal::TRUDataHandler::PatchIndexException, [index](const o2::emcal::TRUDataHandler::PatchIndexException& e) { return e.getIndex() == index; }); + } + + for (int iiter = 0; iiter < 100; iiter++) { + // For 100 iterations simulate patch index and time + std::map patchtimes; + int npatches_expect = static_cast(gRandom->Uniform(0, o2::emcal::TriggerMappingV2::PATCHESINTRU)); + while (patchtimes.size() < npatches_expect) { + auto patchindex = static_cast(gRandom->Uniform(0, o2::emcal::TriggerMappingV2::PATCHESINTRU)); + if (patchtimes.find(patchindex) == patchtimes.end()) { + auto patchtime = static_cast(gRandom->Gaus(8, 1)); + if (patchtime >= 12) { + patchtime = 11; + } + patchtimes[patchindex] = patchtime; + } + } + o2::emcal::TRUDataHandler iterhandler; + iterhandler.setFired(npatches_expect > 0); + for (auto [patchindex, patchtime] : patchtimes) { + iterhandler.setPatch(patchindex, patchtime); + } + + BOOST_CHECK_EQUAL(iterhandler.isFired(), npatches_expect > 0); + BOOST_CHECK_EQUAL(iterhandler.hasAnyPatch(), npatches_expect > 0); + for (auto ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + auto hasPatch = patchtimes.find(ipatch) != patchtimes.end(); + BOOST_CHECK_EQUAL(iterhandler.hasPatch(ipatch), hasPatch); + if (hasPatch) { + BOOST_CHECK_EQUAL(iterhandler.getPatchTime(ipatch), patchtimes[ipatch]); + } + } + } +} + +} // namespace emcal + +} // namespace o2 \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/CMakeLists.txt b/Detectors/EMCAL/simulation/CMakeLists.txt index a392f088b0767..c08bc8c53c69b 100644 --- a/Detectors/EMCAL/simulation/CMakeLists.txt +++ b/Detectors/EMCAL/simulation/CMakeLists.txt @@ -10,16 +10,21 @@ # or submit itself to any jurisdiction. o2_add_library(EMCALSimulation - SOURCES src/Detector.cxx src/Digitizer.cxx src/SDigitizer.cxx - src/DigitsWriteoutBuffer.cxx src/DigitsVectorStream.cxx src/SpaceFrame.cxx src/SimParam.cxx + SOURCES src/Detector.cxx src/Digitizer.cxx src/DigitizerTRU.cxx src/SDigitizer.cxx + src/DigitsWriteoutBuffer.cxx src/DigitsWriteoutBufferTRU.cxx src/LZEROElectronics.cxx src/TRUElectronics.cxx src/DigitsVectorStream.cxx src/SpaceFrame.cxx src/SimParam.cxx src/LabeledDigit.cxx src/RawWriter.cxx - PUBLIC_LINK_LIBRARIES ROOT::TreePlayer O2::EMCALBase O2::DetectorsBase O2::SimConfig O2::SimulationDataFormat O2::Headers O2::DetectorsRaw O2::EMCALReconstruction O2::DataFormatsCTP) + PUBLIC_LINK_LIBRARIES ROOT::TreePlayer O2::EMCALBase O2::DetectorsBase O2::SimConfig O2::SimulationDataFormat O2::Headers O2::DetectorsRaw O2::EMCALReconstruction O2::EMCALCalib O2::DataFormatsCTP) o2_target_root_dictionary(EMCALSimulation HEADERS include/EMCALSimulation/Detector.h include/EMCALSimulation/Digitizer.h + include/EMCALSimulation/DigitizerTRU.h include/EMCALSimulation/SDigitizer.h include/EMCALSimulation/DigitsWriteoutBuffer.h + include/EMCALSimulation/DigitsWriteoutBufferTRU.h + include/EMCALSimulation/LZEROElectronics.h + include/EMCALSimulation/TRUElectronics.h + include/EMCALSimulation/DigitTimebin.h include/EMCALSimulation/DigitsVectorStream.h include/EMCALSimulation/RawWriter.h include/EMCALSimulation/SpaceFrame.h diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitTimebin.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitTimebin.h new file mode 100644 index 0000000000000..87903688d8788 --- /dev/null +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitTimebin.h @@ -0,0 +1,59 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DigitTimebin.h +/// \brief EMCAL DigitTimebin for the DigitsWriteoutBuffer and DigitsWriteoutBufferTRU +#ifndef ALICEO2_EMCAL_DIGITTIMEBIN_H +#define ALICEO2_EMCAL_DIGITTIMEBIN_H + +#include "DataFormatsEMCAL/Digit.h" +#include "EMCALSimulation/LabeledDigit.h" +#include "CommonDataFormat/InteractionRecord.h" + +namespace o2 +{ + +namespace emcal +{ + +template +struct DigitTimebinBase; + +/// \struct DigitTimebinBase +/// \brief DigitTimebinBase templated, used for the DigitsWriteoutBuffer and DigitsWriteoutBufferTRU +/// \ingroup EMCALsimulation +/// \author Markus Fasel, ORNL +/// \author Hadi Hassan, ORNL +/// \author Simone Ragoni, Creighton U. +/// \date 16/12/2022 +/// +/// \param mRecordMode record mode +/// \param mEndWindow end window +/// \param mTriggerColl trigger collision +/// \param mInterRecord InteractionRecord +/// \param mDigitMap map of the digits, templated +template +struct DigitTimebinBase { + bool mRecordMode = false; + bool mEndWindow = false; + bool mTriggerColl = false; + std::optional mInterRecord; + std::shared_ptr>> mDigitMap = std::make_shared>>(); + ClassDefNV(DigitTimebinBase, 1); +}; + +/// \brief DigitTimebin is DigitTimebinBase +using DigitTimebin = DigitTimebinBase; +using DigitTimebinTRU = DigitTimebinBase; + +} // namespace emcal +} // namespace o2 +#endif /* ALICEO2_EMCAL_DIGITTIMEBIN_H */ diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/Digitizer.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/Digitizer.h index 88971fba97d72..66f85184c98e6 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/Digitizer.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/Digitizer.h @@ -12,6 +12,7 @@ #ifndef ALICEO2_EMCAL_FEEDIGITIZER_H #define ALICEO2_EMCAL_FEEDIGITIZER_H +#include #include #include #include @@ -21,6 +22,7 @@ #include "TObject.h" // for TObject #include "TRandom3.h" +#include "DataFormatsEMCAL/Constants.h" #include "DataFormatsEMCAL/Digit.h" #include "EMCALBase/Hit.h" #include "EMCALSimulation/SimParam.h" @@ -65,13 +67,13 @@ class Digitizer : public TObject /// Steer conversion of hits to digits void process(const std::vector& labeledDigit); - void setEventTime(o2::InteractionTimeRecord record); + void setEventTime(o2::InteractionTimeRecord record, bool trigger); double getTriggerTime() const { return mDigits.getTriggerTime(); } double getEventTime() const { return mDigits.getEventTime(); } bool isLive(double t) const { return mDigits.isLive(t); } bool isLive() const { return mDigits.isLive(); } + bool isCurrentEventTriggered() const { return mDigits.isCurrentEventTriggered(); } - void setWindowStartTime(int time) { mTimeWindowStart = time; } void setDebugStreaming(bool doStreaming) { mEnableDebugStreaming = doStreaming; } // function returns true if the collision occurs 600ns before the readout window is open @@ -81,6 +83,7 @@ class Digitizer : public TObject bool doSmearEnergy() const { return mSmearEnergy; } double smearEnergy(double energy); + double smearTime(double time, double energy); bool doSimulateTimeResponse() const { return mSimulateTimeResponse; } void sampleSDigit(const Digit& sdigit); @@ -92,24 +95,32 @@ class Digitizer : public TObject const std::vector& getTriggerRecords() const { return mDigits.getTriggerRecords(); } const o2::dataformats::MCTruthContainer& getMCLabels() const { return mDigits.getMCLabels(); } + static constexpr int getTOFSamplingBins() { return EMC_TOF_BINS; } + private: - short mEventTimeOffset = 0; ///< event time difference from trigger time (in number of bins) - short mPhase = 0; ///< event phase - UInt_t mROFrameMin = 0; ///< lowest RO frame of current digits - UInt_t mROFrameMax = 0; ///< highest RO frame of current digits - bool mSmearEnergy = true; ///< do time and energy smearing - bool mSimulateTimeResponse = true; ///< simulate time response - const SimParam* mSimParam = nullptr; ///< SimParam object - - std::vector mTempDigitVector; ///< temporary digit storage - // std::unordered_map> mDigits; ///< used to sort digits and labels by tower + using TimeSampleContainer = std::array; + static constexpr int EMC_PHASES = 4; ///< Number of phases + static constexpr int EMC_TOF_BINS = 1500; ///< Number of bins in TOF sampling of the time response + static constexpr double EMC_TOF_MIN = 0; ///< Min TOF + static constexpr double EMC_TOF_MAX = 1500.; ///< Max TOF + static constexpr double EMC_TOF_BINWITH = (EMC_TOF_MAX - EMC_TOF_MIN) / EMC_TOF_BINS; ///< Number time samples simulated + short mEventTimeOffset = 0; ///< event time difference from trigger time (in number of bins) + short mPhase = 0; ///< event phase + UInt_t mROFrameMin = 0; ///< lowest RO frame of current digits + UInt_t mROFrameMax = 0; ///< highest RO frame of current digits + bool mSmearEnergy = true; ///< do time and energy smearing + bool mSimulateTimeResponse = true; ///< simulate time response + const SimParam* mSimParam = nullptr; ///< SimParam object + bool mIsBeforeFirstRO = false; ///< check if the signal comes before the ROF + o2::InteractionRecord mIRFirstSampledTF; ///< IR of the 1st sampled IR, noise-only ROFs will be inserted till this IR only + double mTimeBCns; ///< time difference between bc and start of ROF in ns + + std::vector mTempDigitVector; ///< temporary digit storage o2::emcal::DigitsWriteoutBuffer mDigits; ///< used to sort digits and labels by tower - TRandom3* mRandomGenerator = nullptr; ///< random number generator - std::vector> mAmplitudeInTimeBins; ///< amplitude of signal for each time bin - - int mTimeWindowStart = 7; ///< The start of the time window - int mDelay = 7; ///< number of (full) time bins corresponding to the signal time delay + TRandom3* mRandomGenerator = nullptr; ///< random number generator + std::array, EMC_PHASES> + mAmplitudeInTimeBins; ///< template of the sampled time response function: amplitude of signal for each time bin (per phase) std::unique_ptr mDebugStream = nullptr; bool mEnableDebugStreaming = false; diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitizerTRU.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitizerTRU.h new file mode 100644 index 0000000000000..1a29176b8e9cc --- /dev/null +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitizerTRU.h @@ -0,0 +1,161 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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_TRIGGERDIGITIZER_H +#define ALICEO2_EMCAL_TRIGGERDIGITIZER_H + +#include +#include +#include +#include + +#include "Rtypes.h" // for DigitizerTRU::Class, Double_t, ClassDef, etc +#include "TObject.h" // for TObject +#include "TRandom3.h" + +#include "DataFormatsEMCAL/Digit.h" +#include "EMCALBase/Hit.h" +#include "EMCALBase/TriggerMappingV2.h" +#include "EMCALSimulation/SimParam.h" +#include "EMCALSimulation/LabeledDigit.h" +#include "EMCALSimulation/DigitsWriteoutBufferTRU.h" +#include "EMCALSimulation/LZEROElectronics.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "EMCALCalib/FeeDCS.h" + +namespace o2 +{ +namespace utils +{ +class TreeStreamRedirector; +} +namespace emcal +{ +class FeeDCS; + +/// \class DigitizerTRU +/// \brief EMCAL DigitizerTRU, digitizes with the help of a temporary description based upon a pol9*Heavyside +/// \ingroup EMCALsimulation +/// \author Anders Knospe, University of Houston +/// \author Hadi Hassan, ORNL +/// \author Simone Ragoni, Creighton +class DigitizerTRU +{ + public: + DigitizerTRU() = default; + ~DigitizerTRU() = default; + DigitizerTRU(const DigitizerTRU&) = delete; + DigitizerTRU& operator=(const DigitizerTRU&) = delete; + + void init(); + void clear(); + + /// \brief Sets patches for the current geometry + void setPatches(); + + /// clear DigitsVectorStream + void flush() { mDigits.flush(); } + + /// This is for the readout window that was interrupted by the end of the run + void finish(); + + /// Steer conversion of hits to digits + void process(const gsl::span summableDigits); + + /// Postprocessing of the digits, gathers by Fastors, not by Tower/Cell + /// \param sdigits results of the SDigitizer + std::vector> makeAnaloguesFastorSums(const gsl::span sdigits); + + void setEventTime(o2::InteractionTimeRecord record); + + /// Sets geometry for trigger mapping + void setGeometry(o2::emcal::Geometry* gm) { mGeometry = gm; } + + /// Sets FEE DCS for the masking of the fastOrs + void setFEE(o2::emcal::FeeDCS* fees) { mFeeDCS = fees; } + + /// Sets the masked fastOrs from the CCDB in the LZERO + void setMaskedFastOrsInLZERO(); + void printMaskedFastOrsInLZERO(); + + void setWindowStartTime(int time) { mTimeWindowStart = time; } + void setDebugStreaming(bool doStreaming) { mEnableDebugStreaming = doStreaming; } + + void fillOutputContainer(std::vector& digits, o2::dataformats::MCTruthContainer& labels); + + bool doSmearEnergy() const { return mSmearEnergy; } + double smearEnergy(double energy); + double smearTime(double time, double energy); + bool doSimulateTimeResponse() const { return mSimulateTimeResponse; } + + void sampleSDigit(const Digit& sdigit); + + /// Close the TreeStreamer to make the file readable + void endDebugStream() + { + mDebugStream->Close(); + // mDebugStreamPatch->Close(); + } + + /// Getter for debug mode + bool isDebugMode() { return mEnableDebugStreaming; } + + /// Getter for triggers + const std::vector& getTriggerInputs() { return LZERO.getTriggerInputs(); } + + /// Getter for patches + std::vector getPatchesVector() { return patchesFromAllTRUs; } + + /// raw pointers used here to allow interface with TF1 + static double rawResponseFunction(double* x, double* par); + + /// Utility functions obtained from QC for EMC + int GetTRUIndexFromSTUIndex(Int_t id, Int_t detector); + int GetChannelForMaskRun2(int mask, int bitnumber, bool onethirdsm); + std::vector GetAbsFastORIndexFromMask(); + + private: + short mEventTimeOffset = 0; ///< event time difference from trigger time (in number of bins) + bool mSmearEnergy = true; ///< do time and energy smearing + bool mSimulateTimeResponse = true; ///< simulate time response + // const SimParam* mSimParam = nullptr; ///< SimParam object + + std::vector mTempDigitVector; ///< temporary digit storage + // std::unordered_map> mDigits; ///< used to sort digits and labels by tower + o2::emcal::DigitsWriteoutBufferTRU mDigits; ///< used to sort digits by tower + o2::emcal::LZEROElectronics LZERO; ///< to start the trigger + std::vector patchesFromAllTRUs; ///< patches from all TRUs + + // TRandom3* mRandomGenerator = nullptr; ///< random number generator + std::array + mAmplitudeInTimeBins; ///< template of the sampled time response function: amplitude of signal for each time bin (per phase) + + // TriggerMappingV2* mTriggerMap = nullptr; ///< Trigger map for tower to fastor ID + Geometry* mGeometry = nullptr; ///< EMCAL geometry + + int mTimeWindowStart = 7; ///< The start of the time window + int mDelay = 7; ///< number of (full) time bins corresponding to the signal time delay + bool mWasTriggerFound = false; ///< To save the data + int mPreviousTriggerSize = 0; ///< To save the data + + std::unique_ptr mDebugStream = nullptr; + // std::unique_ptr mDebugStreamPatch = nullptr; + bool mEnableDebugStreaming = false; + o2::emcal::FeeDCS* mFeeDCS; ///< EMCAL FEE DCS + + ClassDefNV(DigitizerTRU, 1); +}; +} // namespace emcal +} // namespace o2 + +#endif /* ALICEO2_EMCAL_TRIGGERDIGITIZER_H */ diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsVectorStream.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsVectorStream.h index 15848ce4839eb..861b4d44d54b7 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsVectorStream.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsVectorStream.h @@ -25,6 +25,7 @@ #include "DataFormatsEMCAL/TriggerRecord.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "EMCALSimulation/SimParam.h" +#include "EMCALSimulation/DigitTimebin.h" namespace o2 { @@ -38,14 +39,6 @@ namespace emcal /// \author Markus Fasel, ORNL /// \date 16/02/2022 -struct DigitTimebin { - bool mRecordMode = false; - bool mEndWindow = false; - bool mTriggerColl = false; - std::optional mInterRecord; - std::shared_ptr>> mDigitMap = std::make_shared>>(); -}; - class DigitsVectorStream { diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBuffer.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBuffer.h index 50b6ae5a7afcc..5713f2ef18ad9 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBuffer.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBuffer.h @@ -60,8 +60,21 @@ class DigitsWriteoutBuffer double getTriggerTime() const { return mTriggerTime; } double getEventTime() const { return mLastEventTime; } - bool isLive(double t) const { return ((t - mTriggerTime) < mLiveTime || (t - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime)); } - bool isLive() const { return ((mLastEventTime - mTriggerTime) < mLiveTime || (mLastEventTime - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime)); } + bool isLive(double t) const + { + return ((t - mTriggerTime) < mLiveTime || (t - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime)); + } + bool isLive() const + { + return ((mLastEventTime - mTriggerTime) < (mLiveTime - mPreTriggerTime) || (mLastEventTime - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime)); + } + + /// Check if current collision was triggered + /// \return true if event was triggered + bool isCurrentEventTriggered() const + { + return mLastEventTime == mTriggerTime; + } // function returns true if the collision occurs 600ns before the readout window is open // Look here for more details https://alice.its.cern.ch/jira/browse/EMCAL-681 @@ -81,7 +94,7 @@ class DigitsWriteoutBuffer unsigned int getBufferSize() const { return mBufferSize; } /// forward the marker for every 100 ns - void forwardMarker(o2::InteractionTimeRecord record); + void forwardMarker(o2::InteractionTimeRecord record, bool trigger); /// Setters for the live time, busy time, pre-trigger time void setLiveTime(unsigned int liveTime) { mLiveTime = liveTime; } diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBufferTRU.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBufferTRU.h new file mode 100644 index 0000000000000..d6452a91b920e --- /dev/null +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBufferTRU.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +//_____________________________________________ +// GENERAL IDEA +// +// - Focus on https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/TPC/simulation/include/TPCSimulation/DigitContainer.h +// - Comform it to the classes used by EMCAL in https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsWriteoutBuffer.h +// and in https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/EMCAL/simulation/include/EMCALSimulation/DigitsVectorStream.h +// - The two concepts are similar +// - EMCAL's was however adapted to triggered mode +// - The principle is to reapply the old TPC format to the continuous readout directly +// +// 1) the idea of dumping the time bins in 15 bins is dumped +// 2) the data are pushed to the output stream whenever possible +// 3) the EndOfRun is implemented to break the push to stream +// it is a flag set to 1 which is read by the filloutputcontainer +// 4) no more check of start or end of trigger +// 5) what about the precollision flags? +// 6) is there any longer a need to set the time of the sampled digits time? + +#ifndef ALICEO2_EMCAL_DIGITSWRITEOUTBUFFERTRU_H_ +#define ALICEO2_EMCAL_DIGITSWRITEOUTBUFFERTRU_H_ + +#include +#include +#include +#include +#include +#include +#include "DataFormatsEMCAL/Digit.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "EMCALSimulation/LZEROElectronics.h" +#include "EMCALSimulation/DigitTimebin.h" + +// using namespace o2::emcal; + +namespace o2 +{ +namespace emcal +{ + +/// \class DigitsWriteoutBufferTRU +/// \brief Container class for time sampled digits to be sent to TRUs in true continuous readout +/// \ingroup EMCALsimulation +/// \author Hadi Hassan, ORNL +/// \author Markus Fasel, ORNL +/// \author Simone Ragoni, ORNL +/// \date 27/09/2022 + +class DigitsWriteoutBufferTRU +{ + public: + /// Default constructor + DigitsWriteoutBufferTRU(unsigned int nTimeBins = 15); + + /// Destructor + ~DigitsWriteoutBufferTRU() = default; + + /// clear the container + void clear(); + + /// clear DigitsVectorStream + void flush() + { + // mDigitStream.clear(); + } + + void init(); + + /// Reserve space for the future container + /// \param eventTimeBin resize adding at the end + void reserve(int eventTimeBin); + + /// This is for the readout window that was interrupted by the end of the run + void finish(); + + /// Add digit to the container + /// \param towerID Cell ID + /// \param dig Labaled digit to add + void addDigits(unsigned int towerID, std::vector& digList); + + /// Fill output streamer + /// \param isEndOfTimeFrame End of Time Frame + /// \param nextInteractionRecord Next interaction record, to compute the amount of TimeBins to be saved + void fillOutputContainer(bool isEndOfTimeFrame, InteractionRecord& nextInteractionRecord, std::vector& patchesFromAllTRUs, LZEROElectronics& LZERO); + + /// Setters for the live time, busy time, pre-trigger time + void setLiveTime(unsigned int liveTime) { mLiveTime = liveTime; } + void setBusyTime(unsigned int busyTime) { mBusyTime = busyTime; } + + const std::deque& getTimeBins() const { return mTimeBins; } + + private: + unsigned int mBufferSize = 15; ///< The size of the buffer + unsigned int mLiveTime = 1500; ///< EMCal live time (ns) + unsigned int mBusyTime = 35000; ///< EMCal busy time (ns) + // unsigned int mPreTriggerTime = 600; ///< EMCal pre-trigger time (ns) + unsigned long mTriggerTime = 0; ///< Time of the collision that fired the trigger (ns) + unsigned long mLastEventTime = 0; ///< The event time of last collisions in the readout window + unsigned int mPhase = 0; ///< The event L1 phase + bool mFirstEvent = true; ///< Flag to the first event in the run + std::deque mTimeBins; ///< Container for time sampled digits per tower ID for continuous digits + unsigned int mFirstTimeBin = 0; + bool mEndOfRun = 0; + bool mNoPileupMode = false; ///< pileup mode from SimParam + o2::InteractionRecord mCurrentInteractionRecord; ///< Interaction Record of the current event, to be used to fill the output container + + ClassDefNV(DigitsWriteoutBufferTRU, 5); + // ClassDefNV are for objects which do not inherit from tobject + // you do not need classIMP instead +}; + +} // namespace emcal + +} // namespace o2 + +#endif /* ALICEO2_EMCAL_DIGITSWRITEOUTBUFFERTRU_H_ */ \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/LZEROElectronics.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/LZEROElectronics.h new file mode 100644 index 0000000000000..4927515b7691c --- /dev/null +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/LZEROElectronics.h @@ -0,0 +1,152 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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_LZEROELECTRONICS_H_ +#define ALICEO2_EMCAL_LZEROELECTRONICS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "TRandom3.h" +#include "DataFormatsEMCAL/Digit.h" +#include "EMCALSimulation/DigitTimebin.h" +#include "DataFormatsEMCAL/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "EMCALSimulation/SimParam.h" +#include "EMCALSimulation/TRUElectronics.h" +#include "EMCALBase/TriggerMappingV2.h" +#include // for LOG + +namespace o2 +{ +namespace emcal +{ + +/// @brief Trigger Inputs object, summary of the entire information needed for the L1 algorithm +/// \param mInterRecord Last known interaction record +/// \param mLastTimesumAllFastOrs Vector of tuples with TRU ID, FastOrID with STU indexing, and their last Timesums +struct EMCALTriggerInputs { + o2::InteractionRecord mInterRecord; ///< Last known interaction record + int mTriggeredTRU; ///< Trigger TRU + std::vector mTriggeredPatches; ///< Trigger patches, in local STU indexing + std::vector> mLastTimesumAllFastOrs; ///< TRU ID, FastOrID with STU indexing, and its last Timesum +}; + +/// @brief Trigger Inputs object, summary of the entire information needed for the L1 algorithm +/// \param mInterRecord Last known interaction record +/// \param mLastTimesumAllPatches Vector of tuples with TRU ID, PatchID, and their last Timesums +struct EMCALTriggerInputsPatch { + o2::InteractionRecord mInterRecord; ///< Last known interaction record + std::vector> mLastTimesumAllPatches; ///< TRU ID, PatchID, and its last Timesum +}; + +/// \class LZEROElectronics +/// \brief Container class for Digits, MC lebels, and trigger records +/// \ingroup EMCALsimulation +/// \author Markus Fasel, ORNL +/// \author Simone Ragoni, Creighton +/// \date 22/11/2022 + +class LZEROElectronics +{ + + public: + /// Default constructor + LZEROElectronics() = default; + + /// Destructor + ~LZEROElectronics() = default; + + /// clear the L0 electronics + void clear(); + + /// Initialize the L0 electronics + void init(); + + /// Sets geometry for trigger mapping + void setGeometry(o2::emcal::Geometry* gm) { mGeometry = gm; } + + /// Sets the masked fastOrs from the CCDB in the LZERO + void setMaskedFastOrs(std::vector const& maskedfastors) { mMaskedFastOrs = maskedfastors; } + void printMaskedFastOrs(); + + /// Set Threshold for LZERO algorithm + /// \param threshold LZERO algorithm threshold + void setThreshold(double threshold) { mThreshold = threshold; } + + /// Implements the peak finder algorithm on the patch + /// \param p TRUElectronics object + /// \param patchID Patch ID to implement the peak finding algorithm + bool peakFinderOnPatch(TRUElectronics& p, unsigned int patchID); + + /// Calls the peak finder algorithm on all patches + /// \param p TRUElectronics object + bool peakFinderOnAllPatches(TRUElectronics& p); + + /// Update patches + /// \param p TRUElectronics object + void updatePatchesADC(TRUElectronics& p); + + /// Add noise to this digit + void addNoiseDigits(Digit& d1); + + /// Implements the fill of the patches. Runs the peak finding, and ships to L1 in case it finds something + /// \param digitlist digits to be assigned to patches + /// \param record interaction record time to be propagated + /// \param patchesFromAllTRUs vector contained the patches of all TRUs + void fill(const std::deque& digitlist, const o2::InteractionRecord record, std::vector& patchesFromAllTRUs); + + /// Getter for the pattern of peaks found by the LZERO algorithm + /// \param p TRUElectronics object + const std::vector& getFiredPatches(TRUElectronics const& p) const + { + return p.mFiredPatches; + } + + // Getter for the threshold used for the integral of the ADC values in the LZERO algorithm + const double getLZEROThreshold() const { return mThreshold; } + + /// Getter for the trigger inputs found by the LZERO algorithm + const std::vector& getTriggerInputs() const + { + LOG(debug) << "DIG TRU getTriggerInputs in LZEROElectronics: size of mTriggers = " << mTriggers.size(); + return mTriggers; + } + + /// Getter for the trigger inputs per patches found by the LZERO algorithm + const std::vector& getTriggerInputsPatches() const + { + return mTriggersPatch; + } + + private: + double mThreshold = 0; + // TRandom3* mRandomGenerator = nullptr; ///< random number generator + // const SimParam* mSimParam = nullptr; ///< SimParam object + std::vector mTriggers; ///< Triggers to be sent out + std::vector mTriggersPatch; ///< Triggers to be sent out + std::vector mMaskedFastOrs; ///< Masked fastOrs from CCDB + bool mSimulateNoiseDigits = true; ///< simulate noise digits + // TriggerMappingV2* mTriggerMap = nullptr; ///< Trigger map to properly assign an absolute FastOr to TRU FastOr + Geometry* mGeometry = nullptr; ///< EMCAL geometry + + ClassDefNV(LZEROElectronics, 2); +}; + +} // namespace emcal + +} // namespace o2 + +#endif /* ALICEO2_EMCAL_LZEROELECTRONICS_H_ */ diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h index 91c534c3ba82b..4388035215d4f 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h @@ -191,6 +191,18 @@ class RawWriter /// The input data is converted to 10 but ALTRO words and put on the stream. std::vector encodeBunchData(const std::vector& data); + /// \brief Extracting branch index from the hardware address + /// \param hwaddress Hardware address of the channel + int getBranchIndexFromHwAddress(int hwaddress) { return ((hwaddress >> 11) & 0x1); } + + /// \brief Extracting FEC index in branch from the hardware address + /// \param hwaddress Hardware address of the channel + int getFecIndexFromHwAddress(int hwaddress) { return ((hwaddress >> 7) & 0xF); } + + /// \brief Extracting Channel index in FEC from the hardware address + /// \param hwaddress Hardware address of the channel + int getChannelIndexFromHwAddress(int hwaddress) { return (hwaddress & 0xF); } + private: int mNADCSamples = 15; ///< Number of time samples int mPedestal = 1; ///< Pedestal diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h index 6a8bd3180bda1..e1cd6728f537d 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h @@ -33,9 +33,6 @@ class SimParam : public o2::conf::ConfigurableParamHelper Int_t getDigitThreshold() const { return mDigitThreshold; } Float_t getPinNoise() const { return mPinNoise; } Float_t getPinNoiseLG() const { return mPinNoiseLG; } - Float_t getTimeNoise() const { return mTimeNoise; } - Float_t getTimeDelay() const { return mTimeDelay; } - Bool_t isTimeDelayFromOCDB() const { return mTimeDelayFromOCDB; } Float_t getTimeResolutionPar0() const { return mTimeResolutionPar0; } Float_t getTimeResolutionPar1() const { return mTimeResolutionPar1; } Double_t getTimeResolution(Double_t energy) const; @@ -47,6 +44,15 @@ class SimParam : public o2::conf::ConfigurableParamHelper Float_t getTimeResponseThreshold() const { return mTimeResponseThreshold; } Int_t getBCPhaseSwap() const { return mSwapPhase; } + // Parameters used in TRU Digitizer + Float_t getPinNoiseTRU() const { return mPinNoiseTRU; } + Float_t getTimeResponseTauTRU() const { return mTimeResponseTauTRU; } + Float_t getTimeResponsePowerTRU() const { return mTimeResponsePowerTRU; } + Float_t getTimeResponseNormalisationTRU() const { return mTimeResponseNormalisationTRU; } + + // Parameters used in LZERO + Float_t getThresholdLZERO() const { return mThresholdLZERO; } + // Parameters used in SDigitizer Float_t getA() const { return mA; } Float_t getB() const { return mB; } @@ -74,9 +80,6 @@ class SimParam : public o2::conf::ConfigurableParamHelper Float_t mGainFluctuations{15.}; ///< correct fMeanPhotonElectron by the gain fluctuations Float_t mPinNoise{0.012}; ///< Electronics noise in EMC, APD Float_t mPinNoiseLG{0.1}; ///< Electronics noise in EMC, APD, Low Gain - Float_t mTimeNoise{1.28e-5}; ///< Electronics noise in EMC, time - Float_t mTimeDelay{600e-9}; ///< Simple time delay to mimick roughly delay in data - Bool_t mTimeDelayFromOCDB{false}; ///< Get time delay from OCDB Float_t mTimeResolutionPar0{0.26666}; ///< Time resolution of FEE electronics Float_t mTimeResolutionPar1{1.4586}; ///< Time resolution of FEE electronics Int_t mNADCEC{0x10000}; ///< number of channels in EC section ADC @@ -85,6 +88,15 @@ class SimParam : public o2::conf::ConfigurableParamHelper Float_t mTimeResponseThreshold{0.001}; ///< Raw time response function energy threshold Int_t mSwapPhase{0}; ///< BC phase swap similar to data + // TRU Digitizer + Float_t mPinNoiseTRU{0.04}; ///< Electronics noise in EMC, TRU, normalised by fastOr + Float_t mTimeResponseTauTRU{61.45 / 25.}; ///< Raw time response function tau parameter TRU from Martin Poghosyan + Float_t mTimeResponsePowerTRU{2.}; ///< Raw time response function power parameter TRU from Martin Poghosyan + Float_t mTimeResponseNormalisationTRU{(5. / 3.93) * (45. / 39.25)}; ///< Raw time response function normalisation parameter TRU from correlation studies + + // LZERO peak finding + Float_t mThresholdLZERO{132.}; ///< ADC threshold for peak finding in the LZEROElectronics + // SDigitizer Float_t mA{0.}; ///< Pedestal parameter Float_t mB{1.e+6}; ///< Slope Digitizition parameters diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/TRUElectronics.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/TRUElectronics.h new file mode 100644 index 0000000000000..8d4a14cfee9bb --- /dev/null +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/TRUElectronics.h @@ -0,0 +1,129 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TRUElectronics.h +/// \brief EMCAL TRUElectronics for the LZEROElectronics +#ifndef ALICEO2_EMCAL_PATCH_H +#define ALICEO2_EMCAL_PATCH_H + +#include +#include +#include +#include "Rtypes.h" +// #include "DataFormatsEMCAL/Cluster.h" +// #include "DataFormatsEMCAL/Digit.h" +// #include "DataFormatsEMCAL/Cell.h" +// #include "EMCALBase/Geometry.h" + +// #include // for LOG + +namespace o2 +{ + +namespace emcal +{ + +/// \param mADCvalues vector of the ADC values +/// \param previousTimebinADCvalue previous ADC value, popped out (i-4) if i refers to the current Timebin +struct FastOrStruct { + std::vector mADCvalues; + double mPreviousTimebinADCvalue = 0.; + + /// Compute current timesum + double timesum() + { + double timesumvalue = 0; + for (auto ADCvalue : mADCvalues) { + timesumvalue += ADCvalue; + } + return timesumvalue; + } + + void init() + { + } + + // Update internal ADC values (4 timebins) + void updateADC(double ADCvalue) + { + + if (mADCvalues.size() == 4) { + mPreviousTimebinADCvalue = mADCvalues.front(); + mADCvalues.erase(mADCvalues.begin()); + } + mADCvalues.push_back(ADCvalue); + } +}; + +/// \struct TRUElectronics +/// \brief TRUElectronics creator, based on the TRUElectronics +/// \ingroup EMCALsimulation +/// \author Markus Fasel, ORNL +/// \author Simone Ragoni, Creighton U. +/// \date 03/12/2022 +/// + +struct TRUElectronics { + + /// \brief Main constructor + /// \param patchSize patch size: 2x2, or 4x4 + /// \param whichSide 0 = A side, 1 = C side + /// \param whichSuperModuleSize 0 = Full 1 = 1/3 + TRUElectronics(int patchSize, int whichSide, int whichSuperModuleSize); + + /// \brief Default constructor + TRUElectronics(); + + /// \brief Destructor + ~TRUElectronics() = default; + + /// \brief Clear internal members + void clear(); + + /// \brief Initialise internal members + void init(); + + /// \brief Assign seed module to a Full SM + /// \param patchID Patch ID that need to be assigned a seed + void assignSeedModuleToPatchWithSTUIndexingFullModule(int& patchID); + + /// \brief Assign seed module to a 1/3 SM + /// \param patchID Patch ID that need to be assigned a seed + void assignSeedModuleToPatchWithSTUIndexingOneThirdModule(int& patchID); + + /// \brief Assign seed module to all patches + void assignSeedModuleToAllPatches(); + + /// \brief Assign modules to all patches + void assignModulesToAllPatches(); + + /// \brief Updates the patches + void updateADC(); + + int mPatchSize; //!> mPatchIDSeedFastOrIDs; //!>> mIndexMapPatch; //!>> mFiredFastOrIndexMapPatch; //! mFiredPatches; //!>> mADCvalues; //!>> mTimesum; //!> mPreviousTimebinADCvalue; //! mFastOrs; //! // for LOG #include "CommonDataFormat/InteractionRecord.h" #include "CommonUtils/TreeStreamRedirector.h" +#include "SimConfig/DigiParams.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DetectorsRaw/HBFUtils.h" ClassImp(o2::emcal::Digitizer); @@ -37,39 +40,50 @@ using namespace o2::emcal; void Digitizer::init() { mSimParam = &(o2::emcal::SimParam::Instance()); - mRandomGenerator = new TRandom3(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + auto randomSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + if (o2::conf::DigiParams::Instance().seed != 0) { + randomSeed = o2::conf::DigiParams::Instance().seed; + } + mRandomGenerator = new TRandom3(randomSeed); float tau = mSimParam->getTimeResponseTau(); float N = mSimParam->getTimeResponsePower(); - float delay = std::fmod(mSimParam->getSignalDelay() / constants::EMCAL_TIMESAMPLE, 1); - mDelay = ((int)(std::floor(mSimParam->getSignalDelay() / constants::EMCAL_TIMESAMPLE))); - mTimeWindowStart = ((unsigned int)(std::floor(mSimParam->getTimeBinOffset() / constants::EMCAL_TIMESAMPLE))); mSmearEnergy = mSimParam->doSmearEnergy(); mSimulateTimeResponse = mSimParam->doSimulateTimeResponse(); mDigits.init(); - if ((mDelay - mTimeWindowStart) != 0) { + mDigits.reserve(); + /* + if ((mDelay - mTimeWindowStart) != 0) + { mDigits.setBufferSize(mDigits.getBufferSize() + (mDelay - mTimeWindowStart)); mDigits.reserve(); } + */ - mAmplitudeInTimeBins.clear(); - - // for each phase create a template distribution - TF1 RawResponse("RawResponse", rawResponseFunction, 0, 256, 5); - RawResponse.SetParameters(1., 0., tau, N, 0.); - - for (int i = 0; i < 4; i++) { - - std::vector sf; - RawResponse.SetParameter(1, 0.25 * i); - for (int j = 0; j < constants::EMCAL_MAXTIMEBINS; j++) { - sf.push_back(RawResponse.Eval(j - mTimeWindowStart)); + if (mSimulateTimeResponse) { + // for each phase create a template distribution + TF1 RawResponse("RawResponse", rawResponseFunction, 0, 256, 5); + RawResponse.SetParameters(1., 0., tau, N, 0.); + + for (int phase = 0; phase < 4; phase++) { + // parameter 1: Handling phase + delay + // phase: 25 ns * phase index (-4) + // delay: Average signal delay + for (int itofbin = 0; itofbin < EMC_TOF_BINS; itofbin++) { + double tofbincenter = itofbin * EMC_TOF_BINWITH + 0.5 * EMC_TOF_BINWITH; + RawResponse.SetParameter(1, 0.25 * phase + (tofbincenter + mSimParam->getSignalDelay()) / constants::EMCAL_TIMESAMPLE); + for (int sample = 0; sample < constants::EMCAL_MAXTIMEBINS; sample++) { + mAmplitudeInTimeBins[phase][itofbin][sample] = RawResponse.Eval(sample); + } + } } - mAmplitudeInTimeBins.push_back(sf); + } else { } + mIRFirstSampledTF = o2::raw::HBFUtils::Instance().getFirstSampledTFIR(); + if (mEnableDebugStreaming) { mDebugStream = std::make_unique("emcaldigitsDebug.root", "RECREATE"); } @@ -154,18 +168,39 @@ void Digitizer::sampleSDigit(const Digit& sDigit) return; } + // check if this hit because it comes from an event before readout starts and it does not effect this RO + LOG(debug) << "mIsBeforeFirstRO " << mIsBeforeFirstRO << " sDigit.getTimeStamp() " << sDigit.getTimeStamp() << " mSimParam->getSignalDelay() " << mSimParam->getSignalDelay() << " mPhase " << mPhase << " total: " << sDigit.getTimeStamp() + mSimParam->getSignalDelay() + mPhase * 25 << " EMC_TOF_MAX " << EMC_TOF_MAX << " mTimeBCns " << mTimeBCns; + if (mIsBeforeFirstRO && sDigit.getTimeStamp() + mTimeBCns < 0) { + LOG(debug) << "disregard this hit because it comes from an event before readout starts and it does not effect this RO"; + return; + } + Double_t energies[15]; if (mSimulateTimeResponse) { - for (int j = 0; j < mAmplitudeInTimeBins.at(mPhase).size(); j++) { + if (sDigit.getTimeStamp() + mSimParam->getSignalDelay() + mPhase * 25 > EMC_TOF_MAX) { + // Digit time larger than sampling window, will not be sampled + // For time response simulation take also signal delay and phase into account + return; + } + int tofbin = static_cast(sDigit.getTimeStamp() / EMC_TOF_BINWITH); + if (tofbin >= EMC_TOF_BINS) { + tofbin = EMC_TOF_BINS - 1; + } + for (int sample = 0; sample < mAmplitudeInTimeBins[mPhase][tofbin].size(); sample++) { - double val = energy * (mAmplitudeInTimeBins.at(mPhase).at(j)); - energies[j] = val; - double digitTime = (mEventTimeOffset + mDelay - mTimeWindowStart) * constants::EMCAL_TIMESAMPLE; + double val = energy * (mAmplitudeInTimeBins[mPhase][tofbin][sample]); + energies[sample] = val; + double digitTime = mEventTimeOffset * constants::EMCAL_TIMESAMPLE; Digit digit(tower, val, digitTime); mTempDigitVector.push_back(digit); } } else { - Digit digit(tower, energy, (mDelay - mTimeWindowStart) * constants::EMCAL_TIMESAMPLE); + if (sDigit.getTimeStamp() > EMC_TOF_MAX) { + // Digit time larger than sampling window, will not be sampled + // In non-sampled mode only apply the max. time window + return; + } + Digit digit(tower, energy, smearTime(sDigit.getTimeStamp(), energy)); mTempDigitVector.push_back(digit); } @@ -203,11 +238,16 @@ double Digitizer::smearEnergy(double energy) return energy; } +double Digitizer::smearTime(double time, double energy) +{ + return mRandomGenerator->Gaus(time + mSimParam->getSignalDelay(), mSimParam->getTimeResolution(energy)); +} + //_______________________________________________________________________ -void Digitizer::setEventTime(o2::InteractionTimeRecord record) +void Digitizer::setEventTime(o2::InteractionTimeRecord record, bool trigger) { - mDigits.forwardMarker(record); + mDigits.forwardMarker(record, trigger); mPhase = mSimParam->doSimulateL1Phase() ? mDigits.getPhase() : 0; @@ -217,4 +257,16 @@ void Digitizer::setEventTime(o2::InteractionTimeRecord record) mPhase = 0; mEventTimeOffset++; } + + // get time difference between current bc and start of RO in ns + auto nbc = record.differenceInBC(mIRFirstSampledTF); + mTimeBCns = record.getTimeOffsetWrtBC(); + mTimeBCns += nbc * o2::constants::lhc::LHCBunchSpacingNS; + + if (nbc < 0) { + // this event is before the first RO + mIsBeforeFirstRO = true; + } else { + mIsBeforeFirstRO = false; + } } \ No newline at end of file diff --git a/Detectors/EMCAL/simulation/src/DigitizerTRU.cxx b/Detectors/EMCAL/simulation/src/DigitizerTRU.cxx new file mode 100644 index 0000000000000..7f0da790eff8c --- /dev/null +++ b/Detectors/EMCAL/simulation/src/DigitizerTRU.cxx @@ -0,0 +1,514 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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/DigitizerTRU.h" +#include "EMCALSimulation/SimParam.h" +#include "EMCALSimulation/DigitsWriteoutBuffer.h" +#include "EMCALCalib/FeeDCS.h" +#include "DataFormatsEMCAL/Digit.h" +#include "EMCALBase/Hit.h" +#include "MathUtils/Cartesian.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "EMCALSimulation/DigitsWriteoutBuffer.h" +#include +#include +#include +#include +#include +#include // for LOG +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" + +ClassImp(o2::emcal::DigitizerTRU); + +using o2::emcal::Digit; +using o2::emcal::Hit; + +using namespace o2::emcal; + +//_______________________________________________________________________ +void DigitizerTRU::init() +{ + setPatches(); + auto mSimParam = &(o2::emcal::SimParam::Instance()); + // mRandomGenerator = new TRandom3(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + + float tau = mSimParam->getTimeResponseTauTRU(); + float N = mSimParam->getTimeResponsePowerTRU(); + + mSmearEnergy = mSimParam->doSmearEnergy(); + mSimulateTimeResponse = mSimParam->doSimulateTimeResponse(); + + mDigits.init(); + mDigits.reserve(15); + + LZERO.setGeometry(mGeometry); + LZERO.init(); + + // Parameters from data (@Martin Poghosyan) + // tau = 61.45 / 25.; // 61.45 ns, according to the fact that the + // N = 2.; + + if (mSimulateTimeResponse) { + // for each phase create a template distribution + TF1 RawResponse("RawResponse", rawResponseFunction, 0, 256, 5); + RawResponse.SetParameters(1., 0., tau, N, 0.); + RawResponse.SetParameter(1, 425. / o2::emcal::constants::EMCAL_TIMESAMPLE); + + // parameter 1: Handling phase + delay + // phase: 25 ns * phase index (-4) + // delay: Average signal delay + RawResponse.SetParameter(1, mSimParam->getSignalDelay() / constants::EMCAL_TIMESAMPLE); + for (int sample = 0; sample < constants::EMCAL_MAXTIMEBINS; sample++) { + mAmplitudeInTimeBins[sample] = RawResponse.Eval(sample); + LOG(info) << "DIG TRU init in DigitizerTRU: amplitudes[" << sample << "] = " << mAmplitudeInTimeBins[sample]; + } + + // Rescale the TRU time response function + // so that the sum of the samples at [5], [6], [7] and [8] + // is equal to 1 + auto maxElement = std::max_element(mAmplitudeInTimeBins.begin(), mAmplitudeInTimeBins.end()); + double rescalingFactor = *(maxElement - 1) + *maxElement + *(maxElement + 1) + *(maxElement + 2); + double normalisationTRU = mSimParam->getTimeResponseNormalisationTRU(); + rescalingFactor /= normalisationTRU; + // rescalingFactor /= (5. / 3.93); // the slope seen in the single fastOr correlation + // rescalingFactor /= (45. / 39.25); // the slope seen in the single fastOr correlation + for (int sample = 0; sample < constants::EMCAL_MAXTIMEBINS; sample++) { + mAmplitudeInTimeBins[sample] /= rescalingFactor; + LOG(info) << "DIG TRU init in DigitizerTRU after RESCALING: amplitudes[" << sample << "] = " << mAmplitudeInTimeBins[sample]; + } + } else { + } + + if (mEnableDebugStreaming) { + mDebugStream = std::make_unique("emcaldigitsDebugTRU.root", "RECREATE"); + // mDebugStreamPatch = std::make_unique("emcaldigitsDebugPatchTRU.root", "RECREATE"); + } +} +//_______________________________________________________________________ +double DigitizerTRU::rawResponseFunction(double* x, double* par) +{ + double signal = 0.; + double tau = par[2]; + double n = par[3]; + double ped = par[4]; + double xx = (x[0] - par[1] + tau) / tau; + + // par[0] amp, par[1] peak time + + if (xx <= 0) { + signal = ped; + } else { + signal = ped + par[0] * std::pow(xx, n) * std::exp(n * (1 - xx)); + } + + return signal; +} +//_______________________________________________________________________ +void DigitizerTRU::clear() +{ + mDigits.clear(); +} +//_______________________________________________________________________ +void DigitizerTRU::process(const gsl::span summableDigits) +{ + + auto processedSDigits = makeAnaloguesFastorSums(summableDigits); + + for (auto vectorelement : processedSDigits) { + + int& fastorID = std::get<0>(vectorelement); + auto& digit = std::get<1>(vectorelement); + + int tower = digit.getTower(); + + sampleSDigit(digit); + + if (mTempDigitVector.size() == 0) { + continue; + } + + mDigits.addDigits(fastorID, mTempDigitVector); + } +} +//_______________________________________________________________________ +std::vector> DigitizerTRU::makeAnaloguesFastorSums(const gsl::span sdigits) +{ + TriggerMappingV2 mTriggerMap(mGeometry); + std::unordered_map sdigitsFastOR; + std::vector fastorIndicesFound; + for (const auto& dig : sdigits) { + o2::emcal::TriggerMappingV2::IndexCell towerid = dig.getTower(); + int fastorIndex = mTriggerMap.getAbsFastORIndexFromCellIndex(towerid); + auto whichTRU = std::get<0>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + auto whichFastOrTRU = std::get<1>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + auto found = sdigitsFastOR.find(fastorIndex); + if (found != sdigitsFastOR.end()) { + // sum energy + Digit digitToSum((found->second).getTower(), dig.getAmplitude(), (found->second).getTimeStamp()); + (found->second) += digitToSum; + } else { + // create new digit + fastorIndicesFound.emplace_back(fastorIndex); + sdigitsFastOR.emplace(fastorIndex, dig); + } + } + // sort digits for output + std::sort(fastorIndicesFound.begin(), fastorIndicesFound.end(), std::less<>()); + + for (auto& elem : sdigitsFastOR) { + auto dig = elem.second; + int fastorIndex = elem.first; + auto whichTRU = std::get<0>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + auto whichFastOrTRU = std::get<1>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + } + + // Setting them to be TRU digits + for (auto& elem : sdigitsFastOR) { + (elem.second).setTRU(); + } + + for (auto& elem : sdigitsFastOR) { + auto dig = elem.second; + int fastorIndex = elem.first; + auto whichTRU = std::get<0>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + auto whichFastOrTRU = std::get<1>(mTriggerMap.getTRUFromAbsFastORIndex(fastorIndex)); + } + + std::vector> outputFastorSDigits; + std::for_each(fastorIndicesFound.begin(), fastorIndicesFound.end(), [&outputFastorSDigits, &sdigitsFastOR](int fastorIndex) { outputFastorSDigits.emplace_back(fastorIndex, sdigitsFastOR[fastorIndex]); }); + return outputFastorSDigits; +} + +//_______________________________________________________________________ +void DigitizerTRU::sampleSDigit(const Digit& sDigit) +{ + mTempDigitVector.clear(); + Int_t tower = sDigit.getTower(); + Double_t energy = sDigit.getAmplitude(); + + if (mSmearEnergy) { + energy = smearEnergy(energy); + } + + if (energy < __DBL_EPSILON__) { + return; + } + + Double_t energies[15]; + if (mSimulateTimeResponse) { + for (int sample = 0; sample < mAmplitudeInTimeBins.size(); sample++) { + + double val = energy * (mAmplitudeInTimeBins[sample]); + energies[sample] = val; + double digitTime = mEventTimeOffset * constants::EMCAL_TIMESAMPLE; + Digit digit(tower, val, digitTime); + digit.setTRU(); + mTempDigitVector.push_back(digit); + } + + } else { + Digit digit(tower, energy, smearTime(sDigit.getTimeStamp(), energy)); + digit.setTRU(); + mTempDigitVector.push_back(digit); + } + + if (mEnableDebugStreaming) { + double timeStamp = sDigit.getTimeStamp(); + (*mDebugStream).GetFile()->cd(); + (*mDebugStream) << "DigitsTimeSamples" + << "Tower=" << tower + << "Time=" << timeStamp + << "DigitEnergy=" << energy + << "Sample0=" << energies[0] + << "Sample1=" << energies[1] + << "Sample2=" << energies[2] + << "Sample3=" << energies[3] + << "Sample4=" << energies[4] + << "Sample5=" << energies[5] + << "Sample6=" << energies[6] + << "Sample7=" << energies[7] + << "Sample8=" << energies[8] + << "Sample9=" << energies[9] + << "Sample10=" << energies[10] + << "Sample11=" << energies[11] + << "Sample12=" << energies[12] + << "Sample13=" << energies[13] + << "Sample14=" << energies[14] + << "\n"; + } +} + +//_______________________________________________________________________ +double DigitizerTRU::smearEnergy(double energy) +{ + auto mSimParam = &(o2::emcal::SimParam::Instance()); + Double_t fluct = (energy * mSimParam->getMeanPhotonElectron()) / mSimParam->getGainFluctuations(); + TRandom3 mRandomGenerator(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + energy *= mRandomGenerator.Poisson(fluct) / fluct; + return energy; +} +//_______________________________________________________________________ +double DigitizerTRU::smearTime(double time, double energy) +{ + auto mSimParam = &(o2::emcal::SimParam::Instance()); + TRandom3 mRandomGenerator(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + return mRandomGenerator.Gaus(time + mSimParam->getSignalDelay(), mSimParam->getTimeResolution(energy)); +} + +//_______________________________________________________________________ +void DigitizerTRU::setEventTime(o2::InteractionTimeRecord record) +{ + + // For the digitisation logic, at this time you would do: + // mDigits.forwardMarker(record); + // In the case of the trigger simulation, what is needed is to + // fill the corresponding LZEROElectronics object and + // launch the peak finder. + // If a trigger is found the logic is set to be live. + mDigits.fillOutputContainer(false, record, patchesFromAllTRUs, LZERO); + mEventTimeOffset = 0; + + if (mEnableDebugStreaming) { + auto TriggerInputsAll = LZERO.getTriggerInputs(); + auto TriggerInputsPatchesAll = LZERO.getTriggerInputsPatches(); + + std::vector TriggerInputs; + if (TriggerInputsAll.size() != mPreviousTriggerSize) { + mWasTriggerFound = true; + mPreviousTriggerSize = TriggerInputsAll.size(); + } else { + mWasTriggerFound = false; + } + if (TriggerInputsAll.size() > 0 && mWasTriggerFound == true) { + TriggerInputs.push_back(TriggerInputsAll.back()); + } + std::vector TriggerInputsPatches; + if (TriggerInputsPatchesAll.size() > 0 && mWasTriggerFound == true) { + TriggerInputsPatches.push_back(TriggerInputsPatchesAll.back()); + } + int nIter = TriggerInputs.size(); + + if (nIter != 0) { + for (auto& trigger : TriggerInputs) { + auto InteractionRecordData = trigger.mInterRecord; + auto bc = InteractionRecordData.bc; + auto orbit = InteractionRecordData.orbit; + for (auto& fastor : trigger.mLastTimesumAllFastOrs) { + auto WhichTRU = std::get<0>(fastor); + auto WhichFastOr = std::get<1>(fastor); + auto FastOrAmp = std::get<2>(fastor); + (*mDebugStream).GetFile()->cd(); + (*mDebugStream) << "L0Timesums" + << "bc=" << bc + << "orbit=" << orbit + << "WhichTRU=" << WhichTRU + << "WhichFastOr=" << WhichFastOr + << "FastOrAmp=" << FastOrAmp + << "\n"; + } + } + // for (auto& trigger : TriggerInputsPatches) { + // auto lastTimeSum = trigger.mLastTimesumAllPatches.end() - 1; + // for (auto& patches : trigger.mLastTimesumAllPatches) { + // auto WhichTRU = std::get<0>(patches); + // auto WhichPatch = std::get<1>(patches); + // auto PatchTimesum = std::get<2>(patches); + // auto isFired = std::get<3>(patches); + // (*mDebugStreamPatch).GetFile()->cd(); + // (*mDebugStreamPatch) << "L0TimesumsPatch" + // << "WhichTRU=" << WhichTRU + // << "WhichPatch=" << WhichPatch + // << "PatchTimesum=" << PatchTimesum + // << "isFired=" << isFired + // << "\n"; + // } + // } + } + } +} +//_______________________________________________________________________ +void DigitizerTRU::setPatches() +{ + // Using Run 2 geometry, found in: + // https://www.dropbox.com/s/pussj0olcctroim/PHOS+DCAL_STU.pdf?dl=0 + // TRUs[0+6n, 1+6n, 2+6n] are on the A-side + // TRUs[3+6n, 4+6n, 5+6n] are on the C-side + + patchesFromAllTRUs.clear(); + // patchesFromAllTRUs.resize(); + TRUElectronics FullAside(2, 0, 0); + TRUElectronics FullCside(2, 1, 0); + TRUElectronics ThirdAside(2, 0, 1); + TRUElectronics ThirdCside(2, 1, 1); + FullAside.init(); + FullCside.init(); + ThirdAside.init(); + ThirdCside.init(); + + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 0,1,2 EMCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 3,4,5 EMCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 6,7,8 EMCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 9,10,11 EMCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 12,13,14 EMCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 15,16,17 EMCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 18,19,20 EMCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 21,22,23 EMCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 24,25,26 EMCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 27,28,29 EMCAL C-side, Full + } + patchesFromAllTRUs.push_back(ThirdAside); // TRU ID 30 EMCAL A-side, Third + patchesFromAllTRUs.push_back(ThirdCside); // TRU ID 31 EMCAL C-side, Third + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 32,33,34 DCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 35,36,37 DCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 38,39,40 DCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 41,42,43 DCAL C-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullAside); // TRU ID 44,45,46 DCAL A-side, Full + } + for (int j = 0; j < 3; j++) { + patchesFromAllTRUs.push_back(FullCside); // TRU ID 47,48,49 DCAL C-side, Full + } + patchesFromAllTRUs.push_back(ThirdAside); // TRU ID 50 DCAL A-side, Third + patchesFromAllTRUs.push_back(ThirdCside); // TRU ID 51 DCAL C-side, Third + + while (patchesFromAllTRUs[30].mPatchIDSeedFastOrIDs.size() > 69) { + patchesFromAllTRUs[30].mPatchIDSeedFastOrIDs.pop_back(); + patchesFromAllTRUs[30].mIndexMapPatch.pop_back(); + } + while (patchesFromAllTRUs[31].mPatchIDSeedFastOrIDs.size() > 69) { + patchesFromAllTRUs[31].mPatchIDSeedFastOrIDs.pop_back(); + patchesFromAllTRUs[31].mIndexMapPatch.pop_back(); + } + while (patchesFromAllTRUs[50].mPatchIDSeedFastOrIDs.size() > 69) { + patchesFromAllTRUs[50].mPatchIDSeedFastOrIDs.pop_back(); + patchesFromAllTRUs[50].mIndexMapPatch.pop_back(); + } + while (patchesFromAllTRUs[51].mPatchIDSeedFastOrIDs.size() > 69) { + patchesFromAllTRUs[51].mPatchIDSeedFastOrIDs.pop_back(); + patchesFromAllTRUs[51].mIndexMapPatch.pop_back(); + } +} +//______________________________________________________________________ +void DigitizerTRU::finish() +{ + mDigits.finish(); + if (isDebugMode() == true) { + endDebugStream(); + } +} +//______________________________________________________________________ +int DigitizerTRU::GetTRUIndexFromSTUIndex(Int_t id, Int_t detector) +{ + Int_t kEMCAL = 0; + Int_t kDCAL = 1; + + if ((id > 31 && detector == kEMCAL) || (id > 13 && detector == kDCAL) || id < 0) { + return -1; // Error Condition + } + + if (detector == kEMCAL) { + return id; + } else if (detector == kDCAL) { + return 32 + ((int)(id / 4) * 6) + ((id % 4 < 2) ? (id % 4) : (id % 4 + 2)); + } + return -1; +} +//______________________________________________________________________ +int DigitizerTRU::GetChannelForMaskRun2(int mask, int bitnumber, bool onethirdsm) +{ + if (onethirdsm) { + return mask * 16 + bitnumber; + } + const int kChannelMap[6][16] = {{8, 9, 10, 11, 20, 21, 22, 23, 32, 33, 34, 35, 44, 45, 46, 47}, // Channels in mask0 + {56, 57, 58, 59, 68, 69, 70, 71, 80, 81, 82, 83, 92, 93, 94, 95}, // Channels in mask1 + {4, 5, 6, 7, 16, 17, 18, 19, 28, 29, 30, 31, 40, 41, 42, 43}, // Channels in mask2 + {52, 53, 54, 55, 64, 65, 66, 67, 76, 77, 78, 79, 88, 89, 90, 91}, // Channels in mask3 + {0, 1, 2, 3, 12, 13, 14, 15, 24, 25, 26, 27, 36, 37, 38, 39}, // Channels in mask4 + {48, 49, 50, 51, 60, 61, 62, 63, 72, 73, 74, 75, 84, 85, 86, 87}}; // Channels in mask5 + return kChannelMap[mask][bitnumber]; +} +//______________________________________________________________________ +std::vector DigitizerTRU::GetAbsFastORIndexFromMask() +{ + TriggerMappingV2 mTriggerMap(mGeometry); + std::vector maskedfastors; + int itru = 0; + for (Int_t i = 0; i < 46; i++) { + int localtru = itru % 32, detector = itru >= 32 ? 1 : 0, + globaltru = GetTRUIndexFromSTUIndex(localtru, detector); + bool onethirdsm = ((globaltru >= 30 && globaltru < 32) || (globaltru >= 50 && globaltru < 52)); + for (int ipos = 0; ipos < 6; ipos++) { + auto regmask = mFeeDCS->getTRUDCS(i).getMaskReg(ipos); + std::bitset<16> bitsregmask(regmask); + for (int ibit = 0; ibit < 16; ibit++) { + if (bitsregmask.test(ibit)) { + auto channel = GetChannelForMaskRun2(ipos, ibit, onethirdsm); + int absfastor = mTriggerMap.getAbsFastORIndexFromIndexInTRU(globaltru, channel); + maskedfastors.push_back(absfastor); + } + } + } + itru++; + } + return maskedfastors; +} +//______________________________________________________________________ +void DigitizerTRU::setMaskedFastOrsInLZERO() +{ + auto maskedFastOrs = GetAbsFastORIndexFromMask(); + LOG(info) << "======================================"; + LOG(info) << "== PRINT MASK COMPUTED IN DIGITIZER =="; + int counter = 0; + for (auto fastOr : maskedFastOrs) { + LOG(info) << "fastOr masked (number, ID) = (" << counter << ", " << fastOr; + counter += 1; + } + LZERO.setMaskedFastOrs(maskedFastOrs); +} +//______________________________________________________________________ +void DigitizerTRU::printMaskedFastOrsInLZERO() +{ + LZERO.printMaskedFastOrs(); +} diff --git a/Detectors/EMCAL/simulation/src/DigitsVectorStream.cxx b/Detectors/EMCAL/simulation/src/DigitsVectorStream.cxx index 56b9dee2ebd19..f0ed5d6d9d68a 100644 --- a/Detectors/EMCAL/simulation/src/DigitsVectorStream.cxx +++ b/Detectors/EMCAL/simulation/src/DigitsVectorStream.cxx @@ -19,14 +19,18 @@ #include "EMCALSimulation/LabeledDigit.h" #include "EMCALSimulation/DigitsVectorStream.h" #include "CommonConstants/Triggers.h" +#include "SimConfig/DigiParams.h" using namespace o2::emcal; void DigitsVectorStream::init() { mSimParam = &(o2::emcal::SimParam::Instance()); - - mRandomGenerator = new TRandom3(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + auto randomSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + if (o2::conf::DigiParams::Instance().seed != 0) { + randomSeed = o2::conf::DigiParams::Instance().seed; + } + mRandomGenerator = new TRandom3(randomSeed); mRemoveDigitsBelowThreshold = mSimParam->doRemoveDigitsBelowThreshold(); mSimulateNoiseDigits = mSimParam->doSimulateNoiseDigits(); @@ -47,7 +51,6 @@ void DigitsVectorStream::addNoiseDigits(LabeledDigit& d1) d1 += d; } - //_______________________________________________________________________ void DigitsVectorStream::fill(std::deque& digitlist, o2::InteractionRecord record) { @@ -121,7 +124,7 @@ void DigitsVectorStream::fill(std::deque& digitlist, o2 mTriggerRecords.emplace_back(record, o2::trigger::PhT, mStartIndex, numberOfNewDigits); mStartIndex = mDigits.size(); - LOG(info) << "Have " << mStartIndex << " digits "; + LOG(info) << "Trigger Orbit " << record.orbit << ", BC " << record.bc << ": have " << numberOfNewDigits << " digits (" << mStartIndex << " total)"; } //_______________________________________________________________________ diff --git a/Detectors/EMCAL/simulation/src/DigitsWriteoutBuffer.cxx b/Detectors/EMCAL/simulation/src/DigitsWriteoutBuffer.cxx index 27bf9553c0499..c5c17f4c41d57 100644 --- a/Detectors/EMCAL/simulation/src/DigitsWriteoutBuffer.cxx +++ b/Detectors/EMCAL/simulation/src/DigitsWriteoutBuffer.cxx @@ -19,6 +19,7 @@ #include "EMCALSimulation/DigitsWriteoutBuffer.h" #include "CommonDataFormat/InteractionRecord.h" #include "TMath.h" +#include using namespace o2::emcal; @@ -80,7 +81,7 @@ void DigitsWriteoutBuffer::addDigits(unsigned int towerID, std::vector= (mLiveTime + mBusyTime) || mFirstEvent) { + if (trigger && ((eventTime - mTriggerTime) >= (mLiveTime + mBusyTime) || mFirstEvent)) { + LOG(debug) << "Trigger received and accepted"; mTriggerTime = eventTime; mTimedDigitsFuture.front().mTriggerColl = true; mTimedDigitsFuture.front().mInterRecord = record; @@ -142,8 +144,8 @@ void DigitsWriteoutBuffer::forwardMarker(o2::InteractionTimeRecord record) } // If we have a pre-trigger collision, all the time bins in the future buffer will be set to record mode - if ((eventTime - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime)) { - std::cout << "Pre-trigger collision\n"; + if ((eventTime - mTriggerTime) >= (mLiveTime + mBusyTime - mPreTriggerTime) || mFirstEvent) { + LOG(debug) << "Pre-trigger collision"; long timeStamp = (eventTime / 100) * 100; /// This is to make the event time multiple of 100s for (auto& iNode : mTimedDigitsFuture) { long diff = (timeStamp - eventTime); diff --git a/Detectors/EMCAL/simulation/src/DigitsWriteoutBufferTRU.cxx b/Detectors/EMCAL/simulation/src/DigitsWriteoutBufferTRU.cxx new file mode 100644 index 0000000000000..8b642b2c767eb --- /dev/null +++ b/Detectors/EMCAL/simulation/src/DigitsWriteoutBufferTRU.cxx @@ -0,0 +1,159 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include "EMCALSimulation/DigitsWriteoutBufferTRU.h" +#include "EMCALSimulation/LZEROElectronics.h" +#include "CommonDataFormat/InteractionRecord.h" +#include // for LOG +#include "EMCALBase/TriggerMappingV2.h" + +using namespace o2::emcal; + +//_____________________________________________________________________ +// +void DigitsWriteoutBufferTRU::fillOutputContainer(bool isEndOfTimeFrame, InteractionRecord& nextInteractionRecord, std::vector& patchesFromAllTRUs, LZEROElectronics& LZERO) +{ + int eventTimeBin = 13; + bool needsEmptyTimeBins = false; + int nProcessedTimeBins = 0; + + std::deque + mDequeTime; + + // If end of run or end of timeframe read out what we have + bool isEnd = mEndOfRun || isEndOfTimeFrame; + // Checking the Interaction Record for the time of the next event in BC units + // Difference becomes the new marker if the collision happens before 13 samples + auto difference = nextInteractionRecord.toLong() - mCurrentInteractionRecord.toLong(); + if (mNoPileupMode || difference >= 13 || isEnd) { + // Next collision happening way after the current one, just + // send out and clear entire buffer + // Simplification and optimization at software level - hardware would continuosly sample, but here we don't need to allocate memory dynamically which we are never going to use + // Also in a dedicated no-pileup mode we always write out after each collision. + for (auto& time : mTimeBins) { + mDequeTime.push_back(time); + } + LZERO.fill(mDequeTime, mCurrentInteractionRecord, patchesFromAllTRUs); + auto TriggerInputs = LZERO.getTriggerInputs(); + auto TriggerInputsPatches = LZERO.getTriggerInputsPatches(); + int nIter = TriggerInputs.size(); + LOG(debug) << "DIG TRU fillOutputContainer in DigitsWriteoutBufferTRU: size of TriggerInputs = " << nIter; + // for (auto& patches : patchesFromAllTRUs) { + // LZERO.updatePatchesADC(patches); + // LZERO.peakFinderOnAllPatches(patches); + // } + LOG(debug) << "DIG TRU fillOutputContainer in DigitsWriteoutBufferTRU: LOW IR"; + + mCurrentInteractionRecord = nextInteractionRecord; + clear(); + + } else { + // Next collsions happing in the tail of the current one, + // Copy out up to difference and pop and push this amount + // of samples + + // Now filling the vector stream + for (auto& time : mTimeBins) { + if (!(nProcessedTimeBins + mFirstTimeBin < difference)) { + break; + } + + mDequeTime.push_back(time); + + ++nProcessedTimeBins; + } + + LZERO.fill(mDequeTime, mCurrentInteractionRecord, patchesFromAllTRUs); + auto TriggerInputs = LZERO.getTriggerInputs(); + auto TriggerInputsPatches = LZERO.getTriggerInputsPatches(); + int nIter = TriggerInputs.size(); + LOG(debug) << "DIG TRU fillOutputContainer in DigitsWriteoutBufferTRU: size of TriggerInputs = " << nIter; + // for (auto& patches : patchesFromAllTRUs) { + // LZERO.updatePatchesADC(patches); + // LZERO.peakFinderOnAllPatches(patches); + // } + mCurrentInteractionRecord = nextInteractionRecord; + + if (nProcessedTimeBins > 0) { + mFirstTimeBin += nProcessedTimeBins; + while (nProcessedTimeBins--) { + mTimeBins.pop_front(); // deque + } + } + reserve(15); + + LOG(debug) << "DIG TRU fillOutputContainer in DigitsWriteoutBufferTRU: HIGH IR"; + } +} +//________________________________________________________ +// Constructor: reserves space to keep always a minimum buffer size +DigitsWriteoutBufferTRU::DigitsWriteoutBufferTRU(unsigned int nTimeBins) : mBufferSize(nTimeBins) +{ + for (int itime = 0; itime < nTimeBins; itime++) { + mTimeBins.push_back(o2::emcal::DigitTimebinTRU()); + } +} +//________________________________________________________ +// Reserve space to keep always a minimum buffer size (put it in the cpp) +void DigitsWriteoutBufferTRU::reserve(int eventTimeBin) +{ + const auto space = mBufferSize + eventTimeBin - mFirstTimeBin; + if (mTimeBins.size() < space) { + mTimeBins.resize(space); + } +} +//________________________________________________________ +void DigitsWriteoutBufferTRU::init() +{ + const SimParam* simParam = &(o2::emcal::SimParam::Instance()); + + mLiveTime = simParam->getLiveTime(); + mBusyTime = simParam->getBusyTime(); + mNoPileupMode = simParam->isDisablePileup(); + mEndOfRun = 0; + + // mDigitStream.init(); +} +//________________________________________________________ +void DigitsWriteoutBufferTRU::clear() +{ + mTimeBins.clear(); + reserve(15); + // mEndOfRun = 0; +} +//________________________________________________________ +void DigitsWriteoutBufferTRU::finish() +{ + mEndOfRun = 1; +} +//________________________________________________________ +// Add digits to the buffer +void DigitsWriteoutBufferTRU::addDigits(unsigned int towerID, std::vector& digList) +{ + // mTimeBin has to have the absolute time information + for (int ientry = 0; ientry < digList.size(); ientry++) { + + auto& buffEntry = mTimeBins[ientry]; + auto& dig = digList.at(ientry); + + auto towerEntry = buffEntry.mDigitMap->find(towerID); + if (towerEntry == buffEntry.mDigitMap->end()) { + towerEntry = buffEntry.mDigitMap->insert(std::pair>(towerID, std::list())).first; + } + towerEntry->second.push_back(dig); + } +} diff --git a/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h b/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h index 4eb806d876d40..d070aa2470177 100644 --- a/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h +++ b/Detectors/EMCAL/simulation/src/EMCALSimulationLinkDef.h @@ -20,12 +20,26 @@ #pragma link C++ class o2::emcal::Digitizer + ; #pragma link C++ class o2::emcal::SDigitizer + ; #pragma link C++ class o2::emcal::DigitsWriteoutBuffer + ; +#pragma link C++ class o2::emcal::DigitsWriteoutBufferTRU + ; #pragma link C++ class o2::emcal::DigitsVectorStream + ; +#pragma link C++ class o2::emcal::LZEROElectronics + ; +#pragma link C++ class o2::emcal::TRUElectronics + ; +#pragma link C++ class o2::emcal::FastOrStruct + ; +#pragma link C++ class o2::emcal::EMCALTriggerInputs + ; +#pragma link C++ class o2::emcal::EMCALTriggerInputsPatch + ; +#pragma link C++ class o2::emcal::DigitizerTRU + ; +// #pragma link C++ class o2::emcal::DigitizerTRU + ; +#pragma link C++ class o2::emcal::DigitTimebinBase < o2::emcal::Digit> + ; +#pragma link C++ class o2::emcal::DigitTimebinBase < o2::emcal::LabeledDigit> + ; #pragma link C++ class o2::emcal::SimParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::emcal::SimParam> + ; #pragma link C++ class o2::emcal::LabeledDigit + ; #pragma link C++ class o2::emcal::RawWriter + ; #pragma link C++ class std::list < o2::emcal::LabeledDigit> + ; +#pragma link C++ class std::vector < o2::emcal::LabeledDigit> + ; +#pragma link C++ class std::vector < o2::emcal::FastOrStruct> + ; +#pragma link C++ class std::vector < o2::emcal::EMCALTriggerInputs> + ; +#pragma link C++ class std::vector < o2::emcal::EMCALTriggerInputsPatch> + ; #endif diff --git a/Detectors/EMCAL/simulation/src/LZEROElectronics.cxx b/Detectors/EMCAL/simulation/src/LZEROElectronics.cxx new file mode 100644 index 0000000000000..672c9c5b1ca89 --- /dev/null +++ b/Detectors/EMCAL/simulation/src/LZEROElectronics.cxx @@ -0,0 +1,252 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include "EMCALSimulation/LabeledDigit.h" +#include "EMCALSimulation/LZEROElectronics.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "EMCALSimulation/DigitTimebin.h" +#include "EMCALBase/TriggerMappingV2.h" +#include "TMath.h" +#include // for LOG + +using namespace o2::emcal; + +//_____________________________________________________________________ +// Peak finding algorithm +// +// It checks if there is a rising trend on four consecutive Timebins +// If yes, then it compares the integral to a programmable threshold +bool LZEROElectronics::peakFinderOnPatch(TRUElectronics& p, unsigned int patchID) +{ + auto& CurrentPatchTimeSum = p.mTimesum[patchID]; + auto& TimeSums = std::get<1>(CurrentPatchTimeSum); + bool trendOfDigitsInTower = false; + if (TimeSums.size() < 4) { + return false; + // } else if (TimeSums[0] < TimeSums[1] && TimeSums[1] < TimeSums[2] && TimeSums[2] >= TimeSums[3]) { + } else if (TimeSums[3] < TimeSums[2] && TimeSums[2] < TimeSums[1] && TimeSums[1] >= TimeSums[0]) { + trendOfDigitsInTower = true; + } else if (TimeSums[0] > 0. || TimeSums[1] > 0. || TimeSums[2] > 0. || TimeSums[3] > 0.) { + } + double integralOfADCvalues = 0; + for (auto it = TimeSums.begin(); it != TimeSums.end(); it++) { + integralOfADCvalues += *it; + } + bool peakOverThreshold = false; + if (integralOfADCvalues > mThreshold && trendOfDigitsInTower) { + peakOverThreshold = true; + } + return peakOverThreshold; +} +//_____________________________________________________________________ +// Peak finding algorithm on all patches +// It fills the mPeakFound vector with potential 1s +bool LZEROElectronics::peakFinderOnAllPatches(TRUElectronics& p) +{ + bool isFoundGlobal = false; + p.mFiredPatches.clear(); + for (auto& patches : p.mIndexMapPatch) { + auto PatchID = std::get<0>(patches); + auto isFound = peakFinderOnPatch(p, PatchID); + if (isFound) { + p.mFiredPatches.push_back(PatchID); + isFoundGlobal = true; + } + } + return isFoundGlobal; +} +//________________________________________________________ +void LZEROElectronics::init() +{ + auto mSimParam = &(o2::emcal::SimParam::Instance()); + mSimulateNoiseDigits = mSimParam->doSimulateNoiseDigits(); + setThreshold(mSimParam->getThresholdLZERO()); + // setThreshold(132.); +} +//________________________________________________________ +void LZEROElectronics::clear() +{ +} +//________________________________________________________ +void LZEROElectronics::updatePatchesADC(TRUElectronics& p) +{ + p.updateADC(); +} +//_______________________________________________________________________ +void LZEROElectronics::addNoiseDigits(Digit& d1) +{ + auto mSimParam = &(o2::emcal::SimParam::Instance()); + double amplitude = d1.getAmplitude(); + double sigma = mSimParam->getPinNoiseTRU(); + + TRandom3 mRandomGenerator(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + uint16_t noise = std::floor(std::abs(mRandomGenerator.Gaus(0, sigma) / constants::EMCAL_TRU_ADCENERGY)); // ADC + + Digit d(d1.getTower(), 0., noise, d1.getTimeStamp()); + + d1 += d; +} +//_______________________________________________________________________ +void LZEROElectronics::fill(const std::deque& digitlist, const o2::InteractionRecord record, std::vector& patchesFromAllTRUs) +{ + int counterDigitTimeBin = 0; + int sizemDigitMap = -999; + TriggerMappingV2 mTriggerMap(mGeometry); + + for (auto& digitsTimeBin : digitlist) { + // Inside the DigitTimebinTRU + // Fill the LZEROElectronics with the new ADC value + // At the end of the loop run the peak finder + // Ship to LONEElectronics in case a peak is found + // Entire logic limited to timebin by timebin -> effectively implementing time scan + counterDigitTimeBin++; + + for (auto& [fastor, digitsList] : *digitsTimeBin.mDigitMap) { + // Digit loop + // The peak finding algorithm is run after getting out of the loop! + if (digitsList.size() == 0) { + continue; + } + digitsList.sort(); + + int digIndex = 0; + Digit summedDigit; + bool first = true; + for (auto& ld : digitsList) { + if (first) { + summedDigit = ld; + first = false; + } else { + // summedDigit += ld; + + // safety device in case same fastOr + // but different towers, i.e. remember + // that the += operator fails WITHOUT + // feedback if that were to happen + Digit digitToSum(summedDigit.getTower(), ld.getAmplitude(), summedDigit.getTimeStamp()); + summedDigit += digitToSum; + } + } + + sizemDigitMap = (*digitsTimeBin.mDigitMap).size(); + if (mSimulateNoiseDigits) { + addNoiseDigits(summedDigit); + } + + auto [whichTRU, whichFastOrTRU] = mTriggerMap.getTRUFromAbsFastORIndex(fastor); + + auto whichFastOr = std::get<1>(mTriggerMap.convertFastORIndexTRUtoSTU(whichTRU, whichFastOrTRU)); + auto& patchTRU = patchesFromAllTRUs[whichTRU]; + auto& fastOrPatchTRU = patchTRU.mFastOrs[whichFastOr]; + // fastOrPatchTRU.updateADC(summedDigit.getAmplitudeADC()); + if (std::find(mMaskedFastOrs.begin(), mMaskedFastOrs.end(), fastor) != mMaskedFastOrs.end()) { + fastOrPatchTRU.updateADC(summedDigit.getAmplitudeADC()); + } + + digIndex++; + } + + // Evaluate -> peak finder (ALL TRUElectronics in ALL TRUs) + // in case peak found: + // - Create trigger input (IR of that timebin - delay [typically 8 or 9 samples - rollback (0)]) + // - Create L1 timesums (trivial - last time integral) -> Collect from all fastOrs in ALL TRUs + + // Trigger Inputs needs to have the correction of the delay + // Propagating for now all the interaction record + // The typical delay is 8 to 9 BCs and the rollback as well + // The rollback is taken from EMCALReconstruction/RecoParam.h + // The delay is due to the interactions between EMCAL and CTP + // It accounts for the difference in times between L0a, L0b, and then there will be a L1 and L1b delay + // There is 1BC uncertainty on the trigger readout due to steps in the interaction between CTP and detector simulations + bool foundPeak = false; + int counterWhichTRU = 0; + int triggeredTRU = -1; + std::vector triggeredPatches; + for (auto& patches : patchesFromAllTRUs) { + updatePatchesADC(patches); + bool foundPeakCurrentTRU = peakFinderOnAllPatches(patches); + auto firedPatches = getFiredPatches(patches); + if (foundPeakCurrentTRU == true && foundPeak == false) { + triggeredTRU = counterWhichTRU; + triggeredPatches = firedPatches; + } + if (foundPeakCurrentTRU) { + foundPeak = true; + } + counterWhichTRU += 1; + } + + if (foundPeak == true) { + LOG(debug) << "DIG TRU fill in LZEROElectronics: foundPeak = " << foundPeak; + } + EMCALTriggerInputs TriggerInputsForL1; + if (foundPeak) { + TriggerInputsForL1.mInterRecord = record; + TriggerInputsForL1.mTriggeredTRU = triggeredTRU; + TriggerInputsForL1.mTriggeredPatches = triggeredPatches; + int whichTRU = 0; + for (auto& patches : patchesFromAllTRUs) { + int whichFastOr = 0; + for (auto& fastor : patches.mFastOrs) { + TriggerInputsForL1.mLastTimesumAllFastOrs.push_back(std::make_tuple(whichTRU, std::get<1>(mTriggerMap.convertFastORIndexSTUtoTRU(mTriggerMap.convertTRUIndexTRUtoSTU(whichTRU), whichFastOr, o2::emcal::TriggerMappingV2::DetType_t::DET_EMCAL)), fastor.timesum())); + whichFastOr++; + } + whichTRU++; + } + } + + // EMCALTriggerInputsPatch TriggerInputsPatch; + // if (foundPeak) { + // TriggerInputsPatch.mInterRecord = record; + // int whichTRU = 0; + // for (auto& patches : patchesFromAllTRUs) { + // if(whichTRU < 46){ + // int whichPatch = 0; + // bool firedpatch = false; + // if(std::find(patches.mFiredPatches.begin(), patches.mFiredPatches.end(), whichPatch) != patches.mFiredPatches.end()){ + // firedpatch = true; + // } + // for (auto& patchTimeSums : patches.mTimesum) { + // auto& CurrentPatchTimesum = std::get<1>(patchTimeSums); + // // if( whichTRU == 30 || whichTRU == 31 || whichTRU == 44 || whichTRU == 45 ){ + // // if( whichPatch > 68) continue; + // // } + // TriggerInputsPatch.mLastTimesumAllPatches.push_back(std::make_tuple(whichTRU, whichPatch, CurrentPatchTimesum[3], firedpatch )); + // whichPatch++; + // } + // } + // whichTRU++; + // } + // } + + if (foundPeak) { + mTriggers.push_back(TriggerInputsForL1); + // mTriggersPatch.push_back(TriggerInputsPatch); + } + } +} +//________________________________________________________ +void LZEROElectronics::printMaskedFastOrs() +{ + LOG(info) << "==============================="; + LOG(info) << "== PRINT MASK SAVED IN LZERO =="; + int counter = 0; + for (auto fastOr : mMaskedFastOrs) { + LOG(info) << "fastOr masked (number, ID) = (" << counter << ", " << fastOr; + counter += 1; + } +} diff --git a/Detectors/EMCAL/simulation/src/RawCreator.cxx b/Detectors/EMCAL/simulation/src/RawCreator.cxx index 883d907b8858e..386602e32b91c 100644 --- a/Detectors/EMCAL/simulation/src/RawCreator.cxx +++ b/Detectors/EMCAL/simulation/src/RawCreator.cxx @@ -122,7 +122,7 @@ int main(int argc, const char** argv) rawwriter.init(); // Loop over all entries in the tree, where each tree entry corresponds to a time frame - for (auto en : *treereader) { + while (treereader->Next()) { rawwriter.digitsToRaw(*digitbranch, *triggerbranch); } rawwriter.getWriter().writeConfFile("EMC", "RAWDATA", o2::utils::Str::concat_string(outputdir, "/EMCraw.cfg")); diff --git a/Detectors/EMCAL/simulation/src/RawWriter.cxx b/Detectors/EMCAL/simulation/src/RawWriter.cxx index e289b9bfbca6e..14c32c0e299ae 100644 --- a/Detectors/EMCAL/simulation/src/RawWriter.cxx +++ b/Detectors/EMCAL/simulation/src/RawWriter.cxx @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include + #include #include @@ -127,12 +129,39 @@ bool RawWriter::processTrigger(const o2::emcal::TriggerRecord& trg) continue; } + // sort found towers according to FEC inside + // within the FEC channels are also sorted according + // their local channel ID + std::map> fecSortedTowersWithSignal; + auto& mappingDDL = mMappingHandler->getMappingForDDL(srucont.mSRUid); for (const auto& [tower, channel] : srucont.mChannels) { - bool saturatedBunchHG = false; - createPayload(channel, ChannelType_t::HIGH_GAIN, srucont.mSRUid, payload, saturatedBunchHG); - if (saturatedBunchHG) { - createPayload(channel, ChannelType_t::LOW_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + auto hwaddress = mappingDDL.getHardwareAddress(channel.mRow, channel.mCol, ChannelType_t::HIGH_GAIN); + auto fecInDLL = getBranchIndexFromHwAddress(hwaddress) * 10 + getFecIndexFromHwAddress(hwaddress); + auto channelID = getChannelIndexFromHwAddress(hwaddress); + auto fecFound = fecSortedTowersWithSignal.find(fecInDLL); + if (fecFound != fecSortedTowersWithSignal.end()) { + fecFound->second[channelID] = tower; + } else { + std::map channelsInFec; + channelsInFec[channelID] = tower; + fecSortedTowersWithSignal[fecInDLL] = channelsInFec; + } + } + + // encode payload for sorted channels + for (const auto& [fec, channelsInFec] : fecSortedTowersWithSignal) { + for (auto [channelID, tower] : channelsInFec) { + auto towerChannel = srucont.mChannels.find(tower); + if (towerChannel != srucont.mChannels.end()) { + bool saturatedBunchHG = false; + createPayload(towerChannel->second, ChannelType_t::HIGH_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + if (saturatedBunchHG) { + createPayload(towerChannel->second, ChannelType_t::LOW_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + } + } else { + LOG(error) << "No data found for FEC " << fec << ", channel " << channelID << "(tower " << tower << ")"; + } } } diff --git a/Detectors/EMCAL/simulation/src/SimParam.cxx b/Detectors/EMCAL/simulation/src/SimParam.cxx index 74af39125f4c3..818ecda3ecb58 100644 --- a/Detectors/EMCAL/simulation/src/SimParam.cxx +++ b/Detectors/EMCAL/simulation/src/SimParam.cxx @@ -30,14 +30,13 @@ void SimParam::PrintStream(std::ostream& stream) const stream << "\nEMCal::SimParam.mGainFluctuations = " << mGainFluctuations; stream << "\nEMCal::SimParam.mPinNoise = " << mPinNoise; stream << "\nEMCal::SimParam.mPinNoiseLG = " << mPinNoiseLG; - stream << "\nEMCal::SimParam.mTimeNoise = " << mTimeNoise; - stream << "\nEMCal::SimParam.mTimeDelay = " << mTimeDelay; - stream << "\nEMCal::SimParam.mTimeDelayFromOCDB = " << ((mTimeDelayFromOCDB) ? "true" : "false"); + stream << "\nEMCal::SimParam.mPinNoiseTRU = " << mPinNoiseTRU; stream << "\nEMCal::SimParam.mTimeResolutionPar0 = " << mTimeResolutionPar0; stream << "\nEMCal::SimParam.mTimeResolutionPar1 = " << mTimeResolutionPar1; stream << "\nEMCal::SimParam.mTimeResponseTau = " << mTimeResponseTau; stream << "\nEMCal::SimParam.mTimeResponsePower = " << mTimeResponsePower; stream << "\nEMCal::SimParam.mTimeResponseThreshold = " << mTimeResponseThreshold; + stream << "\nEMCal::SimParam.mThresholdLZERO = " << mThresholdLZERO; stream << "\nEMCal::SimParam.mNADCEC = " << mNADCEC; stream << "\nEMCal::SimParam.mSwapPhase = " << mSwapPhase; stream << "\nEMCal::SimParam.mA = " << mA; diff --git a/Detectors/EMCAL/simulation/src/TRUElectronics.cxx b/Detectors/EMCAL/simulation/src/TRUElectronics.cxx new file mode 100644 index 0000000000000..160fff8cfb062 --- /dev/null +++ b/Detectors/EMCAL/simulation/src/TRUElectronics.cxx @@ -0,0 +1,230 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TRUElectronics.cxx +/// \brief Implementation of the EMCAL TRUElectronics for the LZEROElectronics +#include +#include +#include // for LOG +#include "EMCALSimulation/TRUElectronics.h" +#include "EMCALBase/TriggerMappingV2.h" + +using namespace o2::emcal; + +//____________________________________________________________________________ +TRUElectronics::TRUElectronics() : mPatchSize(2), mWhichSide(0), mWhichSuperModuleSize(0) +{ + // DEFAULT CONSTRUCTOR + mFastOrs.resize(96); +} +//____________________________________________________________________________ +TRUElectronics::TRUElectronics(int patchSize, int whichSide, int whichSuperModuleSize) : mPatchSize(patchSize), mWhichSide(whichSide), mWhichSuperModuleSize(whichSuperModuleSize) +{ + // CONSTRUCTOR + mFastOrs.resize(96); +} +//____________________________________________________________________________ +void TRUElectronics::init() +{ + mIndexMapPatch.resize(77); + for (auto IndexMapPatch : mIndexMapPatch) { + (std::get<1>(IndexMapPatch)).resize(mPatchSize * mPatchSize); + } + mFiredFastOrIndexMapPatch.resize(77); + for (auto FiredFastOrIndexMapPatch : mFiredFastOrIndexMapPatch) { + (std::get<1>(FiredFastOrIndexMapPatch)).resize(mPatchSize * mPatchSize); + } + mADCvalues.resize(77); + mTimesum.resize(77); + mPatchIDSeedFastOrIDs.resize(77); + mPreviousTimebinADCvalue.resize(77); + mFastOrs.resize(96); + for (auto FastOr : mFastOrs) { + FastOr.init(); + } + assignSeedModuleToAllPatches(); + assignModulesToAllPatches(); +} +//____________________________________________________________________________ +void TRUElectronics::clear() +{ + mIndexMapPatch.clear(); + mFiredFastOrIndexMapPatch.clear(); + mADCvalues.clear(); + mTimesum.clear(); + mPatchIDSeedFastOrIDs.clear(); + mPreviousTimebinADCvalue.clear(); +} +//____________________________________________________________________________ +void TRUElectronics::assignSeedModuleToPatchWithSTUIndexingFullModule(int& patchID) +{ + if (mWhichSide == 0) { + // A side + int rowSeed = patchID / 7; + int columnSeed = patchID % 7; + int SeedID = rowSeed + patchID; + std::get<1>(mPatchIDSeedFastOrIDs[patchID]) = SeedID; + } else if (mWhichSide == 1) { + // C side + int rowSeed = patchID / 7; + int columnSeed = patchID % 7; + int SeedID = 95 - (rowSeed + patchID); + std::get<1>(mPatchIDSeedFastOrIDs[patchID]) = SeedID; + } +} +//____________________________________________________________________________ +void TRUElectronics::assignSeedModuleToPatchWithSTUIndexingOneThirdModule(int& patchID) +{ + if (mWhichSide == 0) { + // A side + int rowSeed = patchID / 23; + int columnSeed = patchID % 23; + int SeedID = rowSeed + patchID; + std::get<1>(mPatchIDSeedFastOrIDs[patchID]) = SeedID; + } else if (mWhichSide == 1) { + // C side + int rowSeed = patchID / 23; + int columnSeed = patchID % 23; + int SeedID = 95 - (rowSeed + patchID); + std::get<1>(mPatchIDSeedFastOrIDs[patchID]) = SeedID; + } +} +//____________________________________________________________________________ +void TRUElectronics::assignSeedModuleToAllPatches() +{ + if (mWhichSuperModuleSize == 0) { + // Full Size + for (int i = 0; i < 77; i++) { + std::get<0>(mPatchIDSeedFastOrIDs[i]) = i; + assignSeedModuleToPatchWithSTUIndexingFullModule(i); + } + } else if (mWhichSuperModuleSize == 1) { + // One third Size + for (int i = 0; i < 69; i++) { + std::get<0>(mPatchIDSeedFastOrIDs[i]) = i; + assignSeedModuleToPatchWithSTUIndexingOneThirdModule(i); + } + } +} +//____________________________________________________________________________ +void TRUElectronics::assignModulesToAllPatches() +{ + if (mWhichSuperModuleSize == 0) { + // Full Size + for (int i = 0; i < 77; i++) { + auto& mIndexMapPatchID = std::get<0>(mIndexMapPatch[i]); + auto& mFiredFastOrIndexMapPatchID = std::get<0>(mFiredFastOrIndexMapPatch[i]); + auto& mADCvaluesID = std::get<0>(mADCvalues[i]); + auto& mPreviousTimebinADCvalueID = std::get<0>(mPreviousTimebinADCvalue[i]); + mIndexMapPatchID = i; + mFiredFastOrIndexMapPatchID = i; + mADCvaluesID = i; + mPreviousTimebinADCvalueID = i; + int SeedID = std::get<1>(mPatchIDSeedFastOrIDs[i]); + // assigning a square + if (mWhichSide == 0) { + // A side + for (int iRow = 0; iRow < mPatchSize; iRow++) { // row advancement + for (int iColumn = 0; iColumn < mPatchSize; iColumn++) { // column advancement + auto& IndexMapPatch = std::get<1>(mIndexMapPatch[i]); + IndexMapPatch.push_back(SeedID + iRow + iColumn * 8); + // (std::get<1>(mIndexMapPatch[i])).push_back(SeedID + k + l * 8); + } + } + } else if (mWhichSide == 1) { + // C side + for (int iRow = 0; iRow < mPatchSize; iRow++) { // row advancement + for (int iColumn = 0; iColumn < mPatchSize; iColumn++) { // column advancement + auto& IndexMapPatch = std::get<1>(mIndexMapPatch[i]); + IndexMapPatch.push_back(SeedID - iRow - iColumn * 8); + // (std::get<1>(mIndexMapPatch[i])).push_back(SeedID - k - l * 8); + } + } + } + } + } else if (mWhichSuperModuleSize == 1) { + // One third Size + for (int i = 0; i < 69; i++) { + auto& mIndexMapPatchID = std::get<0>(mIndexMapPatch[i]); + auto& mFiredFastOrIndexMapPatchID = std::get<0>(mFiredFastOrIndexMapPatch[i]); + auto& mADCvaluesID = std::get<0>(mADCvalues[i]); + auto& mPreviousTimebinADCvalueID = std::get<0>(mPreviousTimebinADCvalue[i]); + mIndexMapPatchID = i; + mFiredFastOrIndexMapPatchID = i; + mADCvaluesID = i; + mPreviousTimebinADCvalueID = i; + int SeedID = std::get<1>(mPatchIDSeedFastOrIDs[i]); + // assigning a square + if (mWhichSide == 0) { + // A side + for (int iRow = 0; iRow < mPatchSize; iRow++) { // row advancement + for (int iColumn = 0; iColumn < mPatchSize; iColumn++) { // column advancement + auto& IndexMapPatch = std::get<1>(mIndexMapPatch[i]); + IndexMapPatch.push_back(SeedID + iRow + iColumn * 24); + // (std::get<1>(mIndexMapPatch[i])).push_back(SeedID + k + l * 24); + } + } + } else if (mWhichSide == 1) { + // C side + for (int iRow = 0; iRow < mPatchSize; iRow++) { // row advancement + for (int iColumn = 0; iColumn < mPatchSize; iColumn++) { // column advancement + auto& IndexMapPatch = std::get<1>(mIndexMapPatch[i]); + IndexMapPatch.push_back(SeedID - iRow - iColumn * 8); + // (std::get<1>(mIndexMapPatch[i])).push_back(SeedID - k - l * 8); + } + } + } + } + } +} +//________________________________________________________ +void TRUElectronics::updateADC() +{ + // Loop over all patches and their ADC values + for (auto& patch : mADCvalues) { + auto& ADCvalues = std::get<1>(patch); + auto& PatchID = std::get<0>(patch); + if (ADCvalues.size() == 4) { + // If there are already four elements, pop the first + std::get<1>(mPreviousTimebinADCvalue[PatchID]) = ADCvalues.front(); + ADCvalues.erase(ADCvalues.begin()); + } else { + if (ADCvalues.size() > 4) { + LOG(debug) << "DIG TRU updateADC in TRUElectronics: ERROR!!!!! "; + } + } + double integralADCnew = 0; + + for (auto FastOrs : std::get<1>(mIndexMapPatch[PatchID])) { + // Loop over all the fastOrs addigned to the current patch + // and sum the current ADC values together + auto elem = mFastOrs[FastOrs].mADCvalues; + if (elem.size() == 0) { + continue; + } + auto it = elem.end() - 1; + auto pointedvalue = *it; + integralADCnew += pointedvalue; + } + ADCvalues.push_back(integralADCnew); + + // Saving the timesum + auto& CurrentPatchTimesum = std::get<1>(mTimesum[PatchID]); + if (CurrentPatchTimesum.size() == 4) { + CurrentPatchTimesum.erase(CurrentPatchTimesum.begin()); + } + double IntegralADCvalues = 0; + for (auto ADC : ADCvalues) { + IntegralADCvalues += ADC; + } + CurrentPatchTimesum.push_back(IntegralADCvalues); + } +} \ No newline at end of file diff --git a/Detectors/EMCAL/workflow/CMakeLists.txt b/Detectors/EMCAL/workflow/CMakeLists.txt index 689cfd593d408..7222b0aedc320 100644 --- a/Detectors/EMCAL/workflow/CMakeLists.txt +++ b/Detectors/EMCAL/workflow/CMakeLists.txt @@ -25,8 +25,20 @@ o2_add_library(EMCALWorkflow src/CellRecalibratorSpec.cxx src/EntropyDecoderSpec.cxx src/StandaloneAODProducerSpec.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsCTP O2::DataFormatsEMCAL O2::EMCALSimulation O2::Steer - O2::DPLUtils O2::EMCALBase O2::EMCALCalib O2::EMCALReconstruction O2::Algorithm O2::MathUtils) + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsCTP + O2::DataFormatsEMCAL + O2::DataFormatsFT0 + O2::DataFormatsFV0 + O2::EMCALSimulation + O2::Steer + O2::DPLUtils + O2::EMCALBase + O2::EMCALCalib + O2::EMCALCalibration + O2::EMCALReconstruction + O2::Algorithm + O2::MathUtils) o2_add_executable(reco-workflow COMPONENT_NAME emcal diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/AnalysisClusterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/AnalysisClusterSpec.h index 1c16c4a270454..4bcf6e9f2c577 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/AnalysisClusterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/AnalysisClusterSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_ANALYSISCLUSTER_SPEC +#define O2_EMCAL_ANALYSISCLUSTER_SPEC + #include #include "DataFormatsEMCAL/Cluster.h" @@ -67,10 +70,10 @@ class AnalysisClusterSpec : public framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); - o2::emcal::Clusterizer mClusterizer; ///< Clusterizer object - o2::emcal::Geometry* mGeometry = nullptr; ///< Pointer to geometry object - o2::emcal::EventHandler* mEventHandler = nullptr; ///< Pointer to the event builder - o2::emcal::ClusterFactory* mClusterFactory = nullptr; ///< Pointer to the cluster builder + o2::emcal::Clusterizer mClusterizer; ///< Clusterizer object + o2::emcal::Geometry* mGeometry = nullptr; ///< Pointer to geometry object + o2::emcal::EventHandler* mEventHandler = nullptr; ///< Pointer to the event builder + o2::emcal::ClusterFactory* mClusterFactory = nullptr; ///< Pointer to the cluster builder std::shared_ptr mGGCCDBRequest; std::vector* mOutputAnaClusters = nullptr; ///< Container with output clusters (pointer) }; @@ -86,3 +89,5 @@ framework::DataProcessorSpec getAnalysisClusterSpec(bool useDigits); } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_ANALYSISCLUSTER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CalibLoader.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CalibLoader.h index 1aee7fe1b9fcd..06ffc1f5db3c9 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CalibLoader.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CalibLoader.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_CALIBLOADER +#define O2_EMCAL_CALIBLOADER + #include #include #include @@ -248,3 +251,5 @@ class CalibLoader } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_CALIBLOADER diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellConverterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellConverterSpec.h index 487ef9075f429..bd6c2699a7e33 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellConverterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellConverterSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_CELLCONVERTER_SPEC +#define O2_EMCAL_CELLCONVERTER_SPEC + #include #include "DataFormatsEMCAL/Cell.h" @@ -19,6 +22,7 @@ #include "EMCALBase/Geometry.h" #include "EMCALReconstruction/CaloRawFitter.h" #include "EMCALReconstruction/AltroHelper.h" +#include "EMCALWorkflow/CalibLoader.h" #include "SimulationDataFormat/MCTruthContainer.h" namespace o2 @@ -47,8 +51,9 @@ class CellConverterSpec : public framework::Task /// \brief Constructor /// \param propagateMC If true the MCTruthContainer is propagated to the output /// \param useccdb If true the TecoParams + /// \param inputSubsepc Subsepc of input objects /// \param outputSubspec Subspec of output objects - CellConverterSpec(bool propagateMC, bool useccdb, int outputSubspec) : framework::Task(), mPropagateMC(propagateMC), mLoadRecoParamFromCCDB(useccdb), mSubspecification(outputSubspec){}; + CellConverterSpec(bool propagateMC, int inputSubsepc, int outputSubspec, std::shared_ptr calibhandler) : framework::Task(), mPropagateMC(propagateMC), mSubspecificationIn(inputSubsepc), mSubspecificationOut(outputSubspec), mCalibHandler(calibhandler){}; /// \brief Destructor ~CellConverterSpec() override = default; @@ -86,12 +91,16 @@ class CellConverterSpec : public framework::Task int selectMaximumBunch(const gsl::span& bunchvector); + /// \brief Update calibration objects + void updateCalibrationObjects(); + private: bool mPropagateMC = false; ///< Switch whether to process MC true labels - bool mLoadRecoParamFromCCDB = false; ///< Flag to load the the SimParams from CCDB - bool mSubspecification = 0; ///< Output subspecification - o2::emcal::Geometry* mGeometry = nullptr; ///! mRawFitter; ///! mCalibHandler; //! mRawFitter; //! mOutputCells; ///< Container with output cells std::vector mOutputTriggers; ///< Container with output trigger records o2::dataformats::MCTruthContainer mOutputLabels; ///< Container with output MC labels @@ -101,13 +110,16 @@ class CellConverterSpec : public framework::Task /// \ingroup EMCALworkflow /// \param propagateMC If true the MC truth container is propagated to the output /// \param useccdb If true the RecoParams are loaded from the CCDB +/// \param inputSubsepc Subspec of input objects /// \param outputSubspec Subspec of output objects /// /// Refer to CellConverterSpec::run for input and output specs -framework::DataProcessorSpec getCellConverterSpec(bool propagateMC, bool useccdb = false, int outputSubspec = 0); +framework::DataProcessorSpec getCellConverterSpec(bool propagateMC, int inputSubsepc = 0, int outputSubspec = 0); } // namespace reco_workflow } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_CELLCONVERTER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellRecalibratorSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellRecalibratorSpec.h index 5947b10ff033f..5c55377290862 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellRecalibratorSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/CellRecalibratorSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_CELLRECALIBRATOR_SPEC +#define O2_EMCAL_CELLRECALIBRATOR_SPEC + #include #include #include @@ -159,4 +162,6 @@ framework::DataProcessorSpec getCellRecalibratorSpec(uint32_t inputSubspec, uint } // namespace emcal -} // namespace o2 \ No newline at end of file +} // namespace o2 + +#endif // O2_EMCAL_CELLRECALIBRATOR_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/ClusterizerSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/ClusterizerSpec.h index 67c60fab6655c..6fd204acb161f 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/ClusterizerSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/ClusterizerSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_CLUSTERIZER_SPEC +#define O2_EMCAL_CLUSTERIZER_SPEC + #include #include "DataFormatsEMCAL/Cluster.h" @@ -85,3 +88,5 @@ framework::DataProcessorSpec getClusterizerSpec(bool useDigits); } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_CLUSTERIZER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/DigitsPrinterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/DigitsPrinterSpec.h index 6ee0b7143c186..378da6e14dc7d 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/DigitsPrinterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/DigitsPrinterSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_DIGITSPRINTER_SPEC +#define O2_EMCAL_DIGITSPRINTER_SPEC + #include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" @@ -61,9 +64,11 @@ class DigitsPrinterSpec : public framework::Task /// specs o2::framework::DataProcessorSpec getEmcalDigitsPrinterSpec(std::string inputtype); -//using DigitsPrinterSpecDigit = o2::emcal::reco_workflow::DigitsPrinterSpec; -//using DigitsPrinterSpecCell = o2::emcal::reco_workflow::DigitsPrinterSpec; +// using DigitsPrinterSpecDigit = o2::emcal::reco_workflow::DigitsPrinterSpec; +// using DigitsPrinterSpecCell = o2::emcal::reco_workflow::DigitsPrinterSpec; } // namespace reco_workflow } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_DIGITSPRINTER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EMCALDigitizerSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EMCALDigitizerSpec.h index 9dbf00c91f730..ee50dfb03bb6d 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EMCALDigitizerSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EMCALDigitizerSpec.h @@ -12,6 +12,7 @@ #ifndef STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_ #define STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_ +#include #include #include "Framework/DataProcessorSpec.h" @@ -22,13 +23,27 @@ #include "EMCALSimulation/SDigitizer.h" #include "SimulationDataFormat/MCTruthContainer.h" #include +#include "EMCALSimulation/DigitizerTRU.h" class TChain; namespace o2 { + +namespace steer +{ +class MCKinematicsReader; +} + +namespace ctp +{ +class CTPConfiguration; +} + namespace emcal { +class CalibLoader; + /// \brief Create new digitizer spec /// \return Digitizer spec @@ -42,7 +57,7 @@ class DigitizerSpec final : public o2::base::BaseDPLDigitizer, public o2::framew public: using o2::base::BaseDPLDigitizer::init; /// \brief Constructor - DigitizerSpec(bool useccdb) : o2::base::BaseDPLDigitizer(o2::base::InitServices::GEOM), o2::framework::Task(), mLoadSimParamFromCCDB(useccdb) {} + DigitizerSpec(std::shared_ptr calibloader, bool requireCTPInput) : o2::base::BaseDPLDigitizer(o2::base::InitServices::GEOM), o2::framework::Task(), mRequireCTPInput(requireCTPInput), mCalibHandler(calibloader) {} /// \brief Destructor ~DigitizerSpec() final = default; @@ -65,20 +80,28 @@ class DigitizerSpec final : public o2::base::BaseDPLDigitizer, public o2::framew void run(framework::ProcessingContext& ctx) override; private: - Bool_t mFinished = false; ///< Flag for digitization finished - Bool_t mLoadSimParamFromCCDB = false; ///< Flag to load the the SimParams from CCDB - bool mIsConfigured = false; ///< Initialization status of the digitizer - Digitizer mDigitizer; ///< Digitizer object - o2::emcal::SDigitizer mSumDigitizer; ///< Summed digitizer - std::vector mHits; ///< Vector with input hits + Bool_t mFinished = false; ///< Flag for digitization finished + bool mIsConfigured = false; ///< Initialization status of the digitizer + bool mRunSDitizer = false; ///< Run SDigitization + bool mRequireCTPInput = false; ///< Require CTP min. bias input + Digitizer mDigitizer; ///< Digitizer object + o2::emcal::SDigitizer mSumDigitizer; ///< Summed digitizer + std::shared_ptr mCalibHandler; ///< Handler of calibration objects + std::vector mHits; ///< Vector with input hits std::vector mSimChains; + o2::ctp::CTPConfiguration* mCTPConfig; ///< CTP configuration + o2::steer::MCKinematicsReader* mcReader; ///< reader to access MC collision information + + DigitizerTRU mDigitizerTRU; ///< Digitizer object TRU + o2::emcal::SDigitizer mSumDigitizerTRU; ///< Summed digitizer TRU + bool mRunDigitizerTRU = true; ///< Run Digitizer TRU? }; /// \brief Create new digitizer spec /// \return Digitizer spec -o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool mctruth = true, bool useccdb = true); +o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool requireCTPInput, bool mctruth = true, bool useccdb = true); -} // end namespace emcal +} // namespace emcal } // end namespace o2 #endif /* STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_ */ diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h index 0215e0ae65e43..9cc5ba7887473 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity, unsigned int sspecOut); + EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut = 0, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h index cdfb342e7ff11..df502beef30df 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace emcal class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/OfflineCalibSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/OfflineCalibSpec.h index b9c5ad2f72964..556b4e1040bda 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/OfflineCalibSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/OfflineCalibSpec.h @@ -9,10 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_OFFLINECALIB_SPEC +#define O2_EMCAL_OFFLINECALIB_SPEC + #include #include "Framework/DataProcessorSpec.h" #include "EMCALWorkflow/CalibLoader.h" #include "EMCALCalib/GainCalibrationFactors.h" +#include "EMCALCalibration/EMCALCalibParams.h" +#include "DataFormatsCTP/Digits.h" +#include "DataFormatsCTP/Configuration.h" #include "Framework/Task.h" #include "TFile.h" @@ -35,11 +41,13 @@ namespace emcal /// This task fills offline calibration objects for the EMCAL. class OfflineCalibSpec : public framework::Task { + using EMCALCalibParams = o2::emcal::EMCALCalibParams; + public: /// \brief Constructor /// \param makeCellIDTimeEnergy If true the THnSparseF of cell ID, time, and energy is made /// \param rejectCalibTriggers if true, only events which have the o2::trigger::PhT flag will be taken into account - OfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, std::shared_ptr calibHandler) : mMakeCellIDTimeEnergy(makeCellIDTimeEnergy), mRejectCalibTriggers(rejectCalibTriggers), mCalibrationHandler(calibHandler){}; + OfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, bool rejectL0Trigger, std::shared_ptr calibHandler) : mMakeCellIDTimeEnergy(makeCellIDTimeEnergy), mRejectCalibTriggers(rejectCalibTriggers), mRejectL0Triggers(rejectL0Trigger), mCalibrationHandler(calibHandler){}; /// \brief Destructor ~OfflineCalibSpec() override = default; @@ -63,6 +71,9 @@ class OfflineCalibSpec : public framework::Task /// void endOfStream(o2::framework::EndOfStreamContext& ec) final; + static const char* getCTPDigitsBinding() { return "CTPDigits"; } + static const char* getCTPConfigBinding() { return "CTPConfig"; } + private: /// \brief Update calibration objects (if changed) void updateCalibObjects(); @@ -77,13 +88,17 @@ class OfflineCalibSpec : public framework::Task bool mMakeCellIDTimeEnergy = true; ///< Switch whether or not to make a THnSparseF of cell ID, time, and energy bool mRejectCalibTriggers = true; ///< Switch to select if calib triggerred events should be rejected bool mEnableGainCalib = false; ///< Switch weather gain calibration should be applied or not when filling the hsitograms + bool mRejectL0Triggers = false; ///< Switch to select if L0 triggerred events should be rejected + std::vector mSelectedClassMasks = {}; ///< vector with selected trigger masks that are accepted for processing }; /// \brief Creating offline calib spec /// \ingroup EMCALworkflow /// -o2::framework::DataProcessorSpec getEmcalOfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, uint32_t inputsubspec, bool enableGainCalib); +o2::framework::DataProcessorSpec getEmcalOfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, bool rejectL0Trigger, uint32_t inputsubspec, bool enableGainCalib, bool ctpcfgperrun); } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_OFFLINECALIB_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/PublisherSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/PublisherSpec.h index 105c3aface4cd..468e66c43a0ab 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/PublisherSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/PublisherSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_PUBLISHER_SPEC +#define O2_EMCAL_PUBLISHER_SPEC + #include "DPLUtils/RootTreeReader.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" @@ -57,23 +60,22 @@ framework::DataProcessorSpec getPublisherSpec(PublisherConf const& config, uint3 // a creator callback for the actual reader instance auto creator = [dto, tro, mco, subspec, propagateMC](const char* treename, const char* filename, int nofEvents, Reader::PublishingMode publishingMode, const char* branchname, const char* triggerbranchname, const char* mcbranchname) { - constexpr auto persistency = o2::framework::Lifetime::Timeframe; if (propagateMC) { return std::make_shared(treename, filename, nofEvents, publishingMode, - Output{mco.origin, mco.description, subspec, persistency}, + Output{mco.origin, mco.description, subspec}, mcbranchname, - Reader::BranchDefinition{Output{dto.origin, dto.description, subspec, persistency}, branchname}, - Reader::BranchDefinition{Output{tro.origin, tro.description, subspec, persistency}, triggerbranchname}); + Reader::BranchDefinition{Output{dto.origin, dto.description, subspec}, branchname}, + Reader::BranchDefinition{Output{tro.origin, tro.description, subspec}, triggerbranchname}); } else { return std::make_shared(treename, filename, nofEvents, publishingMode, - Reader::BranchDefinition{Output{dto.origin, dto.description, subspec, persistency}, branchname}, - Reader::BranchDefinition{Output{tro.origin, tro.description, subspec, persistency}, triggerbranchname}); + Reader::BranchDefinition{Output{dto.origin, dto.description, subspec}, branchname}, + Reader::BranchDefinition{Output{tro.origin, tro.description, subspec}, triggerbranchname}); } }; @@ -106,3 +108,5 @@ framework::DataProcessorSpec createPublisherSpec(PublisherConf const& config, ui } // namespace emcal } // end namespace o2 + +#endif // O2_EMCAL_PUBLISHER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h index 5b1ccea2cc9a7..00eb030e470d9 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#ifndef O2_EMCAL_RAWTOCELLCONVERTER_SPEC +#define O2_EMCAL_RAWTOCELLCONVERTER_SPEC + #include #include #include @@ -17,13 +20,17 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "DataFormatsEMCAL/Cell.h" +#include "DataFormatsEMCAL/CompressedTriggerData.h" #include "DataFormatsEMCAL/TriggerRecord.h" #include "Headers/DataHeader.h" #include "EMCALBase/Geometry.h" #include "EMCALBase/Mapper.h" +#include "EMCALBase/TriggerMappingV2.h" #include "EMCALReconstruction/CaloRawFitter.h" +#include "EMCALReconstruction/RawReaderMemory.h" #include "EMCALReconstruction/RecoContainer.h" #include "EMCALReconstruction/ReconstructionErrors.h" +#include "EMCALReconstruction/TRUDecodingErrors.h" #include "EMCALWorkflow/CalibLoader.h" namespace o2 @@ -33,26 +40,92 @@ namespace emcal { class AltroDecoderError; +class Channel; class MinorAltroDecodingError; class RawDecodingError; +class FastORIndexException; +class TRUIndexException; +class FastOrStartTimeInvalidException; namespace reco_workflow { /// \class RawToCellConverterSpec -/// \brief Coverter task for Raw data to EMCAL cells +/// \brief Coverter task for Raw data to EMCAL cells and trigger objects /// \author Hadi Hassan , Oak Ridge National Laboratory +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \ingroup EMCALworkflow /// \since December 10, 2019 +//// +/// General reconstruction task of EMCAL raw data of FEC and trigger sources, running during the synchronous +/// reconstruction. The task decodees pages from ALTRO, Fake ALTRO and STU sources, and mergees the data according +/// to events (triggers). The data is then further processed based on the origin of the data: +/// +/// - in case of FEC data (cells or LEDMONS) a raw fit is performed, and data from high- and low-gain channels are +/// merged, always preferring the high-gain data if not saturated (better resolution) +/// - in case of Fake ALTRO data (TRU) the L0 timesums, trigger patches and TRU information are reconstructed. For +/// the trigger patches a small peak finder selects the time sample with the max. patch energy. For the L0 timesums +/// a fixed L0 time (8) is used for all event types +/// +/// FEC and trigger information are ordered according to events and streamed to their output buffers, where corresponding +/// trigger records mark ranges in the buffer belonging to the same trigger. Tasks subscribing to outputs from this +/// task must always subscribe to teh trigger records in addition. +/// +/// Several components of the task (raw parsing, ALTRO decoding, channel mapping and geometry, raw fit) can end in error +/// states, particularly due to unexpected data. Error handling is performed internally at different stages. Errors are +/// cathegoriezed as major or minor errors, where major errors prevent decoding the page while minor errors only lead +/// to loss of certain segments. For monitoring tasks must subscribe to EMC/DECODERERR. +/// +/// In order to guarantee data consistency a link checker compares the links contributing to data from a certain trigger +/// to the links activated in the DCS. If not all links are present the timeframe is discarded. In order to switch off +/// that feature the option --no-checkactivelinks must be activated. /// +/// Inputs: +/// | Input spec | Optional | CCDB | Purpose | +/// |----------------------|----------|------|----------------------------------------------| +/// | EMC/RAWDATA | no | no | EMCAL raw data | +/// | FLP/DISTSUBTIMEFRAME | yes | no | Message send when no data was received in TF | +/// | EMC/RECOPARAM | no | yes | Reconstruction parameters | +/// | EMC/FEEDCS | no | yes | FEE DCS information | +/// +/// Outputs: +/// | Input spec | Subspec (default) | Optional | Purpose | +/// |----------------------|-------------------|-----------|----------------------------------------------------| +/// | EMC/CELLS | 1 | no | EMCAL cell (tower) data | +/// | EMC/CELLSTRGR | 1 | no | Trigger records related to cell data | +/// | EMC/DECODERERR | 1 | yes | Decoder errors (for QC), if enabled | +/// | EMC/TRUS | 1 | yes | TRU information, if trigger reconstruction enabled | +/// | EMC/TRUSTRGR | 1 | yes | Trigger reconrds related to TRU information | +/// | EMC/PATCHES | 1 | yes | Trigger patches, if trigger reconstruction enabled | +/// | EMC/PATCHESTRGR | 1 | yes | Trigger reconrds related to trigger patches | +/// | EMC/FASTORS | 1 | yes | L0 timesums, if trigger reconstruction enabled | +/// | EMC/FASTORSTRGR | 1 | yes | Trigger reconrds related to L0 timesums | +/// +/// Workflow options (via --EMCALRawToCellConverterSpec ...): +/// | Option | Default | Possible values | Purpose | +/// |---------------------|---------|-----------------|------------------------------------------------| +/// | fitmethod | gamma2 | gamma2,standard | Raw fit method | +/// | maxmessage | 100 | any int | Max. amount of error messages on infoLogger | +/// | printtrailer | false | set (bool) | Print RCU trailer (for debugging) | +/// | no-mergeHGLG | false | set (bool) | Do not merge HG and LG channels for same tower | +/// | no-checkactivelinks | false | set (bool) | Do not check for active links per BC | +/// | no-evalpedestal | false | set (bool) | Disable pedestal evaluation | +/// +/// Global switches of the EMCAL reco workflow related to the RawToCellConverter: +/// | Option | Default | Purpose | +/// | -------------------------------|---------|-----------------------------------------------| +/// | disable-decoding-errors | false | Disable sending decoding errors | +/// | disable-trigger-reconstruction | false | Disable trigger reconstruction | +/// | ignore-dist-stf | false | disable subscribing to FLP/DISTSUBTIMEFRAME/0 | class RawToCellConverterSpec : public framework::Task { public: /// \brief Constructor /// \param subspecification Output subspecification for parallel running on multiple nodes /// \param hasDecodingErrors Option to swich on/off creating raw decoding error objects for later monitoring - /// \param loadRecoParamsFromCCDB Option to load the RecoParams from the CCDB + /// \param hasTriggerReconstruction Perform trigger reconstruction and add trigger-related outputs /// \param calibhandler Calibration object handler - RawToCellConverterSpec(int subspecification, bool hasDecodingErrors, std::shared_ptr calibhandler) : framework::Task(), mSubspecification(subspecification), mCreateRawDataErrors(hasDecodingErrors), mCalibHandler(calibhandler){}; + RawToCellConverterSpec(int subspecification, bool hasDecodingErrors, bool hasTriggerReconstruction, std::shared_ptr calibhandler) : framework::Task(), mSubspecification(subspecification), mCreateRawDataErrors(hasDecodingErrors), mDoTriggerReconstruction(hasTriggerReconstruction), mCalibHandler(calibhandler){}; /// \brief Destructor ~RawToCellConverterSpec() override; @@ -84,7 +157,12 @@ class RawToCellConverterSpec : public framework::Task mMaxErrorMessages = maxMessages; } - void setNoiseThreshold(int thresold) { mNoiseThreshold = thresold; } + /// \brief Set noise threshold for gain type errors + /// \param threshold Noise threshold + void setNoiseThreshold(int threshold) { mNoiseThreshold = threshold; } + + /// \brief Get the noise threshold for gain type errors + /// \return Noise threshold int getNoiseThreshold() const { return mNoiseThreshold; } /// \brief Set ID of the subspecification @@ -166,6 +244,32 @@ class RawToCellConverterSpec : public framework::Task int mRowShifted = -1; /// << shifted row of the module (cell-case) }; + /// \struct CellTimeCorrection + /// \brief Correction for cell time + struct CellTimeCorrection { + double mTimeShift; ///< Constant time shift + int mBcMod4; ///< BC-dependent shift + + /// \brief Get the corrected cell time + /// \param rawtime Raw time from fit + /// \return Corrected time + /// + /// The time is corrected for an average shift and the BC phase + double getCorrectedTime(double rawtime) const { return rawtime - mTimeShift - 25. * mBcMod4; } + }; + + /// \struct LocalPosition + /// \brief Position in the supermodule coordinate system + struct LocalPosition { + uint16_t mSupermoduleID; ///< Supermodule ID + uint16_t mFeeID; ///< FEE ID + uint8_t mColumn; ///< Column in supermodule + uint8_t mRow; ///< Row in supermodule + }; + + using TRUContainer = std::vector; + using PatchContainer = std::vector; + /// \brief Check if the timeframe is empty /// \param ctx Processing context of timeframe /// \return True if the timeframe is empty, false otherwise @@ -189,14 +293,12 @@ class RawToCellConverterSpec : public framework::Task int bookEventCells(const gsl::span& cells, bool isLELDMON); /// \brief Send data to output channels - /// \param cells Container with output cells for timeframe - /// \param triggers Container with trigger records for timeframe - /// \param decodingErrors Container with decoding errors for timeframe + /// \param ctx target processing context /// /// Send data to all output channels for the given subspecification. The subspecification /// is determined on the fly in the run method and therefore used as parameter. Consumers /// must use wildcard subspecification via ConcreteDataTypeMatcher. - void sendData(framework::ProcessingContext& ctx, const std::vector& cells, const std::vector& triggers, const std::vector& decodingErrors) const; + void sendData(framework::ProcessingContext& ctx) const; /// \brief Get absolute Cell ID from column/row in supermodule /// \param supermoduleID Index of the supermodule @@ -213,25 +315,191 @@ class RawToCellConverterSpec : public framework::Task /// \throw ModuleIndexException in case of invalid module indices int geLEDMONAbsID(int supermoduleID, int module); + /// \brief Add FEE channel to the current evnet + /// \param currentEvent Event to add the channel to + /// \param currentchannel Current FEE channel + /// \param timeCorrector Handler for correction of the time + /// \param position Channel coordinates + /// \param chantype Channel type (High Gain, Low Gain, LEDMON) + /// + /// Performing a raw fit of the bunches in the channel to extract energy and time, and + /// adding them to the container for FEE data of the given event. + void addFEEChannelToEvent(o2::emcal::EventContainer& currentEvent, const o2::emcal::Channel& currentchannel, const CellTimeCorrection& timeCorrector, const LocalPosition& position, ChannelType_t chantype); + + /// \brief Add TRU channel to the event + /// \param currentEvent Event to add the channel to + /// \param currentchannel Current TRU channel + /// \param position Channel coordinates + /// + /// TRU channels are encoded in colums: + /// - 0-95: FastOR timeseries (time-reversed) + /// - 96-105: bitmap with fired patches and TRU header + /// The TRU index is taken from the hardware address, while the FastOR index is taken from the + /// column number. The TRU and patch times are taken from the time sample in which the corresponding + /// bit is found. + void addTRUChannelToEvent(o2::emcal::EventContainer& currentEvent, const o2::emcal::Channel& currentchannel, const LocalPosition& position); + + /// @brief Build L0 patches from FastOR time series and TRU data of the current event + /// @param currentevent Current event to process + /// @return Compressed patches + /// + /// Only reconstruct patches which were decoded as fired from the raw data. The patch energy and time + /// are calculated from the corresponding FastOR time series as the energy and time with the largest + /// patch energy extracted from all possible time integrals (see reconstructTriggerPatch) + std::tuple buildL0Patches(const EventContainer& currentevent) const; + + /// @brief Build L0 timesums with respect to a given L0 time + /// @param currentevent Current event with FastOR time series + /// @param l0time L0 time of the event + /// @return Container with time series + std::vector buildL0Timesums(const o2::emcal::EventContainer& currentevent, uint8_t l0time) const; + + /// \brief Reconstruct trigger patch energy and time from its FastOR time series + /// \param fastors FastORs contributing to the patch (only present) + /// \return Tuple with 0 - patch ADC, 1 - patch time + /// + /// For all possible combinations reconstruct the 4-time integral of the patches from + /// its contributing FastORs. The patch is reconstructed when the ADC reached its + /// maximum. The patch time is the start time of the 4-integral + std::tuple reconstructTriggerPatch(const gsl::span fastors) const; + + /// \brief Handling of mapper hardware address errors + /// \param error Exception raised by the mapper + /// \param ddlID DDL ID of the segment raising the exception + /// \param hwaddress Hardware address raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleAddressError(const Mapper::AddressNotFoundException& error, int ddlID, int hwaddress); + /// \brief Handler function for major ALTRO decoder errors + /// \param altroerror Exception raised by the ALTRO decoder + /// \param ddlID DDL ID of the segment raising the exception + /// \param hwaddress Hardware address raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleAltroError(const o2::emcal::AltroDecoderError& altroerror, int ddlID); + /// \brief Handler function for minor ALTRO errors + /// \param altroerror Minor errors created by the ALTRO decoder + /// \param ddlID DDL ID of the segment raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleMinorAltroError(const o2::emcal::MinorAltroDecodingError& altroerror, int ddlID); + /// \brief Handler function of mapper errors related to invalid DDL + /// \param error Exception raised by the mapper + /// \param feeID FEE ID (DDL ID) of the segment raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleDDLError(const MappingHandler::DDLInvalid& error, int feeID); - void handleGeometryError(const ModuleIndexException& e, int supermoduleID, int cellID, int hwaddress, ChannelType_t chantype); - + /// \brief Handler function of errors related to geometry (invalid supermodule / module/ tower ...) + /// \param error Geometry exception + /// \param supermoduleID Supermodule ID of the exception + /// \param cellID Cell (Tower) ID of the exception + /// \param hwaddress Hardware address raising the exception + /// \param chantype Channel type of the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handleGeometryError(const ModuleIndexException& error, int supermoduleID, int cellID, int hwaddress, ChannelType_t chantype); + + /// \brief Handler function of mapper errors related to invalid DDL + /// \param error Exception raised by the mapper + /// \param feeID FEE ID (DDL ID) of the segment raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleFitError(const o2::emcal::CaloRawFitter::RawFitterError_t& fiterror, int ddlID, int cellID, int hwaddress); - /// \brief handler function for gain type errors + /// \brief Handler function for gain type errors /// \param errortype Gain error type - /// \param ddlID ID of the DDL - /// \param hwaddress Hardware address + /// \param ddlID DDL ID of the segment raising the exception + /// \param hwaddress Hardware address raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. void handleGainError(const o2::emcal::reconstructionerrors::GainError_t& errortype, int ddlID, int hwaddress); - void handlePageError(const RawDecodingError& e); + /// \brief Handler function for raw page decoding errors (i.e. header/trailer corruptions) + /// \param error Raw page error + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handlePageError(const RawDecodingError& error); + + /// \brief Handler function for minor raw page decoding errors (i.e. header/trailer corruptions) + /// \param error Raw page error + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handleMinorPageError(const RawReaderMemory::MinorError& error); + + /// \brief Handler function for FastOR indexing errors + /// \param error FastOR index error + /// \param linkID DDL raising the exception + /// \param indexTRU TRU raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handleFastORErrors(const FastORIndexException& error, unsigned int linkID, unsigned int indexTRU); + + /// \brief Handler function for FastOR start time errors + /// \param error FastOR index error + /// \param linkID DDL raising the exception + /// \param indexTRU TRU raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handleFastORStartTimeErrors(const FastOrStartTimeInvalidException& e, unsigned int linkID, unsigned int indexTRU); + + /// \brief Handler function patch index exception + /// \param error Patch index error + /// \param linkID DDL raising the exception + /// \param indexTRU TRU raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handlePatchError(const TRUDataHandler::PatchIndexException& error, unsigned int linkID, unsigned int indexTRU); + + /// \brief Handler function for TRU index exception + /// \param error TRU index error + /// \param linkID DDL raising the exception + /// \param hwaddress Hardware address of the channel raising the exception + /// + /// Errors are printed to the infoLogger until a user-defiened + /// threshold is reached. In case the export of decoder errors + /// is activated an error object with additional information is + /// produced. + void handleTRUIndexError(const TRUIndexException& error, unsigned int linkID, unsigned int hwaddress); header::DataHeader::SubSpecificationType mSubspecification = 0; ///< Subspecification for output channels int mNoiseThreshold = 0; ///< Noise threshold in raw fit @@ -239,30 +507,42 @@ class RawToCellConverterSpec : public framework::Task int mErrorMessagesSuppressed = 0; ///< Counter of suppressed error messages int mMaxErrorMessages = 100; ///< Max. number of error messages bool mMergeLGHG = true; ///< Merge low and high gain cells + bool mActiveLinkCheck = true; ///< Run check for active links bool mPrintTrailer = false; ///< Print RCU trailer bool mDisablePedestalEvaluation = false; ///< Disable pedestal evaluation independent of settings in the RCU trailer bool mCreateRawDataErrors = false; ///< Create raw data error objects for monitoring + bool mDoTriggerReconstruction = false; ///< Do trigger reconstruction std::chrono::time_point mReferenceTime; ///< Reference time for muting messages Geometry* mGeometry = nullptr; ///! mCalibHandler; ///< Handler for calibration objects std::unique_ptr mMapper = nullptr; ///! mTriggerMapping; ///! mRawFitter; ///! mOutputCells; ///< Container with output cells - std::vector mOutputTriggerRecords; ///< Container with output cells + std::vector mOutputTriggerRecords; ///< Container with output trigger records for cells std::vector mOutputDecoderErrors; ///< Container with decoder errors + std::vector mOutputTRUs; ///< Compressed output TRU information + std::vector mOutputTRUTriggerRecords; ///< Container with trigger records for TRU data + std::vector mOutputPatches; ///< Compressed trigger patch information + std::vector mOutputPatchTriggerRecords; ///< Container with trigger records for Patch data + std::vector mOutputTimesums; ///< Compressed L0 timesum information + std::vector mOutputTimesumTriggerRecords; ///< Trigger records for L0 timesum }; /// \brief Creating DataProcessorSpec for the EMCAL Cell Converter Spec /// \param askDISTSTF Include input spec FLP/DISTSUBTIMEFRAME -/// \param loadRecoParamsFromCCDB Obtain reco params from the CCDB +/// \param disableDecodingErrors Obtain reco params from the CCDB +/// \param disableTriggerReconstruction Do not run trigger reconstruction /// \param subspecification Subspecification used in the output spec /// /// Refer to RawToCellConverterSpec::run for input and output specs -framework::DataProcessorSpec getRawToCellConverterSpec(bool askDISTSTF, bool disableDecodingError, int subspecification); +framework::DataProcessorSpec getRawToCellConverterSpec(bool askDISTSTF, bool disableDecodingError, bool disableTriggerReconstruction, int subspecification); } // namespace reco_workflow } // namespace emcal } // namespace o2 + +#endif // O2_EMCAL_RAWTOCELLCONVERTER_SPEC diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h index a41b5c98cf661..909e356297095 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RecoWorkflow.h @@ -48,22 +48,27 @@ enum struct OutputType { Digits, ///< EMCAL digits /// \param propagateMC If true MC labels are propagated to the output files /// \param askDISTSTF If true the Raw->Cell converter subscribes to FLP/DISTSUBTIMEFRAME /// \param enableDigitsPrinter If true then the simple digits printer is added as dummy task -/// \param subspecification Subspecification in case of running on different FLPs +/// \param subspecificationIn Subspecification of input in case of running on different FLPs +/// \param subspecificationOut Subspecification if output in case of running on different FLPs /// \param cfgInput Input objects processed in the workflow /// \param cfgOutput Output objects created in the workflow -/// \param loadRecoParamsFromCCDB Load the reco params from the CCDB +/// \param disableRootInput Disable reading from ROOT file (raw mode) +/// \param disableRootOutput Disable writing ROOT files (sync reco) +/// \param disableDecodingErrors Diable streaming raw decoding errors (async reco) +/// \param disableTriggerReconstruction Disable trigger reconstrction /// \return EMCAL reconstruction workflow for the configuration provided /// \ingroup EMCALwokflow framework::WorkflowSpec getWorkflow(bool propagateMC = true, bool askDISTSTF = true, bool enableDigitsPrinter = false, - int subspecification = 0, + int subspecificationIn = 0, + int subspecificationOut = 0, std::string const& cfgInput = "digits", std::string const& cfgOutput = "clusters", bool disableRootInput = false, bool disableRootOutput = false, bool disableDecodingErrors = false, - bool useccdb = false); + bool disableTriggerReconstruction = false); } // namespace reco_workflow } // namespace emcal diff --git a/Detectors/EMCAL/workflow/src/AnalysisClusterSpec.cxx b/Detectors/EMCAL/workflow/src/AnalysisClusterSpec.cxx index 2c98a100ac17a..7b81fec681c14 100644 --- a/Detectors/EMCAL/workflow/src/AnalysisClusterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/AnalysisClusterSpec.cxx @@ -148,7 +148,7 @@ void AnalysisClusterSpec::run(framework::ProcessingContext& ctx) } LOG(debug) << "[EMCALClusterizer - run] Writing " << mOutputAnaClusters->size() << " clusters ..."; - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "ANALYSISCLUSTERS", 0, o2::framework::Lifetime::Timeframe}, *mOutputAnaClusters); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "ANALYSISCLUSTERS", 0}, *mOutputAnaClusters); } o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getAnalysisClusterSpec(bool useDigits) diff --git a/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx b/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx index 20aa636d849d1..5fa7353e907e2 100644 --- a/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/CellConverterSpec.cxx @@ -53,10 +53,8 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) { LOG(debug) << "[EMCALCellConverter - run] called"; - if (mLoadRecoParamFromCCDB) { - // for reading the reco params from the ccdb - ctx.inputs().get("EMC_RecoParam"); - } + mCalibHandler->checkUpdates(ctx); + updateCalibrationObjects(); double timeshift = RecoParam::Instance().getCellTimeShiftNanoSec(); // subtract offset in ns in order to center the time peak around the nominal delay timeshift = int(timeshift / 100) * 100; // This is a cheat to make the time multiple of 100, since the digitizer takes the delay only as mutiple of 100 @@ -163,10 +161,25 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) ncellsTrigger = 0; } LOG(debug) << "[EMCALCellConverter - run] Writing " << mOutputCells.size() << " cells ..."; - ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLS", mSubspecification, o2::framework::Lifetime::Timeframe}, mOutputCells); - ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLSTRGR", mSubspecification, o2::framework::Lifetime::Timeframe}, mOutputTriggers); + ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLS", mSubspecificationOut}, mOutputCells); + ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLSTRGR", mSubspecificationOut}, mOutputTriggers); if (mPropagateMC) { - ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLSMCTR", mSubspecification, o2::framework::Lifetime::Timeframe}, mOutputLabels); + ctx.outputs().snapshot(o2::framework::Output{"EMC", "CELLSMCTR", mSubspecificationOut}, mOutputLabels); + } +} + +void CellConverterSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +{ + if (mCalibHandler->finalizeCCDB(matcher, obj)) { + return; + } +} + +void CellConverterSpec::updateCalibrationObjects() +{ + if (mCalibHandler->hasUpdateRecoParam()) { + LOG(info) << "RecoParams updated"; + o2::emcal::RecoParam::Instance().printKeyValues(true, true); } } @@ -441,33 +454,25 @@ int CellConverterSpec::selectMaximumBunch(const gsl::span& bunchvec return bunchindex; } -void CellConverterSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) -{ - if (matcher == o2::framework::ConcreteDataMatcher("EMC", "RecoParam", 0)) { - LOG(info) << "EMCal RecoParam updated"; - return; - } -} - -o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getCellConverterSpec(bool propagateMC, bool useccdb, int outputSubspec) +o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getCellConverterSpec(bool propagateMC, int inputSubspec, int outputSubspec) { std::vector inputs; std::vector outputs; - inputs.emplace_back("digits", o2::header::gDataOriginEMC, "DIGITS", 0, o2::framework::Lifetime::Timeframe); - if (useccdb) { - inputs.emplace_back("EMC_RecoParam", o2::header::gDataOriginEMC, "RECOPARAM", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("EMC/Config/RecoParam")); - } - inputs.emplace_back("triggers", "EMC", "DIGITSTRGR", 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); + inputs.emplace_back("digits", o2::header::gDataOriginEMC, "DIGITS", inputSubspec, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("triggers", "EMC", "DIGITSTRGR", inputSubspec, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("EMC", "CELLS", outputSubspec, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("EMC", "CELLSTRGR", outputSubspec, o2::framework::Lifetime::Timeframe); if (propagateMC) { - inputs.emplace_back("digitsmctr", "EMC", "DIGITSMCTR", outputSubspec, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("digitsmctr", "EMC", "DIGITSMCTR", inputSubspec, o2::framework::Lifetime::Timeframe); outputs.emplace_back("EMC", "CELLSMCTR", outputSubspec, o2::framework::Lifetime::Timeframe); } + auto calibhandler = std::make_shared(); + calibhandler->enableRecoParams(true); + calibhandler->defineInputSpecs(inputs); return o2::framework::DataProcessorSpec{"EMCALCellConverterSpec", inputs, outputs, - o2::framework::adaptFromTask(propagateMC, useccdb, outputSubspec), + o2::framework::adaptFromTask(propagateMC, inputSubspec, outputSubspec, calibhandler), o2::framework::Options{ {"fitmethod", o2::framework::VariantType::String, "gamma2", {"Fit method (standard or gamma2)"}}}}; } diff --git a/Detectors/EMCAL/workflow/src/CellRecalibratorSpec.cxx b/Detectors/EMCAL/workflow/src/CellRecalibratorSpec.cxx index 493c205223161..3f19b49cd73bd 100644 --- a/Detectors/EMCAL/workflow/src/CellRecalibratorSpec.cxx +++ b/Detectors/EMCAL/workflow/src/CellRecalibratorSpec.cxx @@ -113,15 +113,15 @@ void CellRecalibratorSpec::run(framework::ProcessingContext& ctx) } // send recalibrated objects - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLS", mOutputSubspec, o2::framework::Lifetime::Timeframe}, outputcells); - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSTRGR", mOutputSubspec, o2::framework::Lifetime::Timeframe}, outputtriggers); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLS", mOutputSubspec}, outputcells); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSTRGR", mOutputSubspec}, outputtriggers); if (outputMCLabels.has_value()) { LOG(info) << "Timeframe: " << inputMCLabels->getIndexedSize() << " label entries read, " << outputMCLabels->getIndexedSize() << " label entries kept"; - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSMCTR", mOutputSubspec, o2::framework::Lifetime::Timeframe}, outputMCLabels.value()); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSMCTR", mOutputSubspec}, outputMCLabels.value()); } if (mLEDsettings == LEDEventSettings::REDIRECT) { - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLS", 10, o2::framework::Lifetime::Timeframe}, ledcells); - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSTRGR", 10, o2::framework::Lifetime::Timeframe}, ledtriggers); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLS", 10}, ledcells); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CELLSTRGR", 10}, ledtriggers); } } diff --git a/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx b/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx index eaa00d6d3d0b6..f938d02ce7e3f 100644 --- a/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/ClusterizerSpec.cxx @@ -107,11 +107,11 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) currentStartIndices = mOutputCellDigitIndices->size(); } LOG(debug) << "[EMCALClusterizer - run] Writing " << mOutputClusters->size() << " clusters ..."; - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, *mOutputClusters); - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "INDICES", 0, o2::framework::Lifetime::Timeframe}, *mOutputCellDigitIndices); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CLUSTERS", 0}, *mOutputClusters); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "INDICES", 0}, *mOutputCellDigitIndices); - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CLUSTERSTRGR", 0, o2::framework::Lifetime::Timeframe}, *mOutputTriggerRecord); - ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "INDICESTRGR", 0, o2::framework::Lifetime::Timeframe}, *mOutputTriggerRecordIndices); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "CLUSTERSTRGR", 0}, *mOutputTriggerRecord); + ctx.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginEMC, "INDICESTRGR", 0}, *mOutputTriggerRecordIndices); mTimer.Stop(); } diff --git a/Detectors/EMCAL/workflow/src/EMCALDigitizerSpec.cxx b/Detectors/EMCAL/workflow/src/EMCALDigitizerSpec.cxx index 25ba397819e7c..cabdb2c74d818 100644 --- a/Detectors/EMCAL/workflow/src/EMCALDigitizerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EMCALDigitizerSpec.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "EMCALWorkflow/CalibLoader.h" #include "EMCALWorkflow/EMCALDigitizerSpec.h" #include "CommonConstants/Triggers.h" #include "Framework/CCDBParamSpec.h" @@ -24,8 +25,14 @@ #include #include "CommonDataFormat/EvIndex.h" +#include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsCTP/Configuration.h" +#include "DataFormatsCTP/Digits.h" #include "DataFormatsEMCAL/TriggerRecord.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFV0/Digit.h" +#include using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; @@ -46,11 +53,17 @@ void DigitizerSpec::initDigitizerTask(framework::InitContext& ctx) // init digitizer mSumDigitizer.setGeometry(geom); + mSumDigitizerTRU.setGeometry(geom); + mDigitizerTRU.setGeometry(geom); if (ctx.options().get("debug-stream")) { mDigitizer.setDebugStreaming(true); + mDigitizerTRU.setDebugStreaming(true); } // mDigitizer.init(); + if (ctx.options().get("disable-dig-tru")) { + mRunDigitizerTRU = false; + } mFinished = false; } @@ -60,10 +73,9 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) if (mFinished) { return; } - - if (mLoadSimParamFromCCDB) { - // for reading the sim params from the ccdb - ctx.inputs().get("EMC_SimParam"); + if (mCalibHandler) { + // Load CCDB object (sim params) + mCalibHandler->checkUpdates(ctx); } if (!mIsConfigured) { @@ -74,10 +86,20 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) o2::emcal::SimParam::Instance().printKeyValues(true, true); mDigitizer.flush(); + mDigitizerTRU.flush(); // read collision context from input auto context = ctx.inputs().get("collisioncontext"); + + // get interaction rate ... so that it can be used in digitization + auto intRate = context->getDigitizerInteractionRate(); context->initSimChains(o2::detectors::DetID::EMC, mSimChains); + + // init the MCKinematicsReader from the digitization context + if (mcReader == nullptr) { + mcReader = new o2::steer::MCKinematicsReader(context.get()); + } + auto& timesview = context->getEventRecords(); LOG(debug) << "GOT " << timesview.size() << " COLLISSION TIMES"; @@ -89,20 +111,238 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) TStopwatch timer; timer.Start(); + auto& eventParts = context->getEventParts(); + + // ------------------------------ + // For the TRIGGER Simulation + // ------------------------------ + // Load the masked fastOr + // This impact the acceptance + // of the detector, and thus the + // overall efficiency of the L0 + if (mCalibHandler) { + mDigitizerTRU.setFEE(mCalibHandler->getFEEDCS()); + mDigitizerTRU.setMaskedFastOrsInLZERO(); + } + + // ------------------------------ + // TRIGGER Simulation + // ------------------------------ + // 1. Run SDigitizer separate loop -> map of vector of SDigits + // 2. Run Trigger simulation chain (Digitizer -> Digits writeout buffer -> L0 Simulation) + // 3. Run Loop SDigits -> Digits, set live only if the trigger is accepted. + // + // loop over all composite collisions given from context + // (aka loop over all the interaction records) + int collisionN = 0; + for (int collID = 0; collID < timesview.size(); ++collID) { + + if (mRunDigitizerTRU == false) { + break; + } + + if (intRate < 10000.) { + break; + } + + collisionN++; + + mDigitizerTRU.setEventTime(timesview[collID]); + + // for each collision, loop over the constituents event and source IDs + // (background signal merging is basically taking place here) + for (auto& part : eventParts[collID]) { + + mSumDigitizerTRU.setCurrEvID(part.entryID); + mSumDigitizerTRU.setCurrSrcID(part.sourceID); + + // get the hits for this event and this source + mHits.clear(); + context->retrieveHits(mSimChains, "EMCHit", part.sourceID, part.entryID, &mHits); + + // LOG(info) << "DIG TRU For collision " << collID << " eventID " << part.entryID << " found " << mHits.size() << " hits "; + + // std::vector summedLabeledDigits = mSumDigitizerTRU.process(mHits); + // std::vector summedDigits; + // for (auto labeledsummeddigit : summedLabeledDigits) { + // summedDigits.push_back(labeledsummeddigit.getDigit()); + // } + + std::vector summedLabeledDigits; + std::vector summedDigits; + if (mRunSDitizer) { + summedLabeledDigits = mSumDigitizerTRU.process(mHits); + for (auto labeledsummeddigit : summedLabeledDigits) { + summedDigits.push_back(labeledsummeddigit.getDigit()); + } + } else { + for (auto& hit : mHits) { + summedDigits.emplace_back(hit.GetDetectorID(), hit.GetEnergyLoss(), hit.GetTime()); + } + } + + // call actual digitization procedure + mDigitizerTRU.process(summedDigits); + } + } + mDigitizerTRU.printMaskedFastOrsInLZERO(); + mDigitizerTRU.finish(); + // Result of the trigger simulation + // -> Set of BCs with triggering patches + auto emcalTriggers = mDigitizerTRU.getTriggerInputs(); + + // Load FIT triggers if not running in self-triggered mode + std::vector mbtriggers; + if (mRequireCTPInput) { + // Hopefully at some point we can replace it by CTP input digits + // In case of CTP digits react only to trigger inputs activated in trigger configuration + // For the moment react to FIT vertex, cent and semicent triggers + ctx.inputs().get("ctpconfig"); + std::vector inputmasks; + for (const auto& trg : mCTPConfig->getCTPClasses()) { + if (trg.cluster->maskCluster[o2::detectors::DetID::EMC]) { + // Class triggering EMCAL cluster + LOG(debug) << "Found trigger class for EMCAL cluster: " << trg.name << " with input mask " << std::bitset<64>(trg.descriptor->getInputsMask()); + inputmasks.emplace_back(trg.descriptor->getInputsMask()); + } + } + unsigned long ft0mask = 0, fv0mask = 0; + std::map detInputName2Mask = + {{"MVBA", 1}, {"MVOR", 2}, {"MVNC", 4}, {"MVCH", 8}, {"MVIR", 0x10}, {"MT0A", 1}, {"MT0C", 2}, {"MTSC", 4}, {"MTCE", 8}, {"MTVX", 0x10}}; + // Translation 2022: EMCAL cluster received CTP input masks, need to track it to FIT trigger masks + std::map> ctpInput2DetInput = { + {"0VBA", {o2::detectors::DetID::FV0, "MVBA"}}, {"0VOR", {o2::detectors::DetID::FV0, "MVOR"}}, {"0VNC", {o2::detectors::DetID::FV0, "MVNC"}}, {"0VCH", {o2::detectors::DetID::FV0, "MVCH"}}, {"0VIR", {o2::detectors::DetID::FV0, "MVIR"}}, {"0T0A", {o2::detectors::DetID::FT0, "MT0A"}}, {"0T0C", {o2::detectors::DetID::FT0, "MT0C"}}, {"0TSC", {o2::detectors::DetID::FT0, "MTSC"}}, {"0TCE", {o2::detectors::DetID::FT0, "MTCE"}}, {"0TVX", {o2::detectors::DetID::FT0, "MTVX"}}}; + for (const auto& [det, ctpinputs] : mCTPConfig->getDet2InputMap()) { + if (!(det == o2::detectors::DetID::FT0 || det == o2::detectors::DetID::FV0 || det == o2::detectors::DetID::CTP)) { + continue; + } + for (const auto& input : ctpinputs) { + LOG(debug) << "CTP det input: " << input.name << " with mask " << std::bitset<64>(input.inputMask); + bool isSelected = false; + for (auto testmask : inputmasks) { + if (testmask & input.inputMask) { + isSelected = true; + } + } + if (isSelected) { + std::string usedInputName = input.name; + o2::detectors::DetID usedDetID = det; + if (det == o2::detectors::DetID::CTP) { + auto found = ctpInput2DetInput.find(input.name); + if (found != ctpInput2DetInput.end()) { + usedInputName = found->second.second; + usedDetID = found->second.first; + LOG(debug) << "Decoded " << input.name << " -> " << usedInputName; + } + } + auto maskFound = detInputName2Mask.find(usedInputName); + if (maskFound != detInputName2Mask.end()) { + if (usedDetID == o2::detectors::DetID::FT0) { + ft0mask |= maskFound->second; + } else { + fv0mask |= maskFound->second; + } + } + } + } + } + LOG(debug) << "FTO mask: " << std::bitset<64>(ft0mask); + LOG(debug) << "FVO mask: " << std::bitset<64>(fv0mask); + for (const auto& trg : ctx.inputs().get>("ft0inputs")) { + if (trg.mInputs.to_ulong() & ft0mask) { + mbtriggers.emplace_back(trg.mIntRecord); + } + } + for (const auto& trg : ctx.inputs().get>("fv0inputs")) { + if (trg.mInputs.to_ulong() & fv0mask) { + if (std::find(mbtriggers.begin(), mbtriggers.end(), trg.mIntRecord) == mbtriggers.end()) { + mbtriggers.emplace_back(trg.mIntRecord); + } + } + } + } + LOG(info) << " CALLING EMCAL DIGITIZATION "; o2::dataformats::MCTruthContainer labelAccum; - auto& eventParts = context->getEventParts(); + // auto& eventParts = context->getEventParts(); + std::vector>> acceptedTriggers; + enum EMCALTriggerBits { kMB, + kEMC, + kDMC }; // EMCAL trigger bits enum for CTP inputs + TRandom3 mRandomGenerator(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // 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]); + std::bitset<5> trigger{0x1}; // Default: Self-triggered mode - all collisions treated as trigger + if (mRequireCTPInput) { + // ==================================================== + // check if we have a trigger input from CTP + // Simulated L0 and downsampling + // 2 cases -> Check from CTP config + // 1) EMCAL trigger active -> MB downsampled + // - Accept the event in 2 conditions + // + MB downsampling (first condition) -> Set trigger bit kTVXinEMC + // + EMCAL L0 trigger -> Set bit EMCAL L0 + // Always test both, set trigger 0x1 -> EMBA, 0x2 -> 0EMC, 0x4 -> 0DCMX, 0 -> No trigger + // 2) EMCAL triggers not active + // . - Old logics kept, no downscaling, pure busy + // ---------------------------------------------------- + // PRNG for downscaling check + auto mbtrigger = std::find(mbtriggers.begin(), mbtriggers.end(), timesview[collID]); + if (mbtrigger != mbtriggers.end()) { + + // ============================ + // retrieve downscaling from + // configuration of CTP classes + int downscaling = 0; + for (const auto& trg : mCTPConfig->getCTPClasses()) { + if (trg.cluster->maskCluster[o2::detectors::DetID::EMC]) { + // Class triggering EMCAL cluster + downscaling = trg.downScale; + } + } + if (mRandomGenerator.Uniform(0., 1) < downscaling) { + // accept as minimum bias + trigger.set(EMCALTriggerBits::kMB, true); + } + // check for LO triggers in 12 BCs + for (auto emcalTrigger : emcalTriggers) { + auto bcTimingOfEmcalTrigger = emcalTrigger.mInterRecord.bc; + auto bcTimingOfMBTrigger = (*mbtrigger).bc; + if (std::abs(bcTimingOfEmcalTrigger - bcTimingOfMBTrigger) < 12) { + if (emcalTrigger.mTriggeredTRU < 32) { + trigger.set(EMCALTriggerBits::kEMC, true); + } else { + trigger.set(EMCALTriggerBits::kDMC, true); + } + } + } + + } else { + // No trigger active + // Set busy + trigger.set(EMCALTriggerBits::kMB, false); + } + } + // Bitset + // Trigger sim: Select event + if (!trigger.any()) { + continue; + } - if (!mDigitizer.isLive()) { + mDigitizer.setEventTime(timesview[collID], trigger.any()); + if (!mDigitizer.isCurrentEventTriggered()) { + LOG(debug) << "reject collision"; continue; } + LOG(debug) << "accept collision"; + + // Trigger sim: Prepare CTP input digit + acceptedTriggers.push_back(std::make_tuple(timesview[collID], trigger)); + LOG(debug) << "EMCAL TRU simulation: Sending trg = " << trigger << " to CTP"; // for each collision, loop over the constituents event and source IDs // (background signal merging is basically taking place here) @@ -111,13 +351,28 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) mSumDigitizer.setCurrEvID(part.entryID); mSumDigitizer.setCurrSrcID(part.sourceID); + // retrieve information about the MC collision via the MCEventHeader + auto& mcEventHeader = mcReader->getMCEventHeader(part.sourceID, part.entryID); + mcEventHeader.print(); + // get the hits for this event and this source mHits.clear(); context->retrieveHits(mSimChains, "EMCHit", part.sourceID, part.entryID, &mHits); LOG(info) << "For collision " << collID << " eventID " << part.entryID << " found " << mHits.size() << " hits "; - std::vector summedDigits = mSumDigitizer.process(mHits); + std::vector summedDigits; + if (mRunSDitizer) { + summedDigits = mSumDigitizer.process(mHits); + } else { + for (auto& hit : mHits) { + o2::emcal::MCLabel digitlabel(hit.GetTrackID(), part.entryID, part.sourceID, false, 1.); + if (hit.GetEnergyLoss() < __DBL_EPSILON__) { + digitlabel.setAmplitudeFraction(0); + } + summedDigits.emplace_back(hit.GetDetectorID(), hit.GetEnergyLoss(), hit.GetTime(), digitlabel); + } + } // call actual digitization procedure mDigitizer.process(summedDigits); @@ -127,15 +382,42 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) mDigitizer.finish(); // here we have all digits and we can send them to consumer (aka snapshot it onto output) - ctx.outputs().snapshot(Output{"EMC", "DIGITS", 0, Lifetime::Timeframe}, mDigitizer.getDigits()); - ctx.outputs().snapshot(Output{"EMC", "TRGRDIG", 0, Lifetime::Timeframe}, mDigitizer.getTriggerRecords()); + ctx.outputs().snapshot(Output{"EMC", "DIGITS", 0}, mDigitizer.getDigits()); + ctx.outputs().snapshot(Output{"EMC", "TRGRDIG", 0}, mDigitizer.getTriggerRecords()); if (ctx.outputs().isAllowed({"EMC", "DIGITSMCTR", 0})) { - ctx.outputs().snapshot(Output{"EMC", "DIGITSMCTR", 0, Lifetime::Timeframe}, mDigitizer.getMCLabels()); + ctx.outputs().snapshot(Output{"EMC", "DIGITSMCTR", 0}, mDigitizer.getMCLabels()); } // EMCAL is always a triggering detector const o2::parameters::GRPObject::ROMode roMode = o2::parameters::GRPObject::TRIGGERING; LOG(info) << "EMCAL: Sending ROMode= " << roMode << " to GRPUpdater"; - ctx.outputs().snapshot(Output{"EMC", "ROMode", 0, Lifetime::Timeframe}, roMode); + ctx.outputs().snapshot(Output{"EMC", "ROMode", 0}, roMode); + // Create CTP digits + std::vector triggerinputs; + // for (auto& trg : mDigitizer.getTriggerRecords()) { + // // covert TriggerRecord into CTP trigger digit + // o2::ctp::CTPInputDigit nextdigit; + // nextdigit.intRecord = trg.getBCData(); + // nextdigit.detector = o2::detectors::DetID::EMC; + // // Set min. bias accept trigger (input 0) as fake trigger + // // Other inputs will be added once available + // nextdigit.inputsMask.set(0); + // triggerinputs.push_back(nextdigit); + // } + for (auto& trg : acceptedTriggers) { + // convert TriggerRecord into CTP trigger digit + o2::ctp::CTPInputDigit nextdigit; + nextdigit.intRecord = std::get<0>(trg); + nextdigit.detector = o2::detectors::DetID::EMC; + // nextdigit.inputsMask = std::get<1>(trg); + for (size_t i = 0; i < nextdigit.inputsMask.size(); i++) { + if (std::get<1>(trg)[i] != 0) { + nextdigit.inputsMask.set(i); + } + } + LOG(debug) << "EMCAL TRU simulation: assigned = " << nextdigit.inputsMask << " as nextdigit for CTP, with IR = " << nextdigit.intRecord.bc << ", orbit = " << nextdigit.intRecord.orbit; + triggerinputs.push_back(nextdigit); + } + ctx.outputs().snapshot(Output{"EMC", "TRIGGERINPUT", 0}, triggerinputs); timer.Stop(); LOG(info) << "Digitization took " << timer.CpuTime() << "s"; @@ -147,17 +429,38 @@ void DigitizerSpec::run(framework::ProcessingContext& ctx) void DigitizerSpec::configure() { mDigitizer.init(); + mDigitizerTRU.init(); } void DigitizerSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher("EMC", "SimParam", 0)) { - LOG(info) << "EMCal SimParam updated"; + if (mCalibHandler->finalizeCCDB(matcher, obj)) { return; } + if (matcher == o2::framework::ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) { + std::cout << "Loading CTP configuration" << std::endl; + mCTPConfig = reinterpret_cast(obj); + for (const auto& trg : mCTPConfig->getCTPClasses()) { + if (trg.cluster->maskCluster[o2::detectors::DetID::EMC]) { + // Class triggering EMCAL cluster + LOG(debug) << "ENABLING Trigger simulation, found trigger class for EMCAL cluster: " << trg.name << " with input mask " << std::bitset<64>(trg.descriptor->getInputsMask()); + for (const auto& [det, ctpinputs] : mCTPConfig->getDet2InputMap()) { + if (!(det == o2::detectors::DetID::EMC)) { + continue; + } + // if the detector ID is EMC AND the input mask is not empty, run the trigger simulation + for (const auto& input : ctpinputs) { + if (input.inputMask != 0) { + mRunDigitizerTRU = true; + } + } + } + } + } + } } -o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool mctruth, bool useccdb) +o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool requireCTPInput, bool mctruth, bool useccdb) { // create the full data processor spec using // a name identifier @@ -171,20 +474,31 @@ o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel, bool mctruth outputs.emplace_back("EMC", "DIGITSMCTR", 0, Lifetime::Timeframe); } outputs.emplace_back("EMC", "ROMode", 0, Lifetime::Timeframe); + outputs.emplace_back("EMC", "TRIGGERINPUT", 0, Lifetime::Timeframe); std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); + std::shared_ptr calibloader; if (useccdb) { - inputs.emplace_back("EMC_SimParam", o2::header::gDataOriginEMC, "SIMPARAM", 0, Lifetime::Condition, ccdbParamSpec("EMC/Config/SimParam")); + calibloader = std::make_shared(); + calibloader->enableSimParams(true); + calibloader->enableFEEDCS(true); + calibloader->defineInputSpecs(inputs); + } + if (requireCTPInput) { + inputs.emplace_back("ft0inputs", "FT0", "TRIGGERINPUT", 0, Lifetime::Timeframe); + inputs.emplace_back("fv0inputs", "FV0", "TRIGGERINPUT", 0, Lifetime::Timeframe); + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", true)); } return DataProcessorSpec{ "EMCALDigitizer", // Inputs{InputSpec{"collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe}, InputSpec{"EMC_SimParam", o2::header::gDataOriginEMC, "SIMPARAM", 0, Lifetime::Condition, ccdbParamSpec("EMC/Config/SimParam")}}, inputs, outputs, - AlgorithmSpec{o2::framework::adaptFromTask(useccdb)}, + AlgorithmSpec{o2::framework::adaptFromTask(calibloader, requireCTPInput)}, Options{ {"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}, + {"disable-dig-tru", VariantType::Bool, false, {"Disable TRU digitisation"}}, {"debug-stream", VariantType::Bool, false, {"Enable debug streaming"}}} // I can't use VariantType::Bool as it seems to have a problem }; diff --git a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx index afaafadb2a790..ecc0e45492bea 100644 --- a/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyDecoderSpec.cxx @@ -24,13 +24,13 @@ namespace o2 { namespace emcal { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder), mSSpecOut(sspecOut) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, unsigned int sspecOut, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt), mSSpecOut(sspecOut) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_EMC"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -51,8 +51,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_EMC"); auto& triggers = pc.outputs().make>(OutputRef{"triggers", mSSpecOut}); auto& cells = pc.outputs().make>(OutputRef{"cells", mSSpecOut}); @@ -73,7 +73,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, unsigned int sspecOut, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "EMC", "CELLSTRGR", sspecOut, Lifetime::Timeframe}, @@ -81,17 +81,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspecInp, un OutputSpec{{"ctfrep"}, "EMC", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "EMC", "CTFDATA", sspecInp, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionary")); + inputs.emplace_back("ctf_EMC", "EMC", "CTFDATA", sspecInp, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_EMC", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "emcal-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity, sspecOut)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, sspecOut, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx index b7e40f71603cb..2928a71a167bc 100644 --- a/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/EMCAL/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace emcal { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,11 +47,11 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto triggers = pc.inputs().get>("triggers"); auto cells = pc.inputs().get>("cells"); - auto& buffer = pc.outputs().make>(Output{"EMC", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"EMC", "CTFDATA", 0}); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "EMC", "CELLSTRGR", 0, Lifetime::Timeframe); inputs.emplace_back("cells", "EMC", "CELLS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "EMC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("EMC/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,13 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"EMC", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "EMC", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{ - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, - {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace emcal } // namespace o2 diff --git a/Detectors/EMCAL/workflow/src/OfflineCalibSpec.cxx b/Detectors/EMCAL/workflow/src/OfflineCalibSpec.cxx index fc0af12d46b62..4ab574793876d 100644 --- a/Detectors/EMCAL/workflow/src/OfflineCalibSpec.cxx +++ b/Detectors/EMCAL/workflow/src/OfflineCalibSpec.cxx @@ -18,6 +18,7 @@ #include "Framework/ControlService.h" #include "Framework/DataRefUtils.h" +#include "Framework/CCDBParamSpec.h" #include "CommonConstants/Triggers.h" #include "DataFormatsEMCAL/Cell.h" #include "DataFormatsEMCAL/TriggerRecord.h" @@ -49,8 +50,37 @@ void OfflineCalibSpec::init(o2::framework::InitContext& ctx) void OfflineCalibSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { LOG(info) << "Handling new Calibration objects"; - if (mCalibrationHandler->finalizeCCDB(matcher, obj)) { - return; + mCalibrationHandler->finalizeCCDB(matcher, obj); + + if (matcher == o2::framework::ConcreteDataMatcher("EMC", "EMCALCALIBPARAM", 0)) { + LOG(info) << "EMCal CalibParams updated"; + EMCALCalibParams::Instance().printKeyValues(true, true); + } + + if (mRejectL0Triggers && matcher == o2::framework::ConcreteDataMatcher("CTP", "CTPCONFIG", 0)) { + // clear current class mask and prepare to fill in the updated values + // The trigger names are seperated by a ":" in one string in the calib params + mSelectedClassMasks.clear(); + std::string strSelClassMasks = EMCALCalibParams::Instance().selectedClassMasks; + std::string delimiter = ":"; + size_t pos = 0; + std::vector vSelMasks; + while ((pos = strSelClassMasks.find(delimiter)) != std::string::npos) { + vSelMasks.push_back(strSelClassMasks.substr(0, pos)); + strSelClassMasks.erase(0, pos + delimiter.length()); + } + vSelMasks.push_back(strSelClassMasks); + + auto ctpconf = reinterpret_cast(obj); + + for (auto& cls : ctpconf->getCTPClasses()) { + LOG(debug) << "CTP class: " << cls.name << "\t " << cls.classMask; + + if (std::find(vSelMasks.begin(), vSelMasks.end(), cls.name) != vSelMasks.end()) { + mSelectedClassMasks.push_back(cls.classMask); + LOG(info) << "Setting selected class mask " << cls.name << " to bit " << cls.classMask; + } + } } } @@ -66,6 +96,18 @@ void OfflineCalibSpec::run(framework::ProcessingContext& pc) { auto cells = pc.inputs().get>("cells"); auto triggerrecords = pc.inputs().get>("triggerrecord"); + + // prepare CTPConfiguration such that it can be loaded in finalise ccdb + if (mRejectL0Triggers) { + pc.inputs().get(getCTPConfigBinding()); + } + + using ctpDigitsType = std::decay_t>(getCTPDigitsBinding()))>; + std::optional ctpDigits; + if (mRejectL0Triggers) { + ctpDigits = pc.inputs().get>(getCTPDigitsBinding()); + } + mCalibrationHandler->checkUpdates(pc); updateCalibObjects(); @@ -86,6 +128,35 @@ void OfflineCalibSpec::run(framework::ProcessingContext& pc) } } + // reject all triggers that are not included in the classMask (typically only EMC min. bias should be accepted) + uint64_t classMaskCTP = 0; + if (mRejectL0Triggers) { + bool acceptEvent = false; + // Match the EMCal bc to the CTP bc + int64_t bcEMC = trg.getBCData().toLong(); + for (auto& ctpDigit : *ctpDigits) { + int64_t bcCTP = ctpDigit.intRecord.toLong(); + LOG(debug) << "bcEMC " << bcEMC << " bcCTP " << bcCTP; + if (bcCTP == bcEMC) { + // obtain trigger mask that belongs to the selected bc + classMaskCTP = ctpDigit.CTPClassMask.to_ulong(); + // now check if min bias trigger is not in mask + for (const uint64_t& selectedClassMask : mSelectedClassMasks) { + if ((classMaskCTP & selectedClassMask) != 0) { + LOG(debug) << "trigger " << selectedClassMask << " found! accepting event"; + acceptEvent = true; + break; + } + } + break; // break as bc was matched + } + } + // if current event is not accepted (selected triggers not present), move on to next event + if (!acceptEvent) { + continue; + } + } + LOG(debug) << "[EMCALOfflineCalib - run] Trigger has " << trg.getNumberOfObjects() << " cells ..." << std::endl; gsl::span objectsTrigger(cells.data() + trg.getFirstEntry(), trg.getNumberOfObjects()); for (const auto& c : objectsTrigger) { @@ -133,13 +204,17 @@ void OfflineCalibSpec::endOfStream(o2::framework::EndOfStreamContext& ec) outputFile->Close(); } -o2::framework::DataProcessorSpec o2::emcal::getEmcalOfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, uint32_t inputsubspec, bool enableGainCalib) +o2::framework::DataProcessorSpec o2::emcal::getEmcalOfflineCalibSpec(bool makeCellIDTimeEnergy, bool rejectCalibTriggers, bool rejectL0Trigger, uint32_t inputsubspec, bool enableGainCalib, bool ctpcfgperrun) { std::vector inputs = {{"cells", o2::header::gDataOriginEMC, "CELLS", inputsubspec, o2::framework::Lifetime::Timeframe}, {"triggerrecord", o2::header::gDataOriginEMC, "CELLSTRGR", inputsubspec, o2::framework::Lifetime::Timeframe}}; + if (rejectL0Trigger) { + inputs.emplace_back(OfflineCalibSpec::getCTPConfigBinding(), "CTP", "CTPCONFIG", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/Config", ctpcfgperrun)); + inputs.emplace_back(OfflineCalibSpec::getCTPDigitsBinding(), "CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe); + } auto calibhandler = std::make_shared(); calibhandler->enableGainCalib(enableGainCalib); calibhandler->defineInputSpecs(inputs); @@ -147,5 +222,5 @@ o2::framework::DataProcessorSpec o2::emcal::getEmcalOfflineCalibSpec(bool makeCe return o2::framework::DataProcessorSpec{"EMCALOfflineCalib", inputs, {}, - o2::framework::adaptFromTask(makeCellIDTimeEnergy, rejectCalibTriggers, calibhandler)}; + o2::framework::adaptFromTask(makeCellIDTimeEnergy, rejectCalibTriggers, rejectL0Trigger, calibhandler)}; } diff --git a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx index bc7e574080cd3..f2acc370a1bdc 100644 --- a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -31,6 +32,8 @@ #include "DetectorsRaw/RDHUtils.h" #include "EMCALBase/Geometry.h" #include "EMCALBase/Mapper.h" +#include "EMCALBase/TriggerMappingErrors.h" +#include "EMCALCalib/FeeDCS.h" #include "EMCALReconstruction/CaloFitResults.h" #include "EMCALReconstruction/Bunch.h" #include "EMCALReconstruction/CaloRawFitterStandard.h" @@ -74,6 +77,10 @@ void RawToCellConverterSpec::init(framework::InitContext& ctx) LOG(error) << "Failed to initialize mapper"; } + if (!mTriggerMapping) { + mTriggerMapping = std::make_unique(mGeometry); + } + auto fitmethod = ctx.options().get("fitmethod"); if (fitmethod == "standard") { LOG(info) << "Using standard raw fitter"; @@ -93,8 +100,11 @@ void RawToCellConverterSpec::init(framework::InitContext& ctx) mMergeLGHG = !ctx.options().get("no-mergeHGLG"); mDisablePedestalEvaluation = ctx.options().get("no-evalpedestal"); + mActiveLinkCheck = !ctx.options().get("no-checkactivelinks"); LOG(info) << "Running gain merging mode: " << (mMergeLGHG ? "yes" : "no"); + LOG(info) << "Checking for active links: " << (mActiveLinkCheck ? "yes" : "no"); + LOG(info) << "Calculate pedestals: " << (mDisablePedestalEvaluation ? "no" : "yes"); LOG(info) << "Using L0LM delay: " << o2::ctp::TriggerOffsetsParam::Instance().LM_L0 << " BCs"; mRawFitter->setAmpCut(mNoiseThreshold); @@ -107,7 +117,11 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) mCalibHandler->checkUpdates(ctx); updateCalibrationObjects(); - double timeshift = RecoParam::Instance().getCellTimeShiftNanoSec(); // subtract offset in ns in order to center the time peak around the nominal delay + // container with BCid and feeID + std::unordered_map> bcFreq; + + double timeshift = RecoParam::Instance().getCellTimeShiftNanoSec(); // subtract offset in ns in order to center the time peak around the nominal delay + auto maxBunchLengthRP = RecoParam::Instance().getMaxAllowedBunchLength(); // exclude bunches where either the start time or the bunch length is above the expected maximum constexpr auto originEMC = o2::header::gDataOriginEMC; constexpr auto descRaw = o2::header::gDataDescriptionRawData; @@ -123,9 +137,15 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) mOutputCells.clear(); mOutputTriggerRecords.clear(); mOutputDecoderErrors.clear(); + mOutputTRUs.clear(); + mOutputTRUTriggerRecords.clear(); + mOutputPatches.clear(); + mOutputPatchTriggerRecords.clear(); + mOutputTimesums.clear(); + mOutputTimesumTriggerRecords.clear(); if (isLostTimeframe(ctx)) { - sendData(ctx, mOutputCells, mOutputTriggerRecords, mOutputDecoderErrors); + sendData(ctx); return; } @@ -139,7 +159,6 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) std::vector filter{{"filter", framework::ConcreteDataTypeMatcher(originEMC, descRaw)}}; int firstEntry = 0; for (const auto& rawData : framework::InputRecordWalker(ctx.inputs(), filter)) { - // Skip SOX headers auto rdhblock = reinterpret_cast(rawData.payload); if (o2::raw::RDHUtils::getHeaderSize(rdhblock) == static_cast(o2::framework::DataRefUtils::getPayloadSize(rawData))) { @@ -155,11 +174,20 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) rawreader.next(); } catch (RawDecodingError& e) { handlePageError(e); + if (e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_DECODING || e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_INVALID) { + // We must break in case of header decoding as the offset to the next payload is lost + // consequently the parser does not know where to continue leading to an infinity loop + break; + } // We must skip the page as payload is not consistent // otherwise the next functions will rethrow the exceptions as // the page format does not follow the expected format continue; } + for (auto& e : rawreader.getMinorErrors()) { + handleMinorPageError(e); + // For minor errors we do not need to skip the page, just print and send the error to the QC + } auto& header = rawreader.getRawHeader(); auto triggerBC = raw::RDHUtils::getTriggerBC(header); @@ -181,6 +209,9 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) continue; } } + + bcFreq[currentIR.toLong()].set(feeID, true); + // Correct the cell time for the bc mod 4 (LHC: 40 MHz clock - ALTRO: 10 MHz clock) // Convention: All times shifted with respect to BC % 4 = 0 for trigger BC // Attention: Correction only works for the permutation (0 1 2 3) of the BC % 4, if the permutation is @@ -197,6 +228,7 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) if (!currentEvent.getTriggerBits()) { currentEvent.setTriggerBits(triggerbits); } + CellTimeCorrection timeCorrector{timeshift, bcmod4}; if (feeID >= 40) { continue; // skip STU ddl @@ -206,6 +238,10 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) // use the altro decoder to decode the raw data, and extract the RCU trailer AltroDecoder decoder(rawreader); + if (maxBunchLengthRP) { + // apply user-defined max. bunch length + decoder.setMaxBunchLength(maxBunchLengthRP); + } // check the words of the payload exception in altrodecoder try { decoder.decode(); @@ -245,68 +281,33 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) // Loop over all the channels int nBunchesNotOK = 0; for (auto& chan : decoder.getChannels()) { - int iRow, iCol; - ChannelType_t chantype; try { - iRow = map.getRow(chan.getHardwareAddress()); - iCol = map.getColumn(chan.getHardwareAddress()); - chantype = map.getChannelType(chan.getHardwareAddress()); + auto iRow = map.getRow(chan.getHardwareAddress()); + auto iCol = map.getColumn(chan.getHardwareAddress()); + auto chantype = map.getChannelType(chan.getHardwareAddress()); + LocalPosition channelPosition{iSM, feeID, iCol, iRow}; + switch (chantype) { + case o2::emcal::ChannelType_t::HIGH_GAIN: + case o2::emcal::ChannelType_t::LOW_GAIN: + addFEEChannelToEvent(currentEvent, chan, timeCorrector, channelPosition, chantype); + break; + case o2::emcal::ChannelType_t::LEDMON: + // Drop LEDMON reconstruction in case of physics triggers + if (triggerbits & o2::trigger::Cal) { + addFEEChannelToEvent(currentEvent, chan, timeCorrector, channelPosition, chantype); + } + break; + case o2::emcal::ChannelType_t::TRU: + addTRUChannelToEvent(currentEvent, chan, channelPosition); + break; + default: + LOG(error) << "Unknown channel type for HW address " << chan.getHardwareAddress(); + break; + } } catch (Mapper::AddressNotFoundException& ex) { handleAddressError(ex, feeID, chan.getHardwareAddress()); continue; } - - if (!(chantype == o2::emcal::ChannelType_t::HIGH_GAIN || chantype == o2::emcal::ChannelType_t::LOW_GAIN || chantype == o2::emcal::ChannelType_t::LEDMON)) { - continue; - } - - // Drop LEDMON reconstruction in case of physics triggers - if (chantype == o2::emcal::ChannelType_t::LEDMON && !(triggerbits & o2::trigger::Cal)) { - continue; - } - - int CellID = -1; - bool isLowGain = false; - try { - if (chantype == o2::emcal::ChannelType_t::HIGH_GAIN || chantype == o2::emcal::ChannelType_t::LOW_GAIN) { - // high- / low-gain cell - CellID = getCellAbsID(iSM, iCol, iRow); - isLowGain = chantype == o2::emcal::ChannelType_t::LOW_GAIN; - } else { - CellID = geLEDMONAbsID(iSM, iCol); // Module index encoded in colum for LEDMONs - isLowGain = iRow == 0; // For LEDMONs gain type is encoded in the row (0 - low gain, 1 - high gain) - } - } catch (ModuleIndexException& e) { - handleGeometryError(e, iSM, CellID, chan.getHardwareAddress(), chantype); - continue; - } - - // define the conatiner for the fit results, and perform the raw fitting using the stadnard raw fitter - CaloFitResults fitResults; - try { - fitResults = mRawFitter->evaluate(chan.getBunches()); - // Prevent negative entries - we should no longer get here as the raw fit usually will end in an error state - if (fitResults.getAmp() < 0) { - fitResults.setAmp(0.); - } - if (fitResults.getTime() < 0) { - fitResults.setTime(0.); - } - // apply correction for bc mod 4 - double celltime = fitResults.getTime() - timeshift - 25 * bcmod4; - double amp = fitResults.getAmp() * o2::emcal::constants::EMCAL_ADCENERGY; - if (isLowGain) { - amp *= o2::emcal::constants::EMCAL_HGLGFACTOR; - } - if (chantype == o2::emcal::ChannelType_t::LEDMON) { - // Mark LEDMONs as HIGH_GAIN/LOW_GAIN for gain type merging - will be flagged as LEDMON later when pushing to the output container - currentEvent.setLEDMONCell(CellID, amp, celltime, isLowGain ? o2::emcal::ChannelType_t::LOW_GAIN : o2::emcal::ChannelType_t::HIGH_GAIN, chan.getHardwareAddress(), feeID, mMergeLGHG); - } else { - currentEvent.setCell(CellID, amp, celltime, chantype, chan.getHardwareAddress(), feeID, mMergeLGHG); - } - } catch (CaloRawFitter::RawFitterError_t& fiterror) { - handleFitError(fiterror, feeID, CellID, chan.getHardwareAddress()); - } } } catch (o2::emcal::MappingHandler::DDLInvalid& ddlerror) { // Unable to catch mapping @@ -315,12 +316,54 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) } } + std::bitset<46> bitSetActiveLinks; + if (mActiveLinkCheck) { + // build expected active mask from DCS + FeeDCS* feedcs = mCalibHandler->getFEEDCS(); + auto list0 = feedcs->getDDLlist0(); + auto list1 = feedcs->getDDLlist1(); + // links 21 and 39 do not exist, but they are set active in DCS + list0.set(21, false); + list1.set(7, false); + // must be 0x307FFFDFFFFF if all links are active + bitSetActiveLinks = std::bitset<46>((list1.to_ullong() << 32) + list0.to_ullong()); + } + // Loop over BCs, sort cells with increasing tower ID and write to output containers RecoContainerReader eventIterator(mCellHandler); while (eventIterator.hasNext()) { int ncellsEvent = 0, nLEDMONsEvent = 0; int eventstart = mOutputCells.size(); auto& currentevent = eventIterator.nextEvent(); + const auto interaction = currentevent.getInteractionRecord(); + if (mActiveLinkCheck) { + // check for current event if all links are present + // discard event if not all links are present + auto bcfreqFound = bcFreq.find(interaction.toLong()); + if (bcfreqFound != bcFreq.end()) { + const auto& activelinks = bcfreqFound->second; + if (activelinks != bitSetActiveLinks) { + static int nErrors = 0; + if (nErrors++ < 3) { + LOG(error) << "Not all EMC active links contributed in global BCid=" << interaction.toLong() << ": mask=" << (activelinks ^ bitSetActiveLinks) << (nErrors == 3 ? " (not reporting further errors to avoid spamming)" : ""); + } + if (mCreateRawDataErrors) { + for (std::size_t ilink = 0; ilink < bitSetActiveLinks.size(); ilink++) { + if (!bitSetActiveLinks.test(ilink)) { + continue; + } + if (!activelinks.test(ilink)) { + mOutputDecoderErrors.emplace_back(ilink, ErrorTypeFEE::ErrorSource_t::LINK_ERROR, 0, -1, -1); + } + } + } + // discard event + // create empty trigger record with dedicated trigger bit marking as rejected + mOutputTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits() | o2::emcal::triggerbits::Inc, eventstart, 0); + continue; + } + } + } // Add cells if (currentevent.getNumberOfCells()) { LOG(debug) << "Event has " << currentevent.getNumberOfCells() << " cells"; @@ -333,13 +376,33 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) currentevent.sortCells(true); nLEDMONsEvent = bookEventCells(currentevent.getLEDMons(), true); } - const auto interaction = currentevent.getInteractionRecord(); LOG(debug) << "Next event [Orbit " << interaction.orbit << ", BC (" << interaction.bc << "]: Accepted " << ncellsEvent << " cells and " << nLEDMONsEvent << " LEDMONS"; mOutputTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits(), eventstart, ncellsEvent + nLEDMONsEvent); + + // Add trigger data + if (mDoTriggerReconstruction) { + auto [trus, patches] = buildL0Patches(currentevent); + LOG(debug) << "Found " << patches.size() << " L0 patches from " << trus.size() << " TRUs"; + auto trusstart = mOutputTRUs.size(); + std::copy(trus.begin(), trus.end(), std::back_inserter(mOutputTRUs)); + mOutputTRUTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits(), trusstart, trus.size()); + auto patchesstart = mOutputPatches.size(); + std::copy(patches.begin(), patches.end(), std::back_inserter(mOutputPatches)); + mOutputPatchTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits(), patchesstart, patches.size()); + // For L0 timesums use fixed time, across TRUs and triggers, determined from the patch time QC + // average found to be - will be made configurable + auto timesumsstart = mOutputTimesums.size(); + auto timesums = buildL0Timesums(currentevent, 8); + std::copy(timesums.begin(), timesums.end(), std::back_inserter(mOutputTimesums)); + mOutputTimesumTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits(), timesumsstart, timesums.size()); + } } LOG(info) << "[EMCALRawToCellConverter - run] Writing " << mOutputCells.size() << " cells from " << mOutputTriggerRecords.size() << " events ..."; - sendData(ctx, mOutputCells, mOutputTriggerRecords, mOutputDecoderErrors); + if (mDoTriggerReconstruction) { + LOG(info) << "[EMCALRawToCellConverter - run] Writing " << mOutputTRUs.size() << " TRU infos and " << mOutputPatches.size() << " trigger patches and " << mOutputTimesums.size() << " timesums from " << mOutputTRUTriggerRecords.size() << " events ..."; + } + sendData(ctx); } void RawToCellConverterSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -355,9 +418,9 @@ void RawToCellConverterSpec::updateCalibrationObjects() LOG(info) << "RecoParams updated"; o2::emcal::RecoParam::Instance().printKeyValues(true, true); } - // if (mCalibHandler->hasUpdateFEEDCS()) { - // LOG(info) << "DCS params updated"; - // } + if (mCalibHandler->hasUpdateFEEDCS()) { + LOG(info) << "DCS params updated"; + } } bool RawToCellConverterSpec::isLostTimeframe(framework::ProcessingContext& ctx) const @@ -374,7 +437,7 @@ bool RawToCellConverterSpec::isLostTimeframe(framework::ProcessingContext& ctx) if (payloadSize == 0) { auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } @@ -385,6 +448,219 @@ bool RawToCellConverterSpec::isLostTimeframe(framework::ProcessingContext& ctx) return false; } +void RawToCellConverterSpec::addFEEChannelToEvent(o2::emcal::EventContainer& currentEvent, const o2::emcal::Channel& currentchannel, const CellTimeCorrection& timeCorrector, const LocalPosition& position, ChannelType_t chantype) +{ + int CellID = -1; + bool isLowGain = false; + try { + if (chantype == o2::emcal::ChannelType_t::HIGH_GAIN || chantype == o2::emcal::ChannelType_t::LOW_GAIN) { + // high- / low-gain cell + CellID = getCellAbsID(position.mSupermoduleID, position.mColumn, position.mRow); + + isLowGain = chantype == o2::emcal::ChannelType_t::LOW_GAIN; + } else { + CellID = geLEDMONAbsID(position.mSupermoduleID, position.mColumn); // Module index encoded in colum for LEDMONs + isLowGain = position.mRow == 0; // For LEDMONs gain type is encoded in the row (0 - low gain, 1 - high gain) + } + } catch (ModuleIndexException& e) { + handleGeometryError(e, position.mSupermoduleID, CellID, currentchannel.getHardwareAddress(), chantype); + return; + } + + // define the conatiner for the fit results, and perform the raw fitting using the stadnard raw fitter + CaloFitResults fitResults; + try { + fitResults = mRawFitter->evaluate(currentchannel.getBunches()); + // Prevent negative entries - we should no longer get here as the raw fit usually will end in an error state + if (fitResults.getAmp() < 0) { + fitResults.setAmp(0.); + } + if (fitResults.getTime() < 0) { + fitResults.setTime(0.); + } + // apply correction for bc mod 4 + double celltime = timeCorrector.getCorrectedTime(fitResults.getTime()); + double amp = fitResults.getAmp() * o2::emcal::constants::EMCAL_ADCENERGY; + if (isLowGain) { + amp *= o2::emcal::constants::EMCAL_HGLGFACTOR; + } + if (chantype == o2::emcal::ChannelType_t::LEDMON) { + // Mark LEDMONs as HIGH_GAIN/LOW_GAIN for gain type merging - will be flagged as LEDMON later when pushing to the output container + currentEvent.setLEDMONCell(CellID, amp, celltime, isLowGain ? o2::emcal::ChannelType_t::LOW_GAIN : o2::emcal::ChannelType_t::HIGH_GAIN, currentchannel.getHardwareAddress(), position.mFeeID, mMergeLGHG); + } else { + currentEvent.setCell(CellID, amp, celltime, chantype, currentchannel.getHardwareAddress(), position.mFeeID, mMergeLGHG); + } + } catch (CaloRawFitter::RawFitterError_t& fiterror) { + handleFitError(fiterror, position.mFeeID, CellID, currentchannel.getHardwareAddress()); + } +} + +void RawToCellConverterSpec::addTRUChannelToEvent(o2::emcal::EventContainer& currentEvent, const o2::emcal::Channel& currentchannel, const LocalPosition& position) +{ + try { + auto tru = mTriggerMapping->getTRUIndexFromOnlineHardareAddree(currentchannel.getHardwareAddress(), position.mFeeID, position.mSupermoduleID); + if (position.mColumn >= 96 && position.mColumn <= 105) { + auto& trudata = currentEvent.getTRUData(tru); + // Trigger patch information encoded columns 95-105 + for (auto& bunch : currentchannel.getBunches()) { + LOG(debug) << "Found bunch of length " << static_cast(bunch.getBunchLength()) << " with start time " << static_cast(bunch.getStartTime()) << " (column " << static_cast(position.mColumn) << ")"; + auto l0time = bunch.getStartTime(); + int isample = 0; + for (auto& adc : bunch.getADC()) { + // patch word might be in any of the samples, need to check all of them + // in case of colum 105 the first 6 bits are the patch word, the remaining 4 bits are the header word + if (adc == 0) { + isample++; + continue; + } + if (position.mColumn == 105) { + std::bitset<6> patchBits(adc & 0x3F); + std::bitset<4> headerbits((adc >> 6) & 0xF); + for (auto localindex = 0; localindex < patchBits.size(); localindex++) { + if (patchBits.test(localindex)) { + auto globalindex = (position.mColumn - 96) * 10 + localindex; + LOG(debug) << "Found patch with index " << globalindex << " in sample " << isample; + // std::cout << "Found patch with index " << globalindex << " in sample " << isample << " (" << (bunch.getStartTime() - isample) << ")" << std::endl; + try { + trudata.setPatch(globalindex, bunch.getStartTime() - isample); + } catch (TRUDataHandler::PatchIndexException& e) { + handlePatchError(e, position.mFeeID, tru); + } + } + } + if (headerbits.test(2)) { + LOG(debug) << "TRU " << tru << ": Found TRU fired (" << tru << ") in sample " << isample; + // std::cout << "TRU " << tru << ": Found TRU fired (" << tru << ") in sample " << isample << " (" << (bunch.getStartTime() - isample) << ")" << std::endl; + trudata.setFired(true); + trudata.setL0time(bunch.getStartTime() - isample); + } + } else { + std::bitset<10> patchBits(adc & 0x3FF); + for (auto localindex = 0; localindex < patchBits.size(); localindex++) { + if (patchBits.test(localindex)) { + auto globalindex = (position.mColumn - 96) * 10 + localindex; + LOG(debug) << "TRU " << tru << ": Found patch with index " << globalindex << " in sample " << isample; + // std::cout << "TRU " << tru << ": Found patch with index " << globalindex << " in sample " << isample << " (" << (bunch.getStartTime() - isample) << ")" << std::endl; + try { + trudata.setPatch(globalindex, bunch.getStartTime() - isample); + } catch (TRUDataHandler::PatchIndexException& e) { + handlePatchError(e, position.mFeeID, tru); + } + } + } + } + isample++; + } + } + } else { + try { + auto absFastOR = mTriggerMapping->getAbsFastORIndexFromIndexInTRU(tru, position.mColumn); + for (auto& bunch : currentchannel.getBunches()) { + // FastOR data reversed internally (positive in time direction) + // -> Start time marks the first timebin, consequently it must be also reversed. + // std::cout << "Adding non-reversed FastOR time series for FastOR " << absFastOR << " (TRU " << tru << ", index " << static_cast(position.mColumn) << ") with start time " << static_cast(bunch.getStartTime()) << " (reversed " << bunch.getStartTime() + 1 - bunch.getADC().size() << "): "; + // for (auto adc : bunch.getADC()) { + // std::cout << adc << ", "; + //} + // std::cout << std::endl; + + try { + currentEvent.setFastOR(absFastOR, bunch.getStartTime(), bunch.getADC()); + } catch (FastOrStartTimeInvalidException& e) { + handleFastORStartTimeErrors(e, position.mFeeID, tru); + } + } + } catch (FastORIndexException& e) { + handleFastORErrors(e, position.mFeeID, tru); + } + } + } catch (TRUIndexException& e) { + handleTRUIndexError(e, position.mFeeID, currentchannel.getHardwareAddress()); + } +} + +std::tuple RawToCellConverterSpec::buildL0Patches(const EventContainer& currentevent) const +{ + LOG(debug) << "Reconstructing patches for Orbit " << currentevent.getInteractionRecord().orbit << ", BC " << currentevent.getInteractionRecord().bc; + TRUContainer eventTRUs; + PatchContainer eventPatches; + auto& fastOrs = currentevent.getTimeSeriesContainer(); + std::set foundFastOrs; + for (auto fastor : fastOrs) { + foundFastOrs.insert(fastor.first); + } + for (std::size_t itru = 0; itru < TriggerMappingV2::ALLTRUS; itru++) { + auto& currenttru = currentevent.readTRUData(itru); + if (!currenttru.hasAnyPatch()) { + continue; + } + auto l0time = currenttru.getL0time(); + LOG(debug) << "Found patches in TRU " << itru << ", fired: " << (currenttru.isFired() ? "yes" : "no") << ", L0 time " << static_cast(l0time); + uint8_t npatches = 0; + for (auto ipatch = 0; ipatch < o2::emcal::TriggerMappingV2::PATCHESINTRU; ipatch++) { + if (currenttru.hasPatch(ipatch)) { + auto patchtime = currenttru.getPatchTime(ipatch); + LOG(debug) << "Found patch " << ipatch << " in TRU " << itru << " with time " << static_cast(patchtime); + auto fastorStart = mTriggerMapping->getAbsFastORIndexFromIndexInTRU(itru, ipatch); + auto fastORs = mTriggerMapping->getFastORIndexFromL0Index(itru, ipatch, 4); + std::array fastors; + std::fill(fastors.begin(), fastors.end(), nullptr); + int indexFastorInTRU = 0; + for (auto fastor : fastORs) { + auto [truID, fastorTRU] = mTriggerMapping->getTRUFromAbsFastORIndex(fastor); + // std::cout << "Patch has abs FastOR " << fastor << " -> " << fastorTRU << " (in TRU)" << std::endl; + auto timeseriesFound = fastOrs.find(fastor); + if (timeseriesFound != fastOrs.end()) { + LOG(debug) << "Adding FastOR (" << indexFastorInTRU << ") with index " << fastor << " to patch"; + fastors[indexFastorInTRU] = &(timeseriesFound->second); + indexFastorInTRU++; + } + } + auto [patchADC, recpatchtime] = reconstructTriggerPatch(fastors); + // Correct for bit shift 12->10 bits due to ALTRO format + patchADC = patchADC << 2; + LOG(debug) << "Reconstructed patch at index " << ipatch << " with peak time " << static_cast(recpatchtime) << " (time sample " << static_cast(patchtime) << ") and energy " << patchADC; + eventPatches.push_back({static_cast(itru), static_cast(ipatch), patchtime, patchADC}); + } + } + eventTRUs.push_back({static_cast(itru), static_cast(l0time), currenttru.isFired(), npatches}); + } + return std::make_tuple(eventTRUs, eventPatches); +} + +std::vector RawToCellConverterSpec::buildL0Timesums(const o2::emcal::EventContainer& currentevent, uint8_t l0time) const +{ + std::vector timesums; + for (const auto& [fastorID, timeseries] : currentevent.getTimeSeriesContainer()) { + timesums.push_back({fastorID, static_cast(timeseries.calculateL1TimeSum(l0time) << 2)}); + } + return timesums; +} + +std::tuple RawToCellConverterSpec::reconstructTriggerPatch(const gsl::span fastors) const +{ + constexpr size_t INTEGRATE_SAMPLES = 4, + MAX_SAMPLES = 12; + double maxpatchenergy = 0; + uint8_t foundtime = 0; + for (size_t itime = 0; itime < MAX_SAMPLES - INTEGRATE_SAMPLES; ++itime) { + double currenttimesum = 0; + for (size_t isample = 0; isample < INTEGRATE_SAMPLES; ++isample) { + for (auto ifastor = 0; ifastor < fastors.size(); ++ifastor) { + if (fastors[ifastor]) { + currenttimesum += fastors[ifastor]->getADCs()[itime + isample]; + } + } + } + if (currenttimesum > maxpatchenergy) { + maxpatchenergy = currenttimesum; + foundtime = itime; + } + } + + return std::make_tuple(maxpatchenergy, foundtime); +} + int RawToCellConverterSpec::bookEventCells(const gsl::span& cells, bool isLELDMON) { double noiseThresholLGnoHG = RecoParam::Instance().getNoiseThresholdLGnoHG(); @@ -639,14 +915,102 @@ void RawToCellConverterSpec::handlePageError(const RawDecodingError& e) } } -void RawToCellConverterSpec::sendData(framework::ProcessingContext& ctx, const std::vector& cells, const std::vector& triggers, const std::vector& decodingErrors) const +void RawToCellConverterSpec::handleMinorPageError(const RawReaderMemory::MinorError& e) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(e.getFEEID(), ErrorTypeFEE::ErrorSource_t::PAGE_ERROR, RawDecodingError::ErrorTypeToInt(e.getErrorType()), -1, -1); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " Page decoding: " << RawDecodingError::getErrorCodeDescription(e.getErrorType()) << " in FEE ID " << e.getFEEID(); + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + +void RawToCellConverterSpec::handleFastORErrors(const FastORIndexException& e, unsigned int linkID, unsigned int indexTRU) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(linkID, ErrorTypeFEE::ErrorSource_t::TRU_ERROR, reconstructionerrors::getErrorCodeFromTRUDecodingError(reconstructionerrors::TRUDecodingError_t::TRU_INDEX_INVALID), indexTRU, -1); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " TRU decoding: " << e.what() << " in FEE ID " << linkID << ", TRU " << indexTRU; + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + +void RawToCellConverterSpec::handleFastORStartTimeErrors(const FastOrStartTimeInvalidException& e, unsigned int linkID, unsigned int indexTRU) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(linkID, ErrorTypeFEE::ErrorSource_t::TRU_ERROR, reconstructionerrors::getErrorCodeFromTRUDecodingError(reconstructionerrors::TRUDecodingError_t::FASTOR_STARTTIME_INVALID), indexTRU, -1); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " TRU decoding: " << e.what() << " in FEE ID " << linkID << ", TRU " << indexTRU; + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + +void RawToCellConverterSpec::handlePatchError(const TRUDataHandler::PatchIndexException& e, unsigned int linkID, unsigned int indexTRU) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(linkID, ErrorTypeFEE::ErrorSource_t::TRU_ERROR, reconstructionerrors::getErrorCodeFromTRUDecodingError(reconstructionerrors::TRUDecodingError_t::PATCH_INDEX_INVALID), indexTRU, -1); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " TRU decoding: " << e.what() << " in FEE ID " << linkID << ", TRU " << indexTRU; + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + +void RawToCellConverterSpec::handleTRUIndexError(const TRUIndexException& e, unsigned int linkID, unsigned int hwaddress) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(linkID, ErrorTypeFEE::ErrorSource_t::TRU_ERROR, reconstructionerrors::getErrorCodeFromTRUDecodingError(reconstructionerrors::TRUDecodingError_t::PATCH_INDEX_INVALID), e.getTRUIndex(), hwaddress); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " TRU decoding: " << e.what() << " in FEE ID " << linkID << ", TRU " << e.getTRUIndex() << "(hardware address: " << hwaddress << ")"; + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + +void RawToCellConverterSpec::sendData(framework::ProcessingContext& ctx) const { constexpr auto originEMC = o2::header::gDataOriginEMC; - ctx.outputs().snapshot(framework::Output{originEMC, "CELLS", mSubspecification, framework::Lifetime::Timeframe}, cells); - ctx.outputs().snapshot(framework::Output{originEMC, "CELLSTRGR", mSubspecification, framework::Lifetime::Timeframe}, triggers); + ctx.outputs().snapshot(framework::Output{originEMC, "CELLS", mSubspecification}, mOutputCells); + ctx.outputs().snapshot(framework::Output{originEMC, "CELLSTRGR", mSubspecification}, mOutputTriggerRecords); if (mCreateRawDataErrors) { - LOG(debug) << "Sending " << decodingErrors.size() << " decoding errors"; - ctx.outputs().snapshot(framework::Output{originEMC, "DECODERERR", mSubspecification, framework::Lifetime::Timeframe}, decodingErrors); + LOG(debug) << "Sending " << mOutputDecoderErrors.size() << " decoding errors"; + ctx.outputs().snapshot(framework::Output{originEMC, "DECODERERR", mSubspecification}, mOutputDecoderErrors); + } + if (mDoTriggerReconstruction) { + ctx.outputs().snapshot(framework::Output{originEMC, "TRUS", mSubspecification}, mOutputTRUs); + ctx.outputs().snapshot(framework::Output{originEMC, "TRUSTRGR", mSubspecification}, mOutputTRUTriggerRecords); + ctx.outputs().snapshot(framework::Output{originEMC, "PATCHES", mSubspecification}, mOutputPatches); + ctx.outputs().snapshot(framework::Output{originEMC, "PATCHESTRGR", mSubspecification}, mOutputPatchTriggerRecords); + ctx.outputs().snapshot(framework::Output{originEMC, "FASTORS", mSubspecification}, mOutputTimesums); + ctx.outputs().snapshot(framework::Output{originEMC, "FASTORSTRGR", mSubspecification}, mOutputTimesumTriggerRecords); } } @@ -659,7 +1023,7 @@ RawToCellConverterSpec::ModuleIndexException::ModuleIndexException(int moduleInd RawToCellConverterSpec::ModuleIndexException::ModuleIndexException(int moduleIndex) : mModuleType(ModuleType_t::LEDMON_MODULE), mIndex(moduleIndex) {} -o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getRawToCellConverterSpec(bool askDISTSTF, bool disableDecodingErrors, int subspecification) +o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getRawToCellConverterSpec(bool askDISTSTF, bool disableDecodingErrors, bool disableTriggerReconstruction, int subspecification) { constexpr auto originEMC = o2::header::gDataOriginEMC; std::vector outputs; @@ -669,25 +1033,35 @@ o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getRawToCellConverter if (!disableDecodingErrors) { outputs.emplace_back(originEMC, "DECODERERR", subspecification, o2::framework::Lifetime::Timeframe); } + if (!disableTriggerReconstruction) { + outputs.emplace_back(originEMC, "TRUS", subspecification, o2::framework::Lifetime::Timeframe); + outputs.emplace_back(originEMC, "TRUSTRGR", subspecification, o2::framework::Lifetime::Timeframe); + outputs.emplace_back(originEMC, "PATCHES", subspecification, o2::framework::Lifetime::Timeframe); + outputs.emplace_back(originEMC, "PATCHESTRGR", subspecification, o2::framework::Lifetime::Timeframe); + outputs.emplace_back(originEMC, "FASTORS", subspecification, o2::framework::Lifetime::Timeframe); + outputs.emplace_back(originEMC, "FASTORSTRGR", subspecification, o2::framework::Lifetime::Timeframe); + } - std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originEMC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Optional}}; + std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originEMC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Timeframe}}; if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); } // CCDB objects auto calibhandler = std::make_shared(); calibhandler->enableRecoParams(true); - // calibhandler->enableFEEDCS(true); + calibhandler->enableFEEDCS(true); calibhandler->defineInputSpecs(inputs); - return o2::framework::DataProcessorSpec{"EMCALRawToCellConverterSpec", - inputs, - outputs, - o2::framework::adaptFromTask(subspecification, !disableDecodingErrors, calibhandler), - o2::framework::Options{ - {"fitmethod", o2::framework::VariantType::String, "gamma2", {"Fit method (standard or gamma2)"}}, - {"maxmessage", o2::framework::VariantType::Int, 100, {"Max. amout of error messages to be displayed"}}, - {"printtrailer", o2::framework::VariantType::Bool, false, {"Print RCU trailer (for debugging)"}}, - {"no-mergeHGLG", o2::framework::VariantType::Bool, false, {"Do not merge HG and LG channels for same tower"}}, - {"no-evalpedestal", o2::framework::VariantType::Bool, false, {"Disable pedestal evaluation"}}}}; + return o2::framework::DataProcessorSpec{ + "EMCALRawToCellConverterSpec", + inputs, + outputs, + o2::framework::adaptFromTask(subspecification, !disableDecodingErrors, !disableTriggerReconstruction, calibhandler), + o2::framework::Options{ + {"fitmethod", o2::framework::VariantType::String, "gamma2", {"Fit method (standard or gamma2)"}}, + {"maxmessage", o2::framework::VariantType::Int, 100, {"Max. amout of error messages to be displayed"}}, + {"printtrailer", o2::framework::VariantType::Bool, false, {"Print RCU trailer (for debugging)"}}, + {"no-mergeHGLG", o2::framework::VariantType::Bool, false, {"Do not merge HG and LG channels for same tower"}}, + {"no-checkactivelinks", o2::framework::VariantType::Bool, false, {"Do not check for active links per BC"}}, + {"no-evalpedestal", o2::framework::VariantType::Bool, false, {"Disable pedestal evaluation"}}}}; } diff --git a/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx b/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx index 0f459b1101d3c..28e0deb3ae0b3 100644 --- a/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx +++ b/Detectors/EMCAL/workflow/src/RecoWorkflow.cxx @@ -47,13 +47,14 @@ namespace reco_workflow o2::framework::WorkflowSpec getWorkflow(bool propagateMC, bool askDISTSTF, bool enableDigitsPrinter, - int subspecification, + int subspecificationIn, + int subspecificationOut, std::string const& cfgInput, std::string const& cfgOutput, bool disableRootInput, bool disableRootOutput, bool disableDecodingErrors, - bool useccdb) + bool disableTriggerReconstruction) { const std::unordered_map InputMap{ @@ -170,10 +171,10 @@ o2::framework::WorkflowSpec getWorkflow(bool propagateMC, if (isEnabled(OutputType::Cells)) { // add converter for cells if (inputType == InputType::Digits) { - specs.emplace_back(o2::emcal::reco_workflow::getCellConverterSpec(propagateMC, useccdb, subspecification)); + specs.emplace_back(o2::emcal::reco_workflow::getCellConverterSpec(propagateMC, subspecificationIn, subspecificationOut)); } else if (inputType == InputType::Raw) { // raw data will come from upstream - specs.emplace_back(o2::emcal::reco_workflow::getRawToCellConverterSpec(askDISTSTF, disableDecodingErrors, subspecification)); + specs.emplace_back(o2::emcal::reco_workflow::getRawToCellConverterSpec(askDISTSTF, disableDecodingErrors, disableTriggerReconstruction, subspecificationOut)); } } diff --git a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx index a9c16616942d4..227fc373bf20c 100644 --- a/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx +++ b/Detectors/EMCAL/workflow/src/StandaloneAODProducerSpec.cxx @@ -17,7 +17,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "MathUtils/Utils.h" using namespace o2::framework; @@ -64,21 +63,21 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) LOG(info) << "FOUND " << cellsIn.size() << " EMC cells in CTF"; LOG(info) << "FOUND " << triggersIn.size() << " EMC tiggers in CTF"; - auto& bcBuilder = pc.outputs().make(Output{"AOD", "BC"}); - auto& collisionsBuilder = pc.outputs().make(Output{"AOD", "COLLISION"}); - auto& caloCellsBuilder = pc.outputs().make(Output{"AOD", "CALO"}); - auto& caloCellsTRGTableBuilder = pc.outputs().make(Output{"AOD", "CALOTRIGGER"}); + auto bcBuilder = pc.outputs().make(Output{"AOD", "BC"}); + auto collisionsBuilder = pc.outputs().make(Output{"AOD", "COLLISION"}); + auto caloCellsBuilder = pc.outputs().make(Output{"AOD", "CALO"}); + auto caloCellsTRGTableBuilder = pc.outputs().make(Output{"AOD", "CALOTRIGGER"}); - auto bcCursor = bcBuilder.cursor(); - auto collisionsCursor = collisionsBuilder.cursor(); - auto caloCellsCursor = caloCellsBuilder.cursor(); - auto caloCellsTRGTableCursor = caloCellsTRGTableBuilder.cursor(); + auto bcCursor = bcBuilder->cursor(); + auto collisionsCursor = collisionsBuilder->cursor(); + auto caloCellsCursor = caloCellsBuilder->cursor(); + auto caloCellsTRGTableCursor = caloCellsTRGTableBuilder->cursor(); // build event for easier handling mCaloEventHandler->reset(); mCaloEventHandler->setCellData(cellsIn, triggersIn); - uint64_t triggerMask = 1; + uint64_t triggerMask = 1, inputMask = 1; // loop over events for (int iev = 0; iev < mCaloEventHandler->getNumberOfEvents(); iev++) { o2::emcal::EventData inputEvent = mCaloEventHandler->buildEvent(iev); @@ -91,7 +90,8 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) bcCursor(0, runNumber, bcID, - triggerMask); + triggerMask, + inputMask); auto indexBC = iev; for (auto& cell : cellsInEvent) { @@ -104,7 +104,7 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) o2::math_utils::detail::truncateFloatFraction(cell.getTimeStamp(), mCaloTime), cell.getType(), 1); // hard coded for emcal (-1 would be undefined, 0 phos) - } // end of cell loop + } // end of cell loop // filled only with BCID, rest dummy for no2 caloCellsTRGTableCursor(0, @@ -135,8 +135,8 @@ void StandaloneAODProducerSpec::run(ProcessingContext& pc) } // end of event loop // std::cout << "Finished cell loop" << std::endl; - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0, Lifetime::Timeframe}, ""); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); mTimer.Stop(); } diff --git a/Detectors/EMCAL/workflow/src/emc-channel-data-producer.cxx b/Detectors/EMCAL/workflow/src/emc-channel-data-producer.cxx index f878a933b2d8d..75e2cdbd9fce3 100644 --- a/Detectors/EMCAL/workflow/src/emc-channel-data-producer.cxx +++ b/Detectors/EMCAL/workflow/src/emc-channel-data-producer.cxx @@ -160,7 +160,7 @@ DataProcessorSpec generateData(const std::string nameRootFile, const std::string o2::pmr::vector TriggerOutput; TriggerOutput.emplace_back(0, 0, 0, CellOutput.size()); - ctx.outputs().adoptContainer(Output{o2::header::gDataOriginEMC, "CELLS", 0, Lifetime::Timeframe}, std::move(CellOutput)); - ctx.outputs().adoptContainer(Output{o2::header::gDataOriginEMC, "CELLSTRGR", 0, Lifetime::Timeframe}, std::move(TriggerOutput)); + ctx.outputs().adoptContainer(Output{o2::header::gDataOriginEMC, "CELLS", 0}, std::move(CellOutput)); + ctx.outputs().adoptContainer(Output{o2::header::gDataOriginEMC, "CELLSTRGR", 0}, std::move(TriggerOutput)); }}}; -} \ No newline at end of file +} diff --git a/Detectors/EMCAL/workflow/src/emc-offline-calib-workflow.cxx b/Detectors/EMCAL/workflow/src/emc-offline-calib-workflow.cxx index 903b729211384..9ea41f2ee342b 100644 --- a/Detectors/EMCAL/workflow/src/emc-offline-calib-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/emc-offline-calib-workflow.cxx @@ -25,6 +25,8 @@ void customize(std::vector& workflowOptions) {"no-rejectCalibTrigg", VariantType::Bool, false, {"if set to true, all events, including calibration triggered events, will be accepted"}}, {"input-subspec", VariantType::UInt32, 0U, {"Subspecification for input objects"}}, {"applyGainCalib", VariantType::Bool, false, {"Apply the gain calibration parameters for the bad channel calibration"}}, + {"rejectL0Trigger", VariantType::Bool, false, {"Reject all emcal triggers except the minimum bias trigger"}}, + {"ctpconfig-run-independent", VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); } @@ -40,11 +42,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) bool makeCellIDTimeEnergy = cfgc.options().get("makeCellIDTimeEnergy"); bool rejectCalibTrigg = !cfgc.options().get("no-rejectCalibTrigg"); bool doApplyGainCalib = cfgc.options().get("applyGainCalib"); + bool doRejectL0Trigger = cfgc.options().get("rejectL0Trigger"); + bool ctpcfgperrun = !cfgc.options().get("ctpconfig-run-independent"); // subpsecs for input auto inputsubspec = cfgc.options().get("input-subspec"); o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::emcal::getEmcalOfflineCalibSpec(makeCellIDTimeEnergy, rejectCalibTrigg, inputsubspec, doApplyGainCalib)); + wf.emplace_back(o2::emcal::getEmcalOfflineCalibSpec(makeCellIDTimeEnergy, rejectCalibTrigg, doRejectL0Trigger, inputsubspec, doApplyGainCalib, ctpcfgperrun)); return wf; -} \ No newline at end of file +} diff --git a/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx b/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx index 1cf0b71182497..4e31a26ee4b5c 100644 --- a/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/emc-reco-workflow.cxx @@ -53,9 +53,10 @@ void customize(std::vector& workflowOptions) {"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable sending of MC information"}}, {"disable-decoding-errors", o2::framework::VariantType::Bool, false, {"disable propagating decoding errors"}}, + {"disable-trigger-reconstruction", o2::framework::VariantType::Bool, false, {"disable reconstruction of trigger data"}}, {"ignore-dist-stf", o2::framework::VariantType::Bool, false, {"do not subscribe to FLP/DISTSUBTIMEFRAME/0 message (no lost TF recovery)"}}, - {"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb EMCAL simulation objects"}}, - {"subspecification", o2::framework::VariantType::Int, 0, {"Subspecification in case the workflow runs in parallel on multiple nodes (i.e. different FLPs)"}}}; + {"subspecificationIn", o2::framework::VariantType::Int, 0, {"Subspecification for input in case the workflow runs in parallel on multiple nodes (i.e. different FLPs)"}}, + {"subspecificationOut", o2::framework::VariantType::Int, 0, {"Subspecification for output in case the workflow runs in parallel on multiple nodes (i.e. different FLPs)"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -80,13 +81,14 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co auto wf = o2::emcal::reco_workflow::getWorkflow(!cfgc.options().get("disable-mc"), !cfgc.options().get("ignore-dist-stf"), cfgc.options().get("enable-digits-printer"), - cfgc.options().get("subspecification"), + cfgc.options().get("subspecificationIn"), + cfgc.options().get("subspecificationOut"), cfgc.options().get("input-type"), cfgc.options().get("output-type"), cfgc.options().get("disable-root-input"), cfgc.options().get("disable-root-output"), cfgc.options().get("disable-decoding-errors"), - cfgc.options().get("use-ccdb")); + cfgc.options().get("disable-trigger-reconstruction")); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cfgc, wf); diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx index e6af02fa10d49..d5264aabc0566 100644 --- a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::emcal::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FDD/base/include/FDDBase/Constants.h b/Detectors/FIT/FDD/base/include/FDDBase/Constants.h index d15da52232af3..f3c315457c46d 100644 --- a/Detectors/FIT/FDD/base/include/FDDBase/Constants.h +++ b/Detectors/FIT/FDD/base/include/FDDBase/Constants.h @@ -37,21 +37,6 @@ constexpr float PMNbOfSecElec = 6.0; // Number of secondary electrons e constexpr int NTimeBinsPerBC = 256; // number of samples per BC -// Detector TOF correction in ns -constexpr float FDAdist = 1696.67; -constexpr float FDCdist = 1954.4; -constexpr float LayerWidth = 1.27; - -constexpr float getTOFCorrection(int det) -{ - constexpr float TOFCorr[4] = { - (FDCdist + LayerWidth) / o2::constants::physics::LightSpeedCm2NS, - (FDCdist - LayerWidth) / o2::constants::physics::LightSpeedCm2NS, - (FDAdist - LayerWidth) / o2::constants::physics::LightSpeedCm2NS, - (FDAdist + LayerWidth) / o2::constants::physics::LightSpeedCm2NS}; - return TOFCorr[det]; -} - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/base/src/Geometry.cxx b/Detectors/FIT/FDD/base/src/Geometry.cxx index 441a30fdda44f..149c086f4fc81 100644 --- a/Detectors/FIT/FDD/base/src/Geometry.cxx +++ b/Detectors/FIT/FDD/base/src/Geometry.cxx @@ -152,7 +152,7 @@ void Geometry::buildGeometry() if (!vCaveRB24) { LOG(fatal) << "Could not find the top volume for A-side"; } - const Float_t kPosFDA = 1696.67 - 1313.347; // z-center of assembly (cm) + const Float_t kPosFDA = 1696.67 - 1313.347 - 75.; // z-center of assembly (cm) vCaveRB24->AddNode(vFDAarray, 1, new TGeoTranslation(0., 0., kPosFDA - kFDACelldz / 2. - 0.1)); vCaveRB24->AddNode(vFDAarray, 2, new TGeoTranslation(0., 0., kPosFDA + kFDACelldz / 2. + 0.1)); diff --git a/Detectors/FIT/FDD/dcsmonitoring/workflow/FDDDCSConfigProcessorSpec.h b/Detectors/FIT/FDD/dcsmonitoring/workflow/FDDDCSConfigProcessorSpec.h index 8ecbbb094cc39..b2844fc0ac95a 100644 --- a/Detectors/FIT/FDD/dcsmonitoring/workflow/FDDDCSConfigProcessorSpec.h +++ b/Detectors/FIT/FDD/dcsmonitoring/workflow/FDDDCSConfigProcessorSpec.h @@ -40,8 +40,8 @@ DataProcessorSpec getFDDDCSConfigProcessorSpec() return DataProcessorSpec{ "fdd-dcs-config-processor", - Inputs{{"inputConfig", o2::header::gDataOriginFDD, "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", o2::header::gDataOriginFDD, "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", o2::header::gDataOriginFDD, "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", o2::header::gDataOriginFDD, "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask("FDD", ddDChM)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, diff --git a/Detectors/FIT/FDD/raw/include/FDDRaw/DigitBlockFDD.h b/Detectors/FIT/FDD/raw/include/FDDRaw/DigitBlockFDD.h index ea86bfda3d406..1047413687dd9 100644 --- a/Detectors/FIT/FDD/raw/include/FDDRaw/DigitBlockFDD.h +++ b/Detectors/FIT/FDD/raw/include/FDDRaw/DigitBlockFDD.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -//file DigitBlockFDD.h class for proccessing RAW data into Digits +// file DigitBlockFDD.h class for proccessing RAW data into Digits // // Artur.Furs // afurs@cern.ch @@ -25,10 +25,10 @@ namespace o2 { namespace fdd { -//Normal data taking mode -using DigitBlockFDD = DigitBlockFIT; -//TCM extended data taking mode -using DigitBlockFDDext = DigitBlockFIText; +// Normal data taking mode +using DigitBlockFDD = o2::fit::DigitBlockFIT; +// TCM extended data taking mode +using DigitBlockFDDext = o2::fit::DigitBlockFIText; } // namespace fdd } // namespace o2 #endif diff --git a/Detectors/FIT/FDD/raw/src/DataBlockFDD.cxx b/Detectors/FIT/FDD/raw/src/DataBlockFDD.cxx index 944c3dbd598f5..53c98a0ac9e7a 100644 --- a/Detectors/FIT/FDD/raw/src/DataBlockFDD.cxx +++ b/Detectors/FIT/FDD/raw/src/DataBlockFDD.cxx @@ -10,4 +10,7 @@ // or submit itself to any jurisdiction. #include "FDDRaw/DataBlockFDD.h" -//using namespace o2::fdd; + +template class o2::fit::DataBlockPM, o2::fdd::RawHeaderPM, o2::fdd::RawDataPM>; +template class o2::fit::DataBlockTCM, o2::fdd::RawHeaderTCM, o2::fdd::RawDataTCM>; +template class o2::fit::DataBlockTCMext, o2::fdd::RawHeaderTCMext, o2::fdd::RawDataTCM, o2::fdd::RawDataTCMext>; diff --git a/Detectors/FIT/FDD/raw/src/DigitBlockFDD.cxx b/Detectors/FIT/FDD/raw/src/DigitBlockFDD.cxx index fe4948ab9480b..0e1bc72bef652 100644 --- a/Detectors/FIT/FDD/raw/src/DigitBlockFDD.cxx +++ b/Detectors/FIT/FDD/raw/src/DigitBlockFDD.cxx @@ -10,4 +10,6 @@ // or submit itself to any jurisdiction. #include "FDDRaw/DigitBlockFDD.h" -//using namespace o2::fdd; + +template class o2::fit::DigitBlockFIT; +template class o2::fit::DigitBlockFIText; \ No newline at end of file diff --git a/Detectors/FIT/FDD/raw/src/RawReaderFDDBase.cxx b/Detectors/FIT/FDD/raw/src/RawReaderFDDBase.cxx index 3708c53d3f03b..5c500b8dac5ca 100644 --- a/Detectors/FIT/FDD/raw/src/RawReaderFDDBase.cxx +++ b/Detectors/FIT/FDD/raw/src/RawReaderFDDBase.cxx @@ -10,4 +10,6 @@ // or submit itself to any jurisdiction. #include "FDDRaw/RawReaderFDDBase.h" -//using namespace o2::fdd; + +template class o2::fit::RawReaderBaseFIT; +template class o2::fit::RawReaderBaseFIT; diff --git a/Detectors/FIT/FDD/raw/src/RawWriterFDD.cxx b/Detectors/FIT/FDD/raw/src/RawWriterFDD.cxx index 29b364ce400be..69d4e23a99815 100644 --- a/Detectors/FIT/FDD/raw/src/RawWriterFDD.cxx +++ b/Detectors/FIT/FDD/raw/src/RawWriterFDD.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FDDRaw/RawWriterFDD.h" + +template class o2::fit::RawWriterFIT; +template class o2::fit::RawWriterFIT; diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index d5811514dd057..c62e013447416 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -25,7 +25,6 @@ #include "DataFormatsFDD/ChannelData.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" class TTree; @@ -34,10 +33,10 @@ namespace o2 namespace fdd { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF @@ -72,15 +71,15 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig using MD = o2::ctf::Metadata::OptStore; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { - MD::EENCODE, // BLC_trigger - MD::EENCODE, // BLC_bcInc - MD::EENCODE, // BLC_orbitInc - MD::EENCODE, // BLC_nChan + MD::EENCODE_OR_PACK, // BLC_trigger + MD::EENCODE_OR_PACK, // BLC_bcInc + MD::EENCODE_OR_PACK, // BLC_orbitInc + MD::EENCODE_OR_PACK, // BLC_nChan - MD::EENCODE, // BLC_idChan - MD::EENCODE, // BLC_time - MD::EENCODE, // BLC_charge - MD::EENCODE // BLC_feeBits + MD::EENCODE_OR_PACK, // BLC_idChan + MD::EENCODE_OR_PACK, // BLC_time + MD::EENCODE_OR_PACK, // BLC_charge + MD::EENCODE_OR_PACK // BLC_feeBits }; CompressedDigits cd; if (mExtHeader.isValidDictTimeStamp()) { @@ -101,11 +100,11 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig ec->setHeader(cd.header); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEFDD(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEFDD(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEFDD(cd.trigger, CTF::BLC_trigger, 0); iosize += ENCODEFDD(cd.bcInc, CTF::BLC_bcInc, 0); @@ -133,7 +132,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& checkDictVersion(hd); ec.print(getPrefix(), mVerbosity); o2::ctf::CTFIOSize iosize; -#define DECODEFDD(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEFDD(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEFDD(cd.trigger, CTF::BLC_trigger); iosize += DECODEFDD(cd.bcInc, CTF::BLC_bcInc); diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h deleted file mode 100644 index 54c8b7b203edb..0000000000000 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \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 -#include -#include -#include -#include -#include -#include -#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> mDigitAccum; // digit accumulator - - template - TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) - { - if (auto br = tree.GetBranch(brname.c_str())) { - br->SetAddress(static_cast(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/include/FDDReconstruction/Reconstructor.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h index 161b800a2c3ca..8881605b652ac 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h @@ -17,6 +17,7 @@ #include #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" namespace o2 { namespace fdd @@ -30,10 +31,16 @@ class Reconstructor gsl::span inChData, std::vector& RecPoints, std::vector& outChData); - void finish(); + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) + { + LOG(info) << "Updated dead channel map"; + mDeadChannelMap = deadChannelMap; + } + private: + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Reconstructor, 3); }; } // namespace fdd diff --git a/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx index 315f9ac346d89..ae20a299c8ebf 100644 --- a/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/CTFCoder.cxx @@ -51,7 +51,7 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); CompressedDigits cd; // just to get member types -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cd.trigger, CTF::BLC_trigger); MAKECODER(cd.bcInc, CTF::BLC_bcInc); @@ -69,22 +69,17 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa size_t CTFCoder::estimateCompressedSize(const CompressedDigits& cd) { size_t sz = 0; - // clang-format off // RS FIXME this is very crude estimate, instead, an empirical values should be used -#define VTP(vec) typename std::remove_reference::type::value_type -#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ - rans::calculateMaxBufferSize(vec.size(), reinterpret_cast*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) - sz += ESTSIZE(cd.trigger, CTF::BLC_trigger); - sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); - sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); - sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); + sz += estimateBufferSize(static_cast(CTF::BLC_trigger), cd.trigger); + sz += estimateBufferSize(static_cast(CTF::BLC_bcInc), cd.bcInc); + sz += estimateBufferSize(static_cast(CTF::BLC_orbitInc), cd.orbitInc); + sz += estimateBufferSize(static_cast(CTF::BLC_nChan), cd.nChan); - sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); - sz += ESTSIZE(cd.time, CTF::BLC_time); - sz += ESTSIZE(cd.charge, CTF::BLC_charge); - sz += ESTSIZE(cd.feeBits, CTF::BLC_feeBits); - // clang-format on + sz += estimateBufferSize(static_cast(CTF::BLC_idChan), cd.idChan); + sz += estimateBufferSize(static_cast(CTF::BLC_time), cd.time); + sz += estimateBufferSize(static_cast(CTF::BLC_charge), cd.charge); + sz += estimateBufferSize(static_cast(CTF::BLC_feeBits), cd.feeBits); - LOG(info) << "Estimated output size is " << sz << " bytes"; + LOG(debug) << "Estimated output size is " << sz << " bytes"; return sz; } diff --git a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx index 3a87a11046a77..7d133e30df08e 100644 --- a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx @@ -33,7 +33,12 @@ void Reconstructor::process(o2::fdd::Digit const& digitBC, gsl::spanisChannelAlive(inChData[ich].mPMNumber)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } bool inTime = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsEventInTVDC); bool inAdcGate = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsCFDinADCgate); if (inAdcGate) { diff --git a/Detectors/FIT/FDD/simulation/CMakeLists.txt b/Detectors/FIT/FDD/simulation/CMakeLists.txt index a123a9929b2eb..ec217c4419830 100644 --- a/Detectors/FIT/FDD/simulation/CMakeLists.txt +++ b/Detectors/FIT/FDD/simulation/CMakeLists.txt @@ -11,26 +11,25 @@ o2_add_library(FDDSimulation SOURCES src/Detector.cxx - src/Digitizer.cxx - PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat - O2::FDDBase - O2::DataFormatsFDD - O2::DetectorsRaw - O2::DetectorsBase - ROOT::Physics) + src/Digitizer.cxx + PUBLIC_LINK_LIBRARIES O2::CommonUtils + O2::SimulationDataFormat + O2::FDDBase + O2::DataFormatsFDD + O2::DetectorsRaw + O2::DetectorsBase + ROOT::Physics) o2_target_root_dictionary(FDDSimulation HEADERS include/FDDSimulation/Detector.h - include/FDDSimulation/Digitizer.h - include/FDDSimulation/DigitizationParameters.h) + include/FDDSimulation/Digitizer.h + include/FDDSimulation/DigitizationParameters.h + include/FDDSimulation/FDDDigParam.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 - O2::FDDRaw) + PUBLIC_LINK_LIBRARIES O2::FDDRaw + Boost::program_options) + o2_data_file(COPY data DESTINATION Detectors/FDD/simulation) \ No newline at end of file diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/DigitizationParameters.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/DigitizationParameters.h index 9c814befebc21..827079b1a97c8 100644 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/DigitizationParameters.h +++ b/Detectors/FIT/FDD/simulation/include/FDDSimulation/DigitizationParameters.h @@ -16,7 +16,6 @@ namespace o2::fdd { struct DigitizationParameters { static constexpr float LightYield = 0.01; - static constexpr float PmGain = 1e6; static constexpr float ShapeAlpha = -0.445; static constexpr float ShapeN = 2.65; static constexpr float ShapeSigma = 3.25; diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digitizer.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digitizer.h index c7bd444d5ce63..943babc0a23d0 100644 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digitizer.h +++ b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digitizer.h @@ -16,6 +16,7 @@ #include "DataFormatsFDD/ChannelData.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "FDDSimulation/Detector.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "FDDSimulation/DigitizationParameters.h" @@ -87,6 +88,8 @@ class Digitizer void init(); void finish(); + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; }; + private: static constexpr int BCCacheMin = -1, BCCacheMax = 10, NBC2Cache = 1 + BCCacheMax - BCCacheMin; @@ -125,6 +128,8 @@ class Digitizer static Double_t PMResponse(Double_t* x, Double_t*); static Double_t SinglePhESpectrum(Double_t* x, Double_t* par); + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; + ClassDefNV(Digitizer, 4); }; } // namespace fdd diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h deleted file mode 100644 index 4afcf5da37ae8..0000000000000 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file 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 -#include -#include -#include -#include -#include - -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 pmchannels, - const o2::fdd::LookUpTable& lut); - - o2::raw::RawFileWriter& getWriter() { return mWriter; } - void setFilePerLink(bool v) { mOutputPerLink = v; } - bool getFilePerLink() const { return mOutputPerLink; } - - int carryOverMethod(const header::RDHAny* rdh, const gsl::span data, - const char* ptr, int maxSize, int splitID, - std::vector& trailer, std::vector& header) const; - - 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::fdd::Triggers mTriggers; - o2::raw::RawFileWriter mWriter{"FDD"}; - bool mOutputPerLink = false; - ///////////////////////////////////////////////// - - ClassDefNV(Digits2Raw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/FDDDigParam.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/FDDDigParam.h new file mode 100644 index 0000000000000..36f85bff24d6f --- /dev/null +++ b/Detectors/FIT/FDD/simulation/include/FDDSimulation/FDDDigParam.h @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FDDDigParam.h +/// \brief Configurable digitization parameters +/// +/// \author Andreas Molander + +#ifndef ALICEO2_FDD_DIG_PARAM +#define ALICEO2_FDD_DIG_PARAM + +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::fdd +{ +struct FDDDigParam : o2::conf::ConfigurableParamHelper { + float hitTimeOffsetA = 0; ///< Hit time offset on the A side [ns] + float hitTimeOffsetC = 0; ///< Hit time offset on the C side [ns] + + float pmGain = 1e6; ///< PM gain + + O2ParamDef(FDDDigParam, "FDDDigParam"); +}; +} // namespace o2::fdd + +#endif // ALICEO2_FDD_DIG_PARAM diff --git a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx index c8be105263fdc..daa3ae33f3c23 100644 --- a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx @@ -10,8 +10,10 @@ // or submit itself to any jurisdiction. #include "FDDSimulation/Digitizer.h" + +#include "CommonDataFormat/InteractionRecord.h" +#include "FDDSimulation/FDDDigParam.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include #include "TMath.h" #include "TRandom.h" @@ -49,6 +51,13 @@ void Digitizer::process(const std::vector& hits, // LOG(info) << "Pulse"; // Conversion of hits to the analogue pulse shape for (auto& hit : sorted_hits) { + int iChannel = hit.GetDetectorID(); + + // If the dead channel map is used, and the channel with ID 'hit_ch' is dead, don't process this hit. + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(iChannel)) { + continue; + } + if (hit.GetTime() > 20e3) { const int maxWarn = 10; static int warnNo = 0; @@ -60,13 +69,16 @@ void Digitizer::process(const std::vector& hits, } std::array cachedIR; - int iChannel = hit.GetDetectorID(); int nPhotoElectrons = simulateLightYield(iChannel, hit.GetNphot()); double delayScintillator = mRndScintDelay.getNextValue(); double timeHit = delayScintillator + hit.GetTime(); - timeHit -= getTOFCorrection(int(iChannel / 4)); // account for TOF to detector + // Subtract time-of-flight from hit time + const float timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS; + const float timeOffset = iChannel < 8 ? FDDDigParam::Instance().hitTimeOffsetC : FDDDigParam::Instance().hitTimeOffsetA; + + timeHit += -timeOfFlight + timeOffset; timeHit += mIntRecord.getTimeNS(); o2::InteractionRecord irHit(timeHit); // BC in which the hit appears (might be different from interaction BC for slow particles) @@ -100,7 +112,7 @@ void Digitizer::createPulse(int nPhE, int parID, double timeHit, std::array& labels) { // LOG(info) << "Storing BC " << bc; - int first = digitsCh.size(), nStored = 0; + float totalChargeA = 0, totalChargeC = 0; + int n_hit_A = 0, n_hit_C = 0, total_time_A = 0, total_time_C = 0; + bool nChCside = 8; for (int ic = 0; ic < Nchannels; ic++) { float chargeADC = integrateCharge(bc.pulse[ic]); + int cfdTime = int(simulateTimeCFD(bc.pulse[ic])); + if (chargeADC != 0) { + if (ic < nChCside) { + totalChargeC += chargeADC; + total_time_C += cfdTime; + n_hit_C++; + } else { + totalChargeA += chargeADC; + total_time_A += cfdTime; + n_hit_A++; + } uint8_t channelBits = parameters.defaultFEEbits; if (std::rand() % 2) { ChannelData::setFlag(ChannelData::kNumberADC, channelBits); } - digitsCh.emplace_back(ic, int(simulateTimeCFD(bc.pulse[ic])), int(chargeADC), channelBits); + digitsCh.emplace_back(ic, cfdTime, int(chargeADC), channelBits); nStored++; } } - // bc.print(); - + // SET TRIGGERS + Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0; + is_A = n_hit_A > 0; + is_C = n_hit_C > 0; + uint32_t amplA = is_A ? totalChargeA * 0.125 : -5000; // sum amplitude A side / 8 (hardware) + uint32_t amplC = is_C ? totalChargeC * 0.125 : -5000; // sum amplitude C side / 8 (hardware) + int timeA = is_A ? total_time_A / n_hit_A : -5000; // average time A side + int timeC = is_C ? total_time_C / n_hit_C : -5000; // average time C side + isVertex = is_A && is_C; + + bool isLaser = false; + bool isOutputsAreBlocked = false; + bool isDataValid = true; + mTriggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C), + amplA, amplC, timeA, timeC, isLaser, isOutputsAreBlocked, isDataValid); if (nStored != 0) { int nBC = digitsBC.size(); digitsBC.emplace_back(first, nStored, bc, mTriggers); - digitsTrig.emplace_back(bc, 0, 0, 0, 0, 0); + digitsTrig.emplace_back(bc, is_A, is_C, isVertex, is_Central, is_SemiCentral); for (const auto& lbl : bc.labels) { labels.addElement(nBC, lbl); @@ -394,3 +432,5 @@ void Digitizer::BCCache::print() const printf("\n"); } } + +O2ParamImpl(FDDDigParam); \ No newline at end of file diff --git a/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h b/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h index 9f57fa111c081..30f111c5ad2bf 100644 --- a/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h +++ b/Detectors/FIT/FDD/simulation/src/FDDSimulationLinkDef.h @@ -19,6 +19,7 @@ #pragma link C++ class o2::base::DetImpl < o2::fdd::Detector> + ; #pragma link C++ class o2::fdd::Digitizer + ; #pragma link C++ class o2::fdd::DigitizationParameters + ; +#pragma link C++ class o2::fdd::FDDDigParam + ; #pragma link C++ class o2::dataformats::MCTruthContainer < o2::fdd::MCLabel> + ; #endif diff --git a/Detectors/FIT/FDD/simulation/src/digit2raw.cxx b/Detectors/FIT/FDD/simulation/src/digit2raw.cxx index 93f67e80d07c7..de24fc92a3976 100644 --- a/Detectors/FIT/FDD/simulation/src/digit2raw.cxx +++ b/Detectors/FIT/FDD/simulation/src/digit2raw.cxx @@ -13,52 +13,12 @@ /// \author ruben.shahoyan@cern.ch afurs@cern.ch #include -#include #include -#include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "DetectorsRaw/HBFUtils.h" #include "FDDRaw/RawWriterFDD.h" -#include "DataFormatsParameters/GRPObject.h" /// MC->raw conversion for FDD namespace bpo = boost::program_options; -struct Configuration { - Configuration(const bpo::variables_map& vm) - { - mInputFile = vm["input-file"].as(); - mOutputDir = vm["output-dir"].as(); - mVerbosity = vm["verbosity"].as(); - mFileFor = vm["file-for"].as(); - mRdhVersion = vm["rdh-version"].as(); - mEnablePadding = vm["enable-padding"].as(); - mNoEmptyHBF = vm["no-empty-hbf"].as(); - mFlpName = vm["flp-name"].as(); - mCcdbPath = vm["ccdb-path"].as(); - mChannelMapPath = vm["lut-path"].as(); - if (mRdhVersion < 7 && !mEnablePadding) { - mEnablePadding = true; - LOG(info) << "padding is always ON for RDH version " << mRdhVersion; - } - mDataFormat = mEnablePadding ? 0 : 2; - } - bool mNoEmptyHBF; - bool mEnablePadding; - int mVerbosity; - uint32_t mRdhVersion; - uint32_t mDataFormat; - std::string mInputFile; - std::string mOutputDir; - std::string mFileFor; - std::string mFlpName; - std::string mCcdbPath; - std::string mChannelMapPath; -}; - -template -void digit2raw(const Configuration& cfg); int main(int argc, char** argv) { @@ -70,21 +30,10 @@ int main(int argc, char** argv) bpo::positional_options_description opt_pos; try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); - add_option("input-file,i", bpo::value()->default_value("fdddigits.root"), "input FDD digits file"); - add_option("flp-name", bpo::value()->default_value("alio2-cr1-flp201"), "single file per: all,flp,cru,link"); - add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); - add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); - uint32_t defRDH = o2::raw::RDHUtils::getVersion(); - add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); - add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); - add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); - add_option("lut-path", bpo::value()->default_value(""), "LookupTable path, e.g. FDD/LookupTable"); - add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + opt_general.add_options()("help,h", "Print this help message"); + // config with FDD defaults + o2::fit::DigitToRawConfig::configureExecOptions(opt_general, "fdddigits.root", "alio2-cr1-flp201"); + // common part opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -103,63 +52,12 @@ int main(int argc, char** argv) std::cerr << e.what() << ", application will now exit" << std::endl; exit(2); } - - std::string confDig = vm["hbfutils-config"].as(); - if (!confDig.empty() && confDig != "none") { - o2::conf::ConfigurableParam::updateFromFile(confDig, "HBFUtils"); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - const Configuration cfg(vm); + const o2::fit::DigitToRawConfig cfg(vm); if (!cfg.mEnablePadding) { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } else { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } o2::raw::HBFUtils::Instance().print(); - return 0; } - -template -void digit2raw(const Configuration& cfg) -{ - TStopwatch swTot; - swTot.Start(); - RawWriterType m2r; - m2r.setFileFor(cfg.mFileFor); - m2r.setFlpName(cfg.mFlpName); - m2r.setVerbosity(cfg.mVerbosity); - if (cfg.mCcdbPath != "") { - m2r.setCCDBurl(cfg.mCcdbPath); - } - if (cfg.mChannelMapPath != "") { - m2r.setLUTpath(cfg.mChannelMapPath); - } - auto& wr = m2r.getWriter(); - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::FDD)); // must be set explicitly - const int superPageSizeInB = 1024 * 1024; - wr.setSuperPageSize(superPageSizeInB); - wr.useRDHVersion(cfg.mRdhVersion); - wr.setDontFillEmptyHBF(cfg.mNoEmptyHBF); - wr.useRDHDataFormat(cfg.mDataFormat); - if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used - wr.setAlignmentSize(16); // change to constexpr static field from class? - wr.setAlignmentPaddingFiller(0xff); - } - o2::raw::assertOutputDirectory(cfg.mOutputDir); - - std::string outDirName(cfg.mOutputDir); - if (outDirName.back() != '/') { - outDirName += '/'; - } - const auto& hbfu = o2::raw::HBFUtils::Instance(); - long startTime = hbfu.startTime > 0 ? hbfu.startTime : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - m2r.convertDigitsToRaw(outDirName, cfg.mInputFile, startTime); - wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FDD/workflow/CMakeLists.txt b/Detectors/FIT/FDD/workflow/CMakeLists.txt index c3e47a0d58536..a4bcc6f0de6fb 100644 --- a/Detectors/FIT/FDD/workflow/CMakeLists.txt +++ b/Detectors/FIT/FDD/workflow/CMakeLists.txt @@ -52,6 +52,31 @@ o2_add_executable(flp-dpl-workflow PUBLIC_LINK_LIBRARIES O2::FDDWorkflow O2::FDDRaw O2::FITWorkflow TARGETVARNAME fddflpexe) +o2_add_executable(recpoints-reader-workflow + SOURCES src/recpoints-reader-workflow.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow) + +o2_add_executable(recpoints-writer-workflow + SOURCES src/recpoints-writer-workflow.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow) + +o2_add_executable(integrate-cluster-workflow + SOURCES src/cluster-integrator.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow O2::FITWorkflow) + +o2_add_executable(integrate-cluster-reader-workflow + SOURCES src/cluster-integrator-reader.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow O2::FITWorkflow) + +o2_add_executable(merge-integrate-cluster-workflow + SOURCES src/cluster-merge-integrator.cxx + COMPONENT_NAME fdd + PUBLIC_LINK_LIBRARIES O2::FDDWorkflow O2::FITWorkflow) + if(NOT APPLE) set_property(TARGET ${fddrecoexe} PROPERTY LINK_WHAT_YOU_USE ON) diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h index a6ee132ee0c34..1fd3cd7835cd9 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h index 87dcca02e869f..37d43f477e836 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fdd class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h deleted file mode 100644 index 6ed465b6181dd..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.h - -#ifndef O2_FDD_RAWDATAPROCESSSPEC_H -#define O2_FDD_RAWDATAPROCESSSPEC_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 "FDDRaw/DigitBlockFDD.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -class RawDataProcessSpec : public Task -{ - public: - RawDataProcessSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~RawDataProcessSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; -}; - -framework::DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor); - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h deleted file mode 100644 index c3b0349826e98..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.h - -#ifndef O2_FDD_RAWDATAREADERSPEC_H -#define O2_FDD_RAWDATAREADERSPEC_H - -#include "DataFormatsFDD/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "DetectorsRaw/RDHUtils.h" - -#include -#include -#include -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -template -class RawDataReaderSpec : public Task -{ - public: - RawDataReaderSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - RawDataReaderSpec() = default; - ~RawDataReaderSpec() override = default; - void init(InitContext& ic) final { o2::fdd::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - DPLRawParser parser(pc.inputs()); - mRawReader.clear(); - LOG(info) << "FDD RawDataReaderSpec"; - uint64_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), int(0)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - } - RawReader mRawReader; -}; - -template -framework::DataProcessorSpec getFDDRawDataReaderSpec(const RawReader& rawReader) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFDD"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - return DataProcessorSpec{ - "fdd-datareader-dpl", - o2::framework::select("TF:FDD/RAWDATA"), - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAREADERDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawReaderFDD.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawReaderFDD.h index e9612ae8c8bda..2d92c907c1d79 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawReaderFDD.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawReaderFDD.h @@ -65,8 +65,8 @@ class RawReaderFDD : public RawReaderFDDBaseNorm } void makeSnapshot(o2::framework::ProcessingContext& pc) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFDD, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFDD, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFDD, "DIGITSBC", 0}, mVecDigits); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFDD, "DIGITSCH", 0}, mVecChannelData); } bool mDumpData; std::vector mVecDigits; diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h deleted file mode 100644 index 3bbab66d16497..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FDD_RAWWORKFLOW_H -#define O2_FDD_RAWWORKFLOW_H - -/// @file RawWorkflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace fdd -{ -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointReaderSpec.h index 500883d5badfa..6c3c9694f3e1c 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointReaderSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecPointReaderSpec.h @@ -31,7 +31,7 @@ namespace fdd class RecPointReader : public Task { public: - RecPointReader(bool useMC = true); + RecPointReader(bool useMC = false); ~RecPointReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -42,7 +42,7 @@ class RecPointReader : public Task std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth + bool mUseMC = false; // use MC truth o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; std::vector* mRecPoints = nullptr; diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h index 2dbd854e34eee..0d5d308216bb0 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fdd } // namespace o2 #endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h index 7dcb5d9aaba40..8f20ff1513ab4 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h @@ -18,6 +18,8 @@ #include "Framework/Task.h" #include "FDDReconstruction/Reconstructor.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" +#include "Framework/ConcreteDataMatcher.h" using namespace o2::framework; @@ -29,21 +31,25 @@ namespace fdd class FDDReconstructorDPL : public Task { public: - FDDReconstructorDPL(bool useMC) : mUseMC(useMC) {} + FDDReconstructorDPL(bool useMC, bool useDeadChannelMap) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap) {} ~FDDReconstructorDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: bool mUseMC = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; std::vector mRecPoints; std::vector mRecChData; + o2::fit::DeadChannelMap const* mDeadChannelMap; o2::fdd::Reconstructor mReco; o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; }; /// create a processor spec -framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true); +framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true, bool useDeadChannelMap = true); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx index 441c679a327e3..628a2160c6d0c 100644 --- a/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/DigitReaderSpec.cxx @@ -81,18 +81,18 @@ void DigitReader::run(ProcessingContext& pc) mTree->GetEntry(ent); 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); + pc.outputs().snapshot(Output{mOrigin, "DIGITSBC", 0}, *digitsBC); + pc.outputs().snapshot(Output{mOrigin, "DIGITSCH", 0}, *digitsCh); if (mUseMC) { // TODO: To be replaced with sending ConstMCTruthContainer as soon as reco workflow supports it - pc.outputs().snapshot(Output{mOrigin, "TRIGGERINPUT", 0, Lifetime::Timeframe}, *digitsTrig); + pc.outputs().snapshot(Output{mOrigin, "TRIGGERINPUT", 0}, *digitsTrig); std::vector flatbuffer; mcTruthRootBuffer->copyandflatten(flatbuffer); o2::dataformats::MCTruthContainer mcTruth; mcTruth.restore_from(flatbuffer.data(), flatbuffer.size()); - pc.outputs().snapshot(Output{mOrigin, "DIGITLBL", 0, Lifetime::Timeframe}, mcTruth); + pc.outputs().snapshot(Output{mOrigin, "DIGITLBL", 0}, mcTruth); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx index 092f55128474d..33c140b5bc198 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -24,12 +24,12 @@ namespace o2 { namespace fdd { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setDictBinding("ctfdict_FDD"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -50,8 +50,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_FDD"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& channels = pc.outputs().make>(OutputRef{"channels"}); @@ -72,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FDD", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -80,17 +80,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "FDD", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "FDD", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionary")); + inputs.emplace_back("ctf_FDD", "FDD", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FDD", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fdd-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx index 7f448c1d62d7b..be81f7ca7d3d4 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fdd { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,13 +47,13 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); auto channels = pc.inputs().get>("channels"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"FDD", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"FDD", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, channels); if (mSelIR) { mCTFCoder.getIRFramesSelector().clear(); @@ -69,12 +68,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FDD", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FDD", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FDD", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -82,12 +84,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "fdd-entropy-encoder", inputs, Outputs{{"FDD", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx deleted file mode 100644 index bf18db67672c2..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.cxx - -#include "FDDWorkflow/RawDataProcessSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -using namespace std; -void RawDataProcessSpec::init(InitContext& ic) -{ -} - -void RawDataProcessSpec::run(ProcessingContext& pc) -{ - LOG(info) << "RawDataProcessSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFDD::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getRawDataProcessSpec"; - return DataProcessorSpec{ - "fdd-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx deleted file mode 100644 index 631655d3038ec..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.cxx - -#include "FDDWorkflow/RawDataReaderSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx deleted file mode 100644 index c9e5c5be0c81d..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawWorkflow.cxx - -#include "FDDWorkflow/RawWorkflow.h" -#include "FDDWorkflow/RawDataProcessSpec.h" -#include "FDDWorkflow/RawDataReaderSpec.h" -#include "FDDWorkflow/DigitWriterSpec.h" -#include "FDDWorkflow/RawReaderFDD.h" -namespace o2 -{ -namespace fdd -{ - -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut) -{ - LOG(info) << "framework::WorkflowSpec getFDDWorkflow"; - framework::WorkflowSpec specs; - specs.emplace_back(o2::fdd::getFDDRawDataReaderSpec(RawReaderFDD{dumpReader})); - - if (useProcess) { - specs.emplace_back(o2::fdd::getFDDRawDataProcessSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::fdd::getFDDDigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx index 3da258a672611..3c4812c75b251 100644 --- a/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecPointReaderSpec.cxx @@ -11,16 +11,14 @@ /// @file RecPointReaderSpec.cxx -#include - -#include "TTree.h" - -#include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" #include "Framework/Logger.h" #include "FDDWorkflow/RecPointReaderSpec.h" #include "CommonUtils/NameConf.h" +#include + using namespace o2::framework; using namespace o2::fdd; @@ -51,8 +49,8 @@ void RecPointReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "FDD RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, *mChannelData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, *mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, *mChannelData); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx index a7d4c15af81bb..b464e689f7a75 100644 --- a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,14 @@ namespace o2 namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fdd::getFDDDigitReaderSpec(useMC)); } - specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC)); + specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fdd::getFDDRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx index 04670a06f8fec..1d5d599b5ee31 100644 --- a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx @@ -18,6 +18,7 @@ #include "FDDWorkflow/ReconstructorSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" +#include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -44,6 +45,11 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) // lblPtr = labels.get(); LOG(info) << "Ignoring MC info"; } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(info) << "Populating reconsturctor object with Dead Channel Map object"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.setDeadChannelMap(deadChannelMap.get()); + } int nDig = digitsBC.size(); mRecPoints.reserve(nDig); mRecChData.reserve(digitsCh.size()); @@ -53,21 +59,34 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) mReco.process(digit, channels, mRecPoints, mRecChData); } // do we ignore MC in this task? - LOG(info) << "FDD 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); + LOG(debug) << "FDD reconstruction pushes " << mRecPoints.size() << " RecPoints"; + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); } -DataProcessorSpec getFDDReconstructorSpec(bool useMC) +void FDDReconstructorDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("FDD", "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + return; + } +} + +DataProcessorSpec getFDDReconstructorSpec(bool useMC, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digitsBC", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digitsCh", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently FDDReconstructor does not consume and provide MC truth"; // inputSpec.emplace_back("labels", o2::header::gDataOriginFDD, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFDD, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECCHDATA", 0, Lifetime::Timeframe); @@ -75,7 +94,7 @@ DataProcessorSpec getFDDReconstructorSpec(bool useMC) "fdd-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FDD/workflow/src/cluster-integrator-reader.cxx b/Detectors/FIT/FDD/workflow/src/cluster-integrator-reader.cxx new file mode 100644 index 0000000000000..55c22c162d732 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/cluster-integrator-reader.cxx @@ -0,0 +1,23 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FITWorkflow/FITIntegrateClusterReaderSpec.h" +#include "DataFormatsFDD/RecPoint.h" +#include "Framework/runDataProcessing.h" + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + wf.emplace_back(o2::fit::getFITIntegrateClusterReaderSpec()); + return wf; +} diff --git a/Detectors/FIT/FDD/workflow/src/cluster-integrator.cxx b/Detectors/FIT/FDD/workflow/src/cluster-integrator.cxx new file mode 100644 index 0000000000000..d28b962aa94d8 --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/cluster-integrator.cxx @@ -0,0 +1,44 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FITWorkflow/FITIntegrateClusterSpec.h" +#include "DataFormatsFDD/RecPoint.h" +#include "FITWorkflow/FITIntegrateClusterWriterSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector options{ + ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"min-NChan", VariantType::Int, 0, {"Minimum NChan signal required to avoid noise"}}, + {"min-Ampl", VariantType::Int, 0, {"Minimum Ampl signal required to avoid noise"}}, + {"disable-root-output", VariantType::Bool, false, {"disable root-files output writers"}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + const bool disableWriter = cfgc.options().get("disable-root-output"); + const int minNChan = cfgc.options().get("min-NChan"); + const int minAmpl = cfgc.options().get("min-Ampl"); + wf.emplace_back(o2::fit::getFITIntegrateClusterSpec(disableWriter, minNChan, minAmpl)); + if (!disableWriter) { + wf.emplace_back(o2::fit::getFITIntegrateClusterWriterSpec()); + } + return wf; +} diff --git a/Detectors/FIT/FDD/workflow/src/cluster-merge-integrator.cxx b/Detectors/FIT/FDD/workflow/src/cluster-merge-integrator.cxx new file mode 100644 index 0000000000000..c6951cf3639ea --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/cluster-merge-integrator.cxx @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FITWorkflow/FITMergeIntegrateClusterSpec.h" +#include "DataFormatsFDD/RecPoint.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector 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; + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + wf.emplace_back(o2::fit::getFITMergeIntegrateClusterSpec()); + return wf; +} diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx index bcc42ebc2e086..b83e25557e760 100644 --- a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fdd::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx index 652ddb8bd2a29..888792425909b 100644 --- a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx @@ -38,6 +38,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -57,8 +58,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); - auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FDD/workflow/src/recpoints-reader-workflow.cxx b/Detectors/FIT/FDD/workflow/src/recpoints-reader-workflow.cxx new file mode 100644 index 0000000000000..fcef4cc46901f --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/recpoints-reader-workflow.cxx @@ -0,0 +1,57 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-reader-workflow.cxx +/// \brief FDD RecPoints reader workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "FDDWorkflow/RecPointReaderSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + WorkflowSpec specs; + DataProcessorSpec producer = o2::fdd::getFDDRecPointReaderSpec(!disableMC); + specs.push_back(producer); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(ctx, specs); + return specs; +} diff --git a/Detectors/FIT/FDD/workflow/src/recpoints-writer-workflow.cxx b/Detectors/FIT/FDD/workflow/src/recpoints-writer-workflow.cxx new file mode 100644 index 0000000000000..e53ccd14c30ab --- /dev/null +++ b/Detectors/FIT/FDD/workflow/src/recpoints-writer-workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-writer-workflow.cxx +/// \brief FDD RecPoints writer workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "FDDWorkflow/RecPointWriterSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::fdd::getFDDRecPointWriterSpec(!disableMC); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FT0/CMakeLists.txt b/Detectors/FIT/FT0/CMakeLists.txt index cd76f3a24fc28..2fbb4dfbbc332 100644 --- a/Detectors/FIT/FT0/CMakeLists.txt +++ b/Detectors/FIT/FT0/CMakeLists.txt @@ -18,5 +18,5 @@ add_subdirectory(raw) add_subdirectory(reconstruction) add_subdirectory(simulation) add_subdirectory(workflow) -add_subdirectory(calibration) add_subdirectory(macros) +add_subdirectory(calibration) \ No newline at end of file diff --git a/Detectors/FIT/FT0/base/README.md b/Detectors/FIT/FT0/base/README.md new file mode 100644 index 0000000000000..2ac1732118237 --- /dev/null +++ b/Detectors/FIT/FT0/base/README.md @@ -0,0 +1,18 @@ + + +# FT0 Base + +## Geometry + +The `o2::ft0::Geometry` class contains dimensions and information used for constructing the FT0 geometry as used in simulation. It also provides utility methods for retrieving the center locations of the detector cells. See the below example for how to use the `Geometry` class to query the FT0 cell locations. Note that these are the ideal locations, and that any misalignment is not considered. + +```cpp +o2::ft0::Geometry ft0Geometry; +ft0Geometry.calculateChannelCenter(); +TVector3 cellPosition = ft0Geometry.getChannelCenter(chId); +float x = cellPosition.X(); +float y = cellPosition.Y(); +float z = cellPosition.Z(); +``` diff --git a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h index fb8b6492354f1..2bf774859aa22 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FT0DigParam.h +/// \file FT0DigParam.h /// \brief Configurable digitization parameters #ifndef ALICEO2_FT0_DIG_PARAM @@ -29,25 +29,25 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mAmp_trsh = 100; // [ph.e] float mAmpRecordLow = -4; // integrate charge from float mAmpRecordUp = 15; // to [ns] - float mC_side_cable_cmps = 2.86; // ns - float mA_side_cable_cmps = 11.110; // ns + float hitTimeOffsetA = 0; ///< hit time offset on the A side [ns] + float hitTimeOffsetC = 0; ///< hit time offset on the C side [ns] int mtrg_central_trh = 600.; // channels int mtrg_semicentral_trh = 300.; // channels float mMip_in_V = 7; // MIP to mV float mPe_in_mip = 0.004; // invserse Np.e. in MIP 1./250. float mCfdShift = 1.66; // ns - float mCFDShiftPos = 1.47; //// shift positive part of CFD signal; distance between 0.3 of max amplitude to max + float mCFDShiftPos = 1.47; // shift positive part of CFD signal; distance between 0.3 of max amplitude to max float mCFDdeadTime = 15.6; // ns float mCharge2amp = 0.22; - float mNoiseVar = 0.1; // noise level - float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; - static constexpr short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) - static constexpr float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP - static constexpr short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time - - static constexpr float mMV_2_Nchannels = 2.2857143; // amplitude channel 7 mV ->16channels - static constexpr float mMV_2_NchannelsInverse = 0.437499997; // inverse amplitude channel 7 mV ->16channels + float mNoiseVar = 0.1; // noise level + float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; + short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP + short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + + float mMV_2_Nchannels = 2.2857143; // amplitude channel 7 mV ->16channels + float mMV_2_NchannelsInverse = 0.437499997; // inverse amplitude channel 7 mV ->16channels O2ParamDef(FT0DigParam, "FT0DigParam"); }; diff --git a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h index 0365e730828cb..83be3af4d5b71 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/Geometry.h @@ -65,11 +65,34 @@ class Geometry return o2::base::GeometryManager::getPNEntry(getDetID(), index); } + // NEW: + // Calculate the coordinates of all the channels. + void calculateChannelCenter(); + // Get the coordinates of the center of the channel channelId. + TVector3 getChannelCenter(UInt_t channelId) const { return mChannelCenter[channelId]; } + private: TVector3 mMCP[52]; TVector3 mAngles[28]; + std::array mChannelCenter; ///< NEW: Center of each channel in FT0 A (96) and C (112) combined. + + // Convert the local ordering of the channels to the official one and apply it to the channel map. + // localChannelOrder[local channel index] = official channel. + Int_t localChannelOrder[Nchannels] = { + 58, 56, 59, 57, 54, 52, 55, 53, 50, 49, 51, 48, 47, 45, 46, 44, 43, 42, 41, 40, + 61, 60, 63, 62, 14, 12, 15, 13, 10, 9, 11, 8, 7, 6, 5, 4, 39, 38, 37, 36, + 65, 64, 66, 67, 17, 16, 18, 19, 3, 2, 0, 1, 35, 34, 32, 33, + 68, 69, 70, 71, 20, 21, 22, 23, 24, 27, 25, 26, 29, 31, 28, 30, 94, 95, 92, 93, + 72, 73, 74, 75, 76, 78, 77, 79, 80, 83, 81, 82, 85, 87, 84, 86, 89, 91, 88, 90, + 173, 172, 175, 174, 206, 207, 204, 205, 169, 168, 171, 170, 202, 203, 200, 201, + 117, 116, 119, 118, 142, 143, 140, 141, 114, 112, 115, 113, 137, 139, 136, 138, + 166, 164, 167, 165, 197, 199, 196, 198, 110, 108, 111, 109, 133, 135, 132, 134, + 162, 160, 163, 161, 193, 195, 192, 194, 107, 105, 106, 104, 128, 130, 129, 131, + 159, 157, 158, 156, 188, 190, 189, 191, 99, 98, 97, 96, 120, 121, 122, 123, + 103, 102, 101, 100, 124, 125, 126, 127, 155, 153, 154, 152, 184, 186, 185, 187, + 147, 146, 145, 144, 176, 177, 178, 179, 151, 150, 149, 148, 180, 181, 182, 183}; - ClassDefNV(Geometry, 2); + ClassDefNV(Geometry, 3); }; } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/base/src/Geometry.cxx b/Detectors/FIT/FT0/base/src/Geometry.cxx index de9b3ddad4c21..4c5fa004f63bb 100644 --- a/Detectors/FIT/FT0/base/src/Geometry.cxx +++ b/Detectors/FIT/FT0/base/src/Geometry.cxx @@ -117,3 +117,61 @@ void Geometry::setCsideModules() mMCP[i + NCellsA].SetXYZ(xc2[i], yc2[i], zc2[i]); } } +void Geometry::calculateChannelCenter() +{ + // This method calculates the position of each channel center composing both FT0-A + // and FT0-C, based on the position of their corresponding modules given by + // "Geometry::setAsideModules()" and "Geometry::setCsideModules()". + + // Ensure the positions of the modules are well defined. + setAsideModules(); + setCsideModules(); + + // Calculate first the positions for the channels for FT0-A. These correspond to the + // channels 0-95 in the modules mMCP[0-23]. + Double_t delta = 5.3 / 4.; // Half-channel width (TODO: ask if it actually corresponds to ChannelWidth) + Double_t xLocalChannels[Nchannels]; // x-positions of all channels ordered according to xi and internal numbering. + Double_t yLocalChannels[Nchannels]; // y-positions of all channels ordered according to yi and internal numbering. + Double_t zLocalChannels[Nchannels]; // z-positions of all channels ordered according to zi and internal numbering. + // INFO: We assume here the modules are perpendicular to z, so z(channel) = z(module). + + for (int iModA = 0; iModA < NCellsA; iModA++) { + xLocalChannels[4 * iModA + 0] = mMCP[iModA].X() - delta; + xLocalChannels[4 * iModA + 1] = mMCP[iModA].X() + delta; + xLocalChannels[4 * iModA + 2] = mMCP[iModA].X() - delta; + xLocalChannels[4 * iModA + 3] = mMCP[iModA].X() + delta; + + yLocalChannels[4 * iModA + 0] = mMCP[iModA].Y() + delta; + yLocalChannels[4 * iModA + 1] = mMCP[iModA].Y() + delta; + yLocalChannels[4 * iModA + 2] = mMCP[iModA].Y() - delta; + yLocalChannels[4 * iModA + 3] = mMCP[iModA].Y() - delta; + + zLocalChannels[4 * iModA + 0] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 1] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 2] = mMCP[iModA].Z(); + zLocalChannels[4 * iModA + 3] = mMCP[iModA].Z(); + } + + // Calculate then the positions for the channels for FT0-C, corresponding to the + // channels 96-207 in the modules mMCP[24-51]. + for (int iModC = 0; iModC < NCellsC; iModC++) { + xLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].X() - delta; + xLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].X() + delta; + xLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].X() - delta; + xLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].X() + delta; + + yLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].Y() + delta; + yLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].Y() + delta; + yLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].Y() - delta; + yLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].Y() - delta; + + zLocalChannels[4 * (iModC + NCellsA) + 0] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 1] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 2] = mMCP[iModC + NCellsA].Z(); + zLocalChannels[4 * (iModC + NCellsA) + 3] = mMCP[iModC + NCellsA].Z(); + } + + for (int iChannel = 0; iChannel < Nchannels; iChannel++) { + mChannelCenter[localChannelOrder[iChannel]].SetXYZ(xLocalChannels[iChannel], yLocalChannels[iChannel], zLocalChannels[iChannel]); + } +} diff --git a/Detectors/FIT/FT0/calibration/CMakeLists.txt b/Detectors/FIT/FT0/calibration/CMakeLists.txt index ac341d1a3db12..bee0493d300c1 100644 --- a/Detectors/FIT/FT0/calibration/CMakeLists.txt +++ b/Detectors/FIT/FT0/calibration/CMakeLists.txt @@ -10,63 +10,50 @@ # or submit itself to any jurisdiction. o2_add_library(FT0Calibration - SOURCES - src/FT0TimeOffsetSlotContainer.cxx - src/GlobalOffsetsContainer.cxx - src/FT0CalibTimeSlewing.cxx - src/FT0CalibCollector.cxx - PUBLIC_LINK_LIBRARIES - O2::CCDB - O2::MathUtils - O2::DataFormatsFT0 - O2::CommonDataFormat - O2::DetectorsCalibration - O2::DataFormatsGlobalTracking - ROOT::Minuit - Microsoft.GSL::GSL - ) - o2_target_root_dictionary(FT0Calibration - HEADERS - include/FT0Calibration/FT0TimeOffsetSlotContainer.h - include/FT0Calibration/GlobalOffsetsContainer.h - include/FT0Calibration/FT0CalibTimeSlewing.h - include/FT0Calibration/FT0CalibCollector.h - ) - o2_add_executable(ft0-time-offset-calib - COMPONENT_NAME calibration - SOURCES testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-time-spectra-processor - COMPONENT_NAME calibration - SOURCES testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-slew-upload - COMPONENT_NAME calibration - SOURCES testWorkflow/slew_upload.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-global-offsets - COMPONENT_NAME calibration - SOURCES testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-collect-global-offsets-workflow - COMPONENT_NAME calibration - SOURCES testWorkflow/GlobalOffsetsCollectWorkflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) - o2_add_executable(ft0-collect-calib-workflow - COMPONENT_NAME calibration - SOURCES testWorkflow/ft0-collect-calib-workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) + SOURCES + src/FT0TimeOffsetSlotContainer.cxx + src/EventsPerBcCalibrator.cxx + PUBLIC_LINK_LIBRARIES + O2::DetectorsCalibration + O2::Framework + O2::CommonUtils + Microsoft.GSL::GSL + O2::DataFormatsFT0 + O2::CommonDataFormat + O2::Steer + O2::CCDB + ROOT::Minuit + ROOT::Hist + ) -o2_data_file(COPY files DESTINATION Detectors/FT0/) +o2_target_root_dictionary(FT0Calibration + HEADERS + include/FT0Calibration/FT0TimeOffsetSlotContainer.h + include/FT0Calibration/EventsPerBcCalibrator.h + ) + +o2_add_executable(ft0-time-offset-calib + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeOffsetCalibration-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration O2::FITCalibration + ) + +o2_add_executable(ft0-time-spectra-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeSpectraProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + ) + +o2_add_executable(ft0-events-per-bc-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0EventsPerBcProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + O2::Framework + O2::CCDB +) \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/README.md b/Detectors/FIT/FT0/calibration/README.md new file mode 100644 index 0000000000000..78b0f980400d2 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/README.md @@ -0,0 +1,62 @@ +# Calibrations + +## Events per BC Calibration +### Description +Generates histograms of **Events per Bunch Crossing (BC)**. Events can be filtered by applying amplitude thresholds to the **A-side** and **C-side**. + +### Command-Line Options +| Option | Default | Description | +| :--- | :--- | :--- | +| `--slot-len-sec` | `3600` | Duration of each slot in seconds. | +| `--slot-len-tf` | `0` | Slot length in Time Frames (TFs). | +| `--one-object-per-run` | — | If set, the workflow creates only one calibration object per run. | +| `--min-entries-number` | `0` | Minimum number of entries required for a slot to be valid. | +| `--min-ampl-side-a` | `-2147483648` | Amplitude threshold for Side A events. | +| `--min-ampl-side-c` | `-2147483648` | Amplitude threshold for Side C events. | + +--- + +## How to Run + +### Simulation Data +First, it is important to digitize data with a non-zero run number, orbit, and timestamp. To set these parameters, one can use the `--configKeyValues` option, as shown in the example below. +``` +o2-sim-digitizer-workflow \ +--onlyDet FT0 \ +--configKeyValues="HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=128;HBFUtils.orbitFirstSampled=256;HBFUtils.runNumber=560560;HBFUtils.startTime=1768464099000" +``` + +To process simulation data, digits must first be converted to RAW format. The `o2-ft0-digi2raw` tool performs this conversion and generates the required configuration file. + +Once converted, you can run the calibration either as a single integrated workflow or by spawning as the sender and receiver components separately. + +#### Single Workflow Example +Execute the following command within the simulation directory: +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080 +``` + +Sender example (in simulation directory): +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-dpl-output-proxy --channel-config "name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq" --dataspec "downstream:FT0/DIGITSBC" +``` + +Receiver example: +``` +o2-dpl-raw-proxy --channel-config "name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" --dataspec "A:FT0/DIGITSBC/0" \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10 --min-ampl-side-a=0" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` + +### CTF Data +Example: +``` +o2-ctf-reader-workflow --ctf-input ctf.root --onlyDet FT0 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json b/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json deleted file mode 100644 index edaf486043790..0000000000000 --- a/Detectors/FIT/FT0/calibration/files/dispatcherCalib.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "dataSamplingPolicies": [ - { - "id": "sampling1", - "active": "true", - "machines": [ - "localhost" - ], - "query": "digits:FT0/DIGITSBC/0;channels:FT0/DIGITSCH/0", - "outputs": "digits_sampled:FT0/SUB_DIGITSBC/0;channels_sampled:FT0/SUB_DIGITSCH/0", - "samplingConditions": [ - { - "condition": "random", - "fraction": "0.99", - "seed": "1234" - } - ], - "blocking": "false" - } - ] -} diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h new file mode 100644 index 0000000000000..d831cc36201ab --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FT0TVXPERBCID +#define O2_FT0TVXPERBCID + +#include +#include +#include +#include + +#include "CommonDataFormat/FlatHisto2D.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "TH1F.h" +#include "Rtypes.h" + +namespace o2::ft0 +{ +struct EventsPerBcContainer { + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) {} + + size_t getEntries() const { return entries; } + void print() const; + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data); + void merge(const EventsPerBcContainer* prev); + + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::array mTvx{0.0}; + size_t entries{0}; + long startTimeStamp{0}; + long stopTimeStamp{0}; + + ClassDefNV(EventsPerBcContainer, 1); +}; + +class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration +{ + using Slot = o2::calibration::TimeSlot; + using TFType = o2::calibration::TFType; + using EventsHistogram = std::array; + + public: + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); + + bool hasEnoughData(const Slot& slot) const override; + void initOutput() override; + void finalizeSlot(Slot& slot) override; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) override; + + const std::vector& getTvxPerBc() { return mTvxPerBcs; } + std::vector>& getTvxPerBcCcdbInfo() { return mTvxPerBcInfos; } + + private: + const uint32_t mMinNumberOfEntries; + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::vector mTvxPerBcs; + std::vector> mTvxPerBcInfos; + + ClassDefOverride(EventsPerBcCalibrator, 1); +}; +} // namespace o2::ft0 + +#endif diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h deleted file mode 100644 index bf6f4ed6c3ad6..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibCollector.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef FT0_CALIB_COLLECTOR_H_ -#define FT0_CALIB_COLLECTOR_H_ - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "FT0Base/Geometry.h" - -#include - -namespace o2 -{ -namespace ft0 -{ - -class FT0CalibInfoSlot -{ - - using Slot = o2::calibration::TimeSlot; - using Geo = o2::ft0::Geometry; - - public: - static constexpr int NCHANNELS = Geo::Nchannels; - static constexpr int HISTO_RANGE = 200; - - FT0CalibInfoSlot() - { - for (int ch = 0; ch < NCHANNELS; ch++) { - mEntriesSlot[ch] = 0; - } - } - - ~FT0CalibInfoSlot() = default; - - void print() const; - void printEntries() const; - void fill(const gsl::span data); - void merge(const FT0CalibInfoSlot* prev); - - auto& getEntriesPerChannel() const { return mEntriesSlot; } - auto& getEntriesPerChannel() { return mEntriesSlot; } - auto& getCollectedCalibInfoSlot() { return mFT0CollectedCalibInfoSlot; } - auto& getCollectedCalibInfoSlot() const { return mFT0CollectedCalibInfoSlot; } - - private: - std::array mEntriesSlot; // vector containing number of entries per channel - std::vector mFT0CollectedCalibInfoSlot; ///< output FT0 calibration info - - ClassDefNV(FT0CalibInfoSlot, 1); -}; - -class FT0CalibCollector final : public o2::calibration::TimeSlotCalibration -{ - using TFType = o2::calibration::TFType; - using Slot = o2::calibration::TimeSlot; - static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; - - public: - FT0CalibCollector(bool TFsendingPolicy, int maxNumOfHits) : mTFsendingPolicy(TFsendingPolicy), mMaxNumOfHits(maxNumOfHits){}; - - ~FT0CalibCollector() final = default; - - bool hasEnoughData(const Slot& slot) const final; - void initOutput() final; - void finalizeSlot(Slot& slot) final; - Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) final; - auto& getCollectedCalibInfo() const { return mFT0CollectedCalibInfo; } - auto& getEntriesPerChannel() const { return mEntries; } - void setIsMaxNumberOfHitsAbsolute(bool absNumber) { mAbsMaxNumOfHits = absNumber; } - - private: - bool mTFsendingPolicy = false; // whether we will send information at every TF or only when we have a certain statistics - int mMaxNumOfHits = 1000000; // maximum number of hits for one single channel to trigger the sending of the information (if mTFsendingPolicy = false) - bool mAbsMaxNumOfHits = true; // to decide if the mMaxNumOfHits should be multiplied by the number of FT0 channels - std::array mEntries; // vector containing number of entries per channel - std::vector mFT0CollectedCalibInfo; ///< output FT0 calibration info - - ClassDefOverride(FT0CalibCollector, 1); -}; - -} // end namespace ft0 -} // end namespace o2 - -#endif /* FT0 CALIB COLLECTOR */ diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h deleted file mode 100644 index 41de777557c6a..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0CalibTimeSlewing.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CalibTimeSlewingParamFT0.h -/// \brief Class to store the output of the matching to FT0 for calibration - -#ifndef ALICEO2_FT0CALIBTIMESLEWING_H -#define ALICEO2_FT0CALIBTIMESLEWING_H - -#include -#include -#include -#include -#include -#include -#include "Rtypes.h" -#include -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "FT0Base/Geometry.h" -namespace o2::ft0 -{ -static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; -class FT0CalibTimeSlewing -{ - public: - static constexpr int HISTOGRAM_RANGE_X = 4000; - static constexpr unsigned int NUMBER_OF_HISTOGRAM_BINS_X = HISTOGRAM_RANGE_X / 4; - static constexpr int HISTOGRAM_RANGE_Y = 200; - static constexpr unsigned int NUMBER_OF_HISTOGRAM_BINS_Y = HISTOGRAM_RANGE_Y; - FT0CalibTimeSlewing(); - - FT0CalibTimeSlewing(const FT0CalibTimeSlewing& source) = default; - - FT0CalibTimeSlewing& operator=(const FT0CalibTimeSlewing& source) = default; - - float getChannelOffset(int channel, int amplitude) const; - - const TGraph getGraph(int channel) const { return mTimeSlewing[channel]; } - std::array getGraphs() const { return mTimeSlewing; } - void fillGraph(int channel, TH2F* histo); - - float getSigmaPeak(int channel) const { return mSigmaPeak[channel]; } - void setSigmaPeak(int channel, float value) { mSigmaPeak[channel] = value; } - - ///< perform all initializations - void init(); - void mergeFilesWithTree(); - void fillHistos(TTree* tr); - TH2F* getTimeAmpHist(int channel) { return mTimeAmpHist[channel]; }; - void setSingleFileName(std::string name) { mSingleFileName = name; } - void setMergedFileName(std::string name) { mMergedFileName = name; } - void setNfiles(int nfiles) { mNfiles = nfiles; }; - - FT0CalibTimeSlewing& operator+=(const FT0CalibTimeSlewing& other); - static constexpr const char* getObjectPath() { return "FT0/Calib/SlewingCorrection"; } - - private: - // FT0 channel calibrations - std::array mTimeSlewing; ///< array of TGraph wirh time -amplitude for each channel - std::array mSigmaPeak; ///< array with the sigma of the peak - TFile* mMergedFile; // file with merged tree - TH2F* mTimeAmpHist[NCHANNELS]; // historgams time vs amplitude - int mNfiles; // number of files with stored Tree with CalibrationInfoObject - std::string mSingleFileName; - std::string mMergedFileName; - - ClassDefNV(FT0CalibTimeSlewing, 1); // class for FT0 time slewing params -}; -} // namespace o2::ft0 -#endif diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h index 0786b0225b52c..2d7470d94e9e8 100644 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/FT0TimeOffsetSlotContainer.h @@ -17,6 +17,8 @@ #include "CommonDataFormat/FlatHisto2D.h" #include "DataFormatsFT0/SpectraInfoObject.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" #include "TList.h" @@ -27,12 +29,14 @@ namespace o2::ft0 class FT0TimeOffsetSlotContainer final { static constexpr int sNCHANNELS = o2::ft0::Geometry::Nchannels; + using TimeSlot = o2::calibration::TimeSlot; + using TimeSlotCalibration = o2::calibration::TimeSlotCalibration; public: FT0TimeOffsetSlotContainer(std::size_t minEntries); // constructor is needed due to current version of FITCalibration library, should be removed FT0TimeOffsetSlotContainer(FT0TimeOffsetSlotContainer const&) = default; FT0TimeOffsetSlotContainer(FT0TimeOffsetSlotContainer&&) = default; - FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer const&) = default; + FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer&) = default; FT0TimeOffsetSlotContainer& operator=(FT0TimeOffsetSlotContainer&&) = default; bool hasEnoughEntries() const; void fill(const gsl::span& data); @@ -42,6 +46,8 @@ class FT0TimeOffsetSlotContainer final TimeSpectraInfoObject generateCalibrationObject(long tsStartMS, long tsEndMS, const std::string& pathToHists) const; typedef float FlatHistoValue_t; typedef o2::dataformats::FlatHisto2D FlatHisto2D_t; + auto getHistogram() const { return mHistogram; } + auto isFirstTF() const { return mIsFirstTF; } private: // Slot number @@ -55,7 +61,8 @@ class FT0TimeOffsetSlotContainer final bool mIsReady{false}; // Once it is upper than max entry threshold it stops increasing std::array mArrEntries{}; - + // Total number of events + uint64_t mTotalNevents{0}; // Contains all information about time spectra FlatHisto2D_t mHistogram; ClassDefNV(FT0TimeOffsetSlotContainer, 1); diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h deleted file mode 100644 index c2f4c1b51ca8f..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/GlobalOffsetsContainer.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_GLOBALOFFSETSCONTAINER_H -#define O2_GLOBALOFFSETSCONTAINER_H - -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "DataFormatsFT0/GlobalOffsetsCalibrationObject.h" -#include "Rtypes.h" -#include -#include -#include -#include - -namespace o2::ft0 -{ -class GlobalOffsetsContainer final -{ - static constexpr int RANGE = 1000; - static constexpr unsigned int NBINS = 2 * RANGE; - - public: - explicit GlobalOffsetsContainer(std::size_t minEntries) - { - // mHisto.resize(NBINS, 0.); - } - - bool hasEnoughEntries() const; - void fill(const gsl::span& data); - int getMeanGaussianFitValue() const; - void merge(GlobalOffsetsContainer* prev); - void print() const; - GlobalOffsetsCalibrationObject generateCalibrationObject(long, long, const std::string&) const; - void updateFirstCreation(std::uint64_t creation) - { - if (creation < mFirstCreation) { - mFirstCreation = creation; - } - } - void resetFirstCreation() - { - - mFirstCreation = std::numeric_limits::max(); - } - std::uint64_t getFirstCreation() const - { - return mFirstCreation; - } - - private: - std::uint64_t mFirstCreation = std::numeric_limits::max(); - std::size_t mMinEntries; - std::array mHisto{}; - int mEntries = 0; - - ClassDefNV(GlobalOffsetsContainer, 2); -}; - -} // namespace o2::ft0 - -#endif // O2_GLOBALOFFSETCONTAINER_H diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h deleted file mode 100644 index 42e69c199b946..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/LHCClockCalibrator.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef FT0_LHCPHASE_CALIBRATION_H_ -#define FT0_LHCPHASE_CALIBRATION_H_ - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "DataFormatsFT0/LHCphaseCalibrationObject.h" -#include "CommonConstants/LHCConstants.h" -#include "CommonUtils/NameConf.h" -#include "CCDB/CcdbObjectInfo.h" -#include - -namespace o2 -{ -namespace ft0 -{ - -struct LHCClockDataHisto { - float range = 1000. * o2::constants::lhc::LHCBunchSpacingNS * 0.5; // BC in PS - int nbins = 1000; - float v2Bin = nbins / (2 * range); - int entries = 0; - std::vector histo{0}; - - LHCClockDataHisto(); - - LHCClockDataHisto(int nb, float r) : nbins(nb), range(r), v2Bin(0) - { - if (r <= 0. || nb < 1) { - throw std::runtime_error("Wrong initialization of the histogram"); - } - v2Bin = nbins / (2 * range); - histo.resize(nbins, 0.); - } - - size_t getEntries() const { return entries; } - void print() const; - void fill(const gsl::span& data); - void merge(const LHCClockDataHisto* prev); - - ClassDefNV(LHCClockDataHisto, 1); -}; - -class LHCClockCalibrator -{ - using TFType = o2::calibration::TFType; - using Slot = o2::calibration::TimeSlot; - using LHCphase = o2::ft0::LHCphaseCalibrationObject; - using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; - using CcdbObjectInfoVector = std::vector; - using LHCphaseVector = std::vector; - - public: - LHCClockCalibrator(int minEnt = 500, int nb = 1000, float r = 24400, const std::string path = o2::base::NameConf::getCCDBServer()) : mMinEntries(minEnt), mNBins(nb), mRange(r) {} - ~LHCClockCalibrator() = default; - bool hasEnoughData(const Slot& slot) const { return slot.getContainer()->entries >= mMinEntries; } - void initOutput(); - void finalizeSlot(Slot& slot); - Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend); - - const LHCphaseVector& getLHCphaseVector() const { return mLHCphaseVector; } - const CcdbObjectInfoVector& getLHCphaseInfoVector() const { return mInfoVector; } - CcdbObjectInfoVector& getLHCphaseInfoVector() { return mInfoVector; } - - private: - int mMinEntries = 0; - int mNBins = 0; - float mRange = 0.; - CcdbObjectInfoVector mInfoVector; // vector of CCDB Infos , each element is filled with the CCDB description of the accompanying LHCPhase - LHCphaseVector mLHCphaseVector; // vector of LhcPhase, each element is filled in "process" when we finalize one slot (multiple can be finalized during the same "process", which is why we have a vector. Each element is to be considered the output of the device, and will go to the CCDB - - ClassDef(LHCClockCalibrator, 1); -}; - -} // end namespace ft0 -} // end namespace o2 - -#endif /* FT0_LHCPHASE_CALIBRATION_H_ */ diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h deleted file mode 100644 index b2444ae45f15a..0000000000000 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/RecoCalibInfoWorkflow.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoCalibInfoWorkflow.h -///\ brief Collect data for global offsets calibration -/// \author Alla.Maevskaya@cern.ch - -#ifndef O2_RECOCALIBINFO_WORKFLOW -#define O2_RECOCALIBINFO_WORKFLOW - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" -#include "TStopwatch.h" -#include -#include - -using namespace o2::framework; -using DataRequest = o2::globaltracking::DataRequest; -using GID = o2::dataformats::GlobalTrackID; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - public: - RecoCalibInfoWorkflow() {} - void run(o2::framework::ProcessingContext& pc) final; - void init(InitContext& ic) final; - void endOfStream(framework::EndOfStreamContext& ec) final; - - private: - std::shared_ptr mDataRequest; - const float cSpeed = 0.029979246f; // speed of light in TOF units - GID::mask_t mInputSources; - TStopwatch mTimer; -}; -framework::DataProcessorSpec getRecoCalibInfoWorkflow(bool useMC); - -} // namespace o2::ft0 - -#endif /* O2_RECOCALIBINFO_WORKFLOW */ diff --git a/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt b/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt deleted file mode 100644 index 9125746b3a695..0000000000000 --- a/Detectors/FIT/FT0/calibration/macros/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -o2_add_test_root_macro( - makeChannelOffsetCalibObjectInCCDB.C - PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 O2::Framework O2::CCDB O2::DetectorsCalibration - LABELS ft0) diff --git a/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C b/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C deleted file mode 100644 index f184989c75bc3..0000000000000 --- a/Detectors/FIT/FT0/calibration/macros/makeChannelOffsetCalibObjectInCCDB.C +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include -#include "TFile.h" -#include "CCDB/CcdbApi.h" -#include "CCDB/CCDBTimeStampUtils.h" -#include "CCDB/CcdbObjectInfo.h" -#include "CommonUtils/MemFileHelper.h" -#include "CCDB/BasicCCDBManager.h" -#include -#include -#include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" -#endif - -int makeChannelOffsetCalibObjectInCCDB(const std::string url = "http://ccdb-test.cern.ch:8080") -{ - using CalibObjWithInfoType = std::pair>>; - std::array offsets; - for (int i = 0; i < 208; i++) { - offsets[i] = 0; - } - o2::ccdb::CcdbApi api; - api.init(url); - // std::map md; - CalibObjWithInfoType result; - o2::ft0::FT0ChannelTimeCalibrationObject calibrationObject; - static std::map metaData; - auto clName = o2::utils::MemFileHelper::getClassName(calibrationObject); - auto flName = o2::ccdb::CcdbApi::generateFileName(clName); - uint64_t starting = 1546300800; // 01.01.2019 - uint64_t stopping = o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP; // 1633046400; // 01.10.2021 - LOG(info) << " clName " << clName << " flName " << flName; - result.first = o2::ccdb::CcdbObjectInfo("FT0/Calib/ChannelTimeOffset", clName, flName, metaData, starting, stopping); - result.second = o2::ccdb::CcdbApi::createObjectImage(&offsets, &result.first); - LOG(info) << " start " << starting << " end " << stopping; - api.storeAsTFileAny(&calibrationObject, "FT0/Calib/ChannelTimeOffset", metaData, starting, stopping); - - return 0; -} diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx new file mode 100644 index 0000000000000..b17c81213cd08 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0Calibration/EventsPerBcCalibrator.h" +#include "CommonUtils/MemFileHelper.h" + +namespace o2::ft0 +{ +void EventsPerBcContainer::print() const +{ + LOG(info) << entries << " entries"; +} + +void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data) +{ + size_t oldEntries = entries; + for (const auto& digit : data) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { + mTvx[digit.mIntRecord.bc]++; + entries++; + } + } + LOG(debug) << "Container is filled with " << entries - oldEntries << " new events"; +} + +void EventsPerBcContainer::merge(const EventsPerBcContainer* prev) +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + mTvx[bc] += prev->mTvx[bc]; + } + entries += prev->entries; +} + +void EventsPerBcCalibrator::initOutput() +{ + mTvxPerBcs.clear(); + mTvxPerBcInfos.clear(); +} + +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) +{ + LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; + LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; + LOG(info) << "Defined threshold for side C amplitude for event: " << mMinAmplitudeSideC; +} + +bool EventsPerBcCalibrator::hasEnoughData(const EventsPerBcCalibrator::Slot& slot) const +{ + return slot.getContainer()->entries > mMinNumberOfEntries; +} + +void EventsPerBcCalibrator::finalizeSlot(EventsPerBcCalibrator::Slot& slot) +{ + LOG(info) << "Finalizing slot from " << slot.getStartTimeMS() << " to " << slot.getEndTimeMS(); + o2::ft0::EventsPerBcContainer* data = slot.getContainer(); + mTvxPerBcs.emplace_back(data->mTvx); + + auto clName = o2::utils::MemFileHelper::getClassName(mTvxPerBcs.back()); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + + std::map metaData; + mTvxPerBcInfos.emplace_back(std::make_unique("FT0/Calib/EventsPerBc", clName, flName, metaData, slot.getStartTimeMS(), slot.getEndTimeMS())); + LOG(info) << "Created object valid from " << mTvxPerBcInfos.back()->getStartValidityTimestamp() << " to " << mTvxPerBcInfos.back()->getEndValidityTimestamp(); +} + +EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) +{ + auto& cont = getSlots(); + auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude)); + return slot; +} +} // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx b/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx deleted file mode 100644 index acff7a13ceabe..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibCollector.cxx +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Calibration/FT0CalibCollector.h" -#include "Framework/Logger.h" -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace ft0 -{ - -using Slot = o2::calibration::TimeSlot; - -//_____________________________________________ -void FT0CalibInfoSlot::fill(const gsl::span data) -{ - // fill container - // we first order the data that arrived, to improve speed when filling - int nd = data.size(); - LOG(info) << "FT0CalibInfoSlot::fill entries in incoming data = " << nd; - std::vector ord(nd); - std::iota(ord.begin(), ord.end(), 0); - std::sort(ord.begin(), ord.end(), [&data](int i, int j) { return data[i].getChannelIndex() < data[j].getChannelIndex(); }); - int chPrev = 0, offsPrev = 0; - for (int i = 0; i < nd; i++) { - if (std::abs(data[ord[i]].getTime()) > HISTO_RANGE) { - continue; - } - const auto& dti = data[ord[i]]; - auto ch = dti.getChannelIndex(); - auto offset = offsPrev; - if (ch > chPrev) { - offset += std::accumulate(mEntriesSlot.begin() + chPrev, mEntriesSlot.begin() + ch, 0); - } - offsPrev = offset; - chPrev = ch; - auto it = mFT0CollectedCalibInfoSlot.emplace(mFT0CollectedCalibInfoSlot.begin() + offset, data[ord[i]].getChannelIndex(), data[ord[i]].getTime(), data[ord[i]].getAmp(), data[ord[i]].getTimeStamp()); - mEntriesSlot[ch]++; - } -} -//_____________________________________________ -void FT0CalibInfoSlot::merge(const FT0CalibInfoSlot* prev) -{ - // merge data of 2 slots - - LOG(info) << "Merging two slots with entries: current slot -> " << mFT0CollectedCalibInfoSlot.size() << " , previous slot -> " << prev->mFT0CollectedCalibInfoSlot.size(); - - int offset = 0, offsetPrev = 0; - std::vector tmpVector; - for (int ch = 0; ch < NCHANNELS; ch++) { - if (mEntriesSlot[ch] != 0) { - for (int i = offset; i < offset + mEntriesSlot[ch]; i++) { - tmpVector.emplace_back(mFT0CollectedCalibInfoSlot[i]); - } - offset += mEntriesSlot[ch]; - } - if (prev->mEntriesSlot[ch] != 0) { - for (int i = offsetPrev; i < offsetPrev + prev->mEntriesSlot[ch]; i++) { - tmpVector.emplace_back(prev->mFT0CollectedCalibInfoSlot[i]); - } - offsetPrev += prev->mEntriesSlot[ch]; - mEntriesSlot[ch] += prev->mEntriesSlot[ch]; - } - } - mFT0CollectedCalibInfoSlot.swap(tmpVector); - LOG(debug) << "After merging the size is " << mFT0CollectedCalibInfoSlot.size(); - return; -} -//_____________________________________________ -void FT0CalibInfoSlot::print() const -{ - // to print number of entries in the tree and the channel with the max number of entries - - LOG(info) << "Total number of entries " << mFT0CollectedCalibInfoSlot.size(); - auto maxElementIndex = std::max_element(mEntriesSlot.begin(), mEntriesSlot.end()); - auto channelIndex = std::distance(mEntriesSlot.begin(), maxElementIndex); - LOG(info) << "The maximum number of entries per channel in the current mFT0CollectedCalibInfo is " << *maxElementIndex << " for channel " << channelIndex; - return; -} - -//_____________________________________________ -void FT0CalibInfoSlot::printEntries() const -{ - // to print number of entries in the tree and per channel - - LOG(debug) << "Total number of entries " << mFT0CollectedCalibInfoSlot.size(); - for (int i = 0; i < mEntriesSlot.size(); ++i) { - if (mEntriesSlot[i] != 0) { - LOG(info) << "channel " << i << " has " << mEntriesSlot[i] << " entries"; - } - } - return; -} - -//=================================================================== - -//_____________________________________________ -void FT0CalibCollector::initOutput() -{ - // emptying the vectors - - mFT0CollectedCalibInfo.clear(); - for (int ch = 0; ch < NCHANNELS; ch++) { - mEntries[ch] = 0; - } - - return; -} - -//_____________________________________________ -bool FT0CalibCollector::hasEnoughData(const Slot& slot) const -{ - // We define that we have enough data if the tree is big enough. - // each FT0CalibrationInfoObject is composed of two int8 and one int16 --> 32 bytes - // E.g. supposing that we have 500000 entries per channel --> 500 eneries per one amplitude bin - // we can check if we have 500000*o2::ft0::Geometry::NCHANNELS entries in the vector - - const o2::ft0::FT0CalibInfoSlot* c = slot.getContainer(); - LOG(info) << "we have " << c->getCollectedCalibInfoSlot().size() << " entries"; - int maxNumberOfHits = mAbsMaxNumOfHits ? mMaxNumOfHits : mMaxNumOfHits * NCHANNELS; - if (mTFsendingPolicy || c->getCollectedCalibInfoSlot().size() > maxNumberOfHits) { - return true; - } - return false; -} - -//_____________________________________________ -void FT0CalibCollector::finalizeSlot(Slot& slot) -{ - - o2::ft0::FT0CalibInfoSlot* c = slot.getContainer(); - mFT0CollectedCalibInfo = c->getCollectedCalibInfoSlot(); - LOG(info) << "vector of received with size = " << mFT0CollectedCalibInfo.size(); - mEntries = c->getEntriesPerChannel(); - return; -} - -//_____________________________________________ -Slot& FT0CalibCollector::emplaceNewSlot(bool front, TFType tstart, TFType tend) -{ - - LOG(info) << "FIT_CALIBRATOR_TYPE::emplaceNewSlot " - << " start " << tstart << " end " << tend; - auto& cont = getSlots(); - auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique()); - return slot; -} - -} // end namespace ft0 -} // end namespace o2 diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx b/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx deleted file mode 100644 index 3ea4fcb788a99..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibTimeSlewing.cxx +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// \file FT0CalibTimeSlewing.cxx -/// \brief Class for slewing calibration object -/// -#include -#include -#include -#include -#include -#include -#include -#include "FT0Calibration/FT0CalibTimeSlewing.h" - -using namespace o2::ft0; - -FT0CalibTimeSlewing::FT0CalibTimeSlewing() -{ - for (int iCh = 0; iCh < NCHANNELS; iCh++) { - mSigmaPeak[iCh] = -1.; - mTimeAmpHist[iCh] = new TH2F(Form("hTimeAmpHist%d", iCh), Form("TimeAmp%d", iCh), - NUMBER_OF_HISTOGRAM_BINS_X, 0, HISTOGRAM_RANGE_X, - NUMBER_OF_HISTOGRAM_BINS_Y, -HISTOGRAM_RANGE_Y, HISTOGRAM_RANGE_Y); - } -} - -//______________________________________________ -float FT0CalibTimeSlewing::getChannelOffset(int channel, int amplitude) const -{ - return mTimeSlewing[channel].Eval(amplitude); -} - -//______________________________________________ -void FT0CalibTimeSlewing::fillGraph(int channel, TH2F* histo) -{ - LOG(info) << "FT0CalibTimeSlewing::fillGraph " << channel << " entries " << int(histo->GetEntries()); - double shiftchannel = 0; - TH1D* hist_Proj = histo->ProjectionY(); - TFitResultPtr res = hist_Proj->Fit("gaus", "SQ"); - if ((Int_t)res == 0) { - shiftchannel = res->Parameter(1); - } - Double_t xgr[NUMBER_OF_HISTOGRAM_BINS_X] = {}; - Double_t ygr[NUMBER_OF_HISTOGRAM_BINS_X] = {}; - TH1D* proj = nullptr; - int nbins = 0; - for (int ibin = 1; ibin < NUMBER_OF_HISTOGRAM_BINS_X; ibin++) { - xgr[ibin] = histo->GetXaxis()->GetBinCenter(ibin); - proj = histo->ProjectionY(Form("proj_px%i", ibin), ibin, ibin + 1); - if (proj->GetEntries() < 500) { - ygr[ibin] = 0; - continue; - } - TFitResultPtr r = proj->Fit("gaus", "SQ"); - if ((Int_t)r == 0) { - ygr[ibin] = r->Parameter(1) - shiftchannel; - } - nbins++; - LOG(info) << "channel " << channel << " bin " << ibin << " x " << xgr[ibin] << " y " << ygr[ibin] << " ent " << proj->GetEntries() << " sigma " << r->Parameter(2) << " shiftchannel " << shiftchannel; - } - TGraph* grTimeAmp = new TGraph(nbins + 5, xgr, ygr); - mTimeSlewing[channel] = *grTimeAmp; -} -//______________________________________________ -FT0CalibTimeSlewing& FT0CalibTimeSlewing::operator+=(const FT0CalibTimeSlewing& other) -{ - for (int i = 0; i < NCHANNELS; i++) { - mTimeSlewing[i] = other.mTimeSlewing[i]; - mSigmaPeak[i] = other.mSigmaPeak[i]; - } - return *this; -} - -//______________________________________________ -void FT0CalibTimeSlewing::mergeFilesWithTree() -{ - TFileMerger merger; - merger.OutputFile(mMergedFileName.c_str()); - for (Int_t i = 0; i < mNfiles; i++) { - TFile* file = - TFile::Open(Form("%s_%d.root", mSingleFileName.c_str(), i)); - if (file) { - merger.AddAdoptFile(file); - } - } - if (!merger.Merge()) { - LOG(fatal) << "Could not merge files"; - } - TFile mMergedFile{merger.GetOutputFileName()}; - TTree* tr = (TTree*)mMergedFile.Get("treeCollectedCalibInfo"); - if (!tr) { - LOG(fatal) << "Could not get tree with calib info"; - } - fillHistos(tr); -} - -//______________________________________________ -void FT0CalibTimeSlewing::fillHistos(TTree* tr) -{ - std::vector* localCalibInfoFT0 = nullptr; - if (!tr->GetBranch("FT0CollectedCalibInfo")) { - LOG(fatal) << "Did not find collected FT0 calib info branch in the input tree"; - } - tr->SetBranchAddress("FT0CollectedCalibInfo", &localCalibInfoFT0); - for (Int_t ievent = 0; ievent < tr->GetEntries(); ievent++) { - tr->GetEvent(ievent); - for (auto& info : *localCalibInfoFT0) { - LOG(debug) << " ch " << int(info.getChannelIndex()) << " time " << info.getTime() << " amp " << info.getAmp(); - int iCh = info.getChannelIndex(); - mTimeAmpHist[iCh]->Fill(info.getAmp(), info.getTime()); - } - } - delete localCalibInfoFT0; -} diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h index 1facc4be739fc..11b1ce25e9353 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h +++ b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h @@ -16,14 +16,9 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::FT0TimeOffsetSlotContainer + ; -#pragma link C++ class o2::ft0::GlobalOffsetsContainer + ; -#pragma link C++ class o2::ft0::FT0CalibTimeSlewing + ; -#pragma link C++ class o2::ft0::FT0CalibCollector + ; -#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0CalibInfoSlot>; -#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0CalibInfoSlot>; +#pragma link C++ class o2::ft0::EventsPerBcCalibrator + ; #pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0TimeOffsetSlotContainer>; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0TimeOffsetSlotContainer>; -#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::GlobalOffsetsContainer>; -#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::GlobalOffsetsContainer>; - +#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::EventsPerBcContainer> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::EventsPerBcContainer> + ; #endif diff --git a/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx b/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx index d7e0ae742d59b..1e31d24f8c20d 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx +++ b/Detectors/FIT/FT0/calibration/src/FT0TimeOffsetSlotContainer.cxx @@ -25,7 +25,11 @@ FT0TimeOffsetSlotContainer::FT0TimeOffsetSlotContainer(std::size_t minEntries) { bool FT0TimeOffsetSlotContainer::hasEnoughEntries() const { - if (mIsReady) { + if (mTotalNevents == 0) { + // Dummy slot, should be ignored for protection + LOG(warning) << "RESULT: Empty slot, ignoring"; + return false; + } else if (mIsReady) { // ready : bad+good == NChannels (i.e. no pending channel) LOG(info) << "RESULT: ready"; print(); @@ -78,6 +82,7 @@ void FT0TimeOffsetSlotContainer::fill(const gsl::span& data) nEntries += en; } mArrEntries[iCh] = nEntries; + mTotalNevents += nEntries; if (nEntries >= CalibParam::Instance().mMaxEntriesThreshold) { mBitsetGoodChIDs.set(iCh); } @@ -91,6 +96,14 @@ void FT0TimeOffsetSlotContainer::fill(const gsl::span& data) void FT0TimeOffsetSlotContainer::merge(FT0TimeOffsetSlotContainer* prev) { LOG(info) << "MERGING"; + if (mIsFirstTF && prev->isFirstTF()) { + // nothing to be done + return; + } else if (mIsFirstTF && !prev->isFirstTF()) { + // need to make mHistogram operational first + mHistogram.init(prev->getHistogram().getNBinsX(), prev->getHistogram().getXMin(), prev->getHistogram().getXMax(), prev->getHistogram().getNBinsY(), prev->getHistogram().getYMin(), prev->getHistogram().getYMax()); + mIsFirstTF = false; + } *this = *prev; if (mCurrentSlot == 0) { // This part should at the stage `hasEnoughData()` but it is const method diff --git a/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx b/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx deleted file mode 100644 index d768edc856952..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/GlobalOffsetsContainer.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/Logger.h" -#include "MathUtils/fit.h" -#include "CommonUtils/MemFileHelper.h" -#include "CCDB/CcdbApi.h" -#include "DetectorsCalibration/Utils.h" -#include "FT0Calibration//GlobalOffsetsContainer.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include -#include -#include "MathUtils/fit.h" -#include -#include - -using namespace o2::ft0; -using o2::math_utils::fitGaus; - -bool GlobalOffsetsContainer::hasEnoughEntries() const -{ - return mEntries < mMinEntries; -} -void GlobalOffsetsContainer::fill(const gsl::span& data) -{ - // fill container - for (auto& entry : data) { - if (std::abs(entry.getT0AC()) < RANGE) { - updateFirstCreation(entry.getTimeStamp()); - auto time = entry.getT0AC(); - time += RANGE; - mHisto[(time)]++; - mEntries++; - } - } -} -void GlobalOffsetsContainer::merge(GlobalOffsetsContainer* prev) -{ - // merge data of 2 slots - for (int i = mHisto.size(); i--;) { - mHisto[i] += prev->mHisto[i]; - } - mEntries += prev->mEntries; - mFirstCreation = std::min(mFirstCreation, prev->mFirstCreation); -} - -int GlobalOffsetsContainer::getMeanGaussianFitValue() const -{ - if (!hasEnoughEntries()) { - return 0; - } - if (mEntries == 0) { - return 0; - } - float sum = 0; - for (int ic = 0; ic < NBINS; ic++) { - sum += float(ic - RANGE) * float(mHisto[ic]); - // LOG(info)<<" histo "< fitValues; - double fitres = fitGaus(NBINS, mHisto.data(), -RANGE, RANGE, fitValues); - if (fitres >= 0) { - LOG(info) << "Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2] << " size " << mEntries << " mean " << sum / mEntries; - return (static_cast(std::round(fitValues[1]))); - } else { - LOG(warning) << "Fit failed with result = " << fitres; - return 0; - } -} -GlobalOffsetsCalibrationObject GlobalOffsetsContainer::generateCalibrationObject(long, long, const std::string&) const -{ - GlobalOffsetsCalibrationObject calibrationObject; - calibrationObject.mCollisionTimeOffsets = getMeanGaussianFitValue(); - LOG(info) << "GlobalOffsetsCalibrationObjectAlgorithm generate CalibrationObject for T0" - << " = " << calibrationObject.mCollisionTimeOffsets; - return calibrationObject; -} - -void GlobalOffsetsContainer::print() const -{ - LOG(info) << "Container keep data for LHC phase calibration:"; - LOG(info) << "Gaussian mean time AC side " << getMeanGaussianFitValue() << " based on" << mEntries << " entries"; -} diff --git a/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx deleted file mode 100644 index 9de69b8748e60..0000000000000 --- a/Detectors/FIT/FT0/calibration/src/RecoCalibInfoWorkflow.cxx +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file RecoCalibInfoWorkflow.cxx -///\ brief Collect data for global offsets calibration -/// \author Alla.Maevskaya@cern.ch - -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "FT0Calibration/RecoCalibInfoWorkflow.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" -#include -#include -#include - -using namespace o2::framework; -using namespace o2::math_utils::detail; -using PVertex = o2::dataformats::PrimaryVertex; -using GIndex = o2::dataformats::VtxTrackIndex; -using DataRequest = o2::globaltracking::DataRequest; - -namespace o2::ft0 -{ -void RecoCalibInfoWorkflow::init(InitContext& ic) -{ - mTimer.Stop(); - mTimer.Reset(); -} -void RecoCalibInfoWorkflow::run(o2::framework::ProcessingContext& pc) -{ - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - auto primVertices = recoData.getPrimaryVertices(); - auto ft0RecPoints = recoData.getFT0RecPoints(); - std::map bcsMap; - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.reserve(ft0RecPoints.size()); - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - LOG(debug) << "PrimVertices " << globalBC; - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) { - iter->second = nullptr; - } - } - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - auto item = bcsMap.find(bc); - LOG(debug) << " <second == nullptr) { - LOG(debug) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(debug) << "CurrentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont < 3) { - continue; - } - auto shift = currentVertex / cSpeed; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - LOG(debug) << " BC t0 " << bc << " shift " << shift << " A " << t0A << " C " << t0C << " AC " << t0AC; - calib_data.emplace_back(t0A, t0C, t0AC); - } - mTimer.Stop(); -} -void RecoCalibInfoWorkflow::endOfStream(EndOfStreamContext& ec) -{ - LOGF(info, "Reco calib info workflow dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); -} - -DataProcessorSpec getRecoCalibInfoWorkflow(GID::mask_t src, bool useMC) -{ - auto dataRequest = std::make_shared(); - dataRequest->requestPrimaryVertertices(false); - dataRequest->requestFT0RecPoints(false); - - return DataProcessorSpec{ - "ft0-calib-reco", - dataRequest->inputs, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask(src, dataRequest)}, - Options{}}; -} - -}; // namespace o2::ft0 diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h deleted file mode 100644 index 217561d815c91..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibCollectorWriterSpec.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_FT0CALIB_COLLECTOR_WRITER_H -#define O2_CALIBRATION_FT0CALIB_COLLECTOR_WRITER_H - -/// @file FT0CalibCollectorWriterSpec.h -/// @brief Device to write to tree the information for FT0 time slewing calibration. - -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace calibration -{ - -class FT0CalibCollectorWriter : public o2::framework::Task -{ - - using Geo = o2::ft0::Geometry; - - public: - void createAndOpenFileAndTree() - { - TString filename = TString::Format("collFT0%d.root", mCount); - LOG(debug) << "opening file " << filename.Data(); - mfileOut.reset(TFile::Open(TString::Format("%s", filename.Data()), "RECREATE")); - mOutputTree = std::make_unique("treeCollectedCalibInfo", "Tree with FT0 calib info for Time Slewing"); - mOutputTree->Branch(mOutputBranchName.data(), &mPFT0CalibInfoOut); - LOG(info) << " @@@@@ createAndOpenFileAndTree tree set"; - } - - void init(o2::framework::InitContext& ic) final - { - mCount = 0; - createAndOpenFileAndTree(); - mFT0CalibInfoOut.reserve(1000000 * Geo::Nchannels); // tree size 216ch * 10^6 entries * 12 byte - LOG(info) << " @@@@@ init"; - } - - void run(o2::framework::ProcessingContext& pc) final - { - auto collectedInfo = pc.inputs().get>("collectedInfo"); - auto entriesPerChannel = pc.inputs().get>("entriesCh"); - int offsetStart = 0; - for (int ich = 0; ich < Geo::Nchannels; ich++) { - mFT0CalibInfoOut.clear(); - if (entriesPerChannel[ich] > 0) { - mFT0CalibInfoOut.resize(entriesPerChannel[ich]); - auto subSpanVect = collectedInfo.subspan(offsetStart, entriesPerChannel[ich]); - memcpy(&mFT0CalibInfoOut[0], subSpanVect.data(), sizeof(o2::ft0::FT0CalibrationInfoObject) * subSpanVect.size()); - const o2::ft0::FT0CalibrationInfoObject* tmp = subSpanVect.data(); - LOG(debug) << "@@@@@ run ich " << ich << " entries " << entriesPerChannel[ich]; - } - mOutputTree->Fill(); - offsetStart += entriesPerChannel[ich]; - } - sendOutput(pc.outputs()); - } - - void endOfStream(o2::framework::EndOfStreamContext& ec) final - { - mIsEndOfStream = true; - sendOutput(ec.outputs()); - } - - private: - int mCount = 0; // how many times we filled the tree - bool mIsEndOfStream = false; - std::vector mFT0CalibInfoOut, *mPFT0CalibInfoOut = &mFT0CalibInfoOut; ///< these are the object and pointer to the CalibInfo of a specific channel that we need to fill the output tree - std::unique_ptr mOutputTree; ///< tree for the collected calib FT0 info - std::string mFT0CalibInfoBranchName = "FT0CalibInfo"; ///< name of branch containing input FT0 calib infos - std::string mOutputBranchName = "FT0CollectedCalibInfo"; ///< name of branch containing output - std::unique_ptr mfileOut = nullptr; // file in which to write the output - - //________________________________________________________________ - void sendOutput(DataAllocator& output) - { - // This is to fill the tree. - // One file with an empty tree will be created at the end, because we have to have a - // tree opened before processing, since we do not know a priori if something else - // will still come. The size of this extra file is ~6.5 kB - - mfileOut->cd(); - mOutputTree->Write(); - mOutputTree->Reset(); - mCount++; - if (!mIsEndOfStream) { - createAndOpenFileAndTree(); - } - } -}; -} // namespace calibration - -namespace framework -{ - -DataProcessorSpec getFT0CalibCollectorWriterSpec() -{ - LOG(debug) << " @@@@ getFT0CalibCollectorWriterSpec "; - using device = o2::calibration::FT0CalibCollectorWriter; - std::vector inputs; - inputs.emplace_back("collectedInfo", o2::header::gDataOriginFT0, "COLLECTEDINFO"); - inputs.emplace_back("entriesCh", o2::header::gDataOriginFT0, "ENTRIESCH"); - - std::vector outputs; // empty - - return DataProcessorSpec{ - "ft0-calib-collector-writer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask()}, - Options{}}; -} - -} // namespace framework -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h deleted file mode 100644 index e01fed6f20588..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0CalibSlewingCollectorSpec.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_FT0CALIB_SLEWING_COLLECTOR_H -#define O2_CALIBRATION_FT0CALIB_SLEWING_COLLECTOR_H - -/// @file FT0CalibSlewingCollectorSpec.h -/// @brief Device to collect information for FT0 time slewing calibration. - -//#include "FT0Calibration/FT0CollectCalibInfo.h" -#include "FT0Calibration/FT0CalibCollector.h" -#include "DetectorsCalibration/Utils.h" -#include "DataFormatsFT0/FT0CalibrationInfoObject.h" -#include "Framework/Task.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/WorkflowSpec.h" -#include -#include "DetectorsBase/GRPGeomHelper.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0CalibCollectorDevice : public o2::framework::Task -{ - - public: - FT0CalibCollectorDevice(std::shared_ptr req) : mCCDBRequest(req) {} - void init(o2::framework::InitContext& ic) final - { - o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); - bool isTFsendingPolicy = ic.options().get("tf-sending-policy"); - int maxEnt = ic.options().get("max-number-hits-to-fill-tree"); - auto slotL = ic.options().get("tf-per-slot"); - bool absMaxEnt = ic.options().get("is-max-number-hits-to-fill-tree-absolute"); - mCollector = std::make_unique(isTFsendingPolicy, maxEnt); - mCollector->setSlotLength(slotL); - } - - void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final - { - o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); - } - - void run(o2::framework::ProcessingContext& pc) final - { - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCollector->getCurrentTFInfo()); - auto data = pc.inputs().get>("input"); - mCollector->process(data); - sendOutput(pc.outputs()); - } - - void endOfStream(o2::framework::EndOfStreamContext& ec) final - { - mCollector->checkSlotsToFinalize(o2::calibration::INFINITE_TF); - // we force finalizing slot zero (unless everything was already finalized), no matter how many entries we had - if (mCollector->getNSlots() != 0) { - mCollector->finalizeSlot(mCollector->getSlot(0)); - } - sendOutput(ec.outputs()); - } - - private: - std::unique_ptr mCollector; - std::shared_ptr mCCDBRequest; - int mMaxNumOfHits = 0; - - //________________________________________________________________ - void sendOutput(DataAllocator& output) - { - // in output we send the calibration tree - auto& collectedInfo = mCollector->getCollectedCalibInfo(); - LOG(debug) << "In CollectorSpec sendOutput: size = " << collectedInfo.size(); - if (collectedInfo.size()) { - auto entries = collectedInfo.size(); - // this means that we are ready to send the output - auto entriesPerChannel = mCollector->getEntriesPerChannel(); - output.snapshot(Output{o2::header::gDataOriginFT0, "COLLECTEDINFO", 0, Lifetime::Timeframe}, collectedInfo); - output.snapshot(Output{o2::header::gDataOriginFT0, "ENTRIESCH", 0, Lifetime::Timeframe}, entriesPerChannel); - mCollector->initOutput(); // reset the output for the next round - } - } -}; - -} // namespace ft0 - -namespace framework -{ - -DataProcessorSpec getFT0CalibCollectorDeviceSpec() -{ - using device = o2::ft0::FT0CalibCollectorDevice; - using clbUtils = o2::calibration::Utils; - - std::vector outputs; - outputs.emplace_back(o2::header::gDataOriginFT0, "COLLECTEDINFO", 0, Lifetime::Timeframe); - outputs.emplace_back(o2::header::gDataOriginFT0, "ENTRIESCH", 0, Lifetime::Timeframe); - - std::vector inputs; - inputs.emplace_back("input", "FT0", "CALIB_INFO"); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - return DataProcessorSpec{ - "calib-ft0calib-collector", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest)}, - Options{ - {"max-number-hits-to-fill-tree", VariantType::Int, 1000, {"maximum number of entries in one channel to trigger teh filling of the tree"}}, - {"is-max-number-hits-to-fill-tree-absolute", VariantType::Bool, false, {"to decide if we want to multiply the max-number-hits-to-fill-tree by the number of channels (when set to true), or not (when set to false) for fast checks"}}, - {"tf-sending-policy", VariantType::Bool, false, {"if we are sending output at every TF; otherwise, we use the max-number-hits-to-fill-tree"}}, - {"tf-per-slot", o2::framework::VariantType::UInt32, 200000u, {"TF per slot "}}}}; -} - -} // namespace framework - -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx deleted file mode 100644 index 9a9c8e078322a..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0SlewingCalibrationWorkflow.cxx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0SlewingCalibrationSpec.h" - -void customize(std::vector& workflowOptions) -{ - //probably some option will be added -} - -#include "Framework/runDataProcessing.h" - -using namespace o2::framework; -WorkflowSpec defineDataProcessing(ConfigContext const& config) -{ - WorkflowSpec workflow; - workflow.emplace_back(o2::ft0::getFT0SlewingCalibrationSpec()); - //add calib spec here... - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h deleted file mode 100644 index d8e3414ca83ff..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationSpec.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H -#define O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H - -/// @file LHCClockCalibratorSpec.h -/// @brief Device to calibrate LHC clock phase using FT0 data - -#include "Framework/DataProcessorSpec.h" -#include "DataFormatsFT0/GlobalOffsetsCalibrationObject.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "FT0Calibration/GlobalOffsetsContainer.h" -#include "FITCalibration/FITCalibrationDevice.h" - -using namespace o2::framework; -namespace o2::ft0 -{ -o2::framework::DataProcessorSpec getGlobalOffsetsCalibrationSpec() -{ - using CalibrationDeviceType = o2::fit::FITCalibrationDevice; - LOG(info) << " getGlobalOffsetsCalibrationSpec()"; - - std::vector inputs; - std::vector outputs; - const o2::header::DataDescription inputDataDescriptor{"CALIB_INFO"}; - const o2::header::DataDescription outputDataDescriptor{"FT0_GLTIME_CALIB"}; - CalibrationDeviceType::prepareVecInputSpec(inputs, o2::header::gDataOriginFT0, inputDataDescriptor); - CalibrationDeviceType::prepareVecOutputSpec(outputs, outputDataDescriptor); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - return o2::framework::DataProcessorSpec{ - "ft0-global-offsets", - inputs, // o2::framework::Inputs{{"input", "FT0", "CALIBDATA"}}, - outputs, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(ccdbRequest, outputDataDescriptor)}, - Options{ - {"tf-per-slot", VariantType::UInt32, 55000u, {"number of TFs per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, - {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}, - {"extra-info-per-slot", o2::framework::VariantType::String, "", {"Extra info for time slot(usually for debugging)"}}}}; -} - -} // namespace o2::ft0 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx deleted file mode 100644 index f355a96a4a792..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCalibrationWorkflow.cxx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "GlobalOffsetsCalibrationSpec.h" - -void customize(std::vector& workflowOptions) -{ - //probably some option will be added -} - -#include "Framework/runDataProcessing.h" - -using namespace o2::framework; -WorkflowSpec defineDataProcessing(ConfigContext const& config) -{ - - WorkflowSpec workflow; - workflow.emplace_back(o2::ft0::getGlobalOffsetsCalibrationSpec()); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx deleted file mode 100644 index 3a90816f7d5c2..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/GlobalOffsetsCollectWorkflow.cxx +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/ConfigParamSpec.h" -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/GlobalOffsetsInfoObject.h" -#include "DataFormatsFT0/RecPoints.h" -#include "Framework/Logger.h" -using namespace o2::framework; - -namespace o2::ft0 -{ - -class GlobalOffsetsCollectWorkflow final : public o2::framework::Task -{ - - public: - void run(o2::framework::ProcessingContext& pc) final - { - auto creationTime = pc.services().get().creation; // approximate time in ms - auto recpoints = pc.inputs().get>("recpoints"); - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - for (const auto& recpoint : recpoints) { - short t0AC = recpoint.getCollisionTimeMean(); - if (std::abs(t0AC) < 1000) { - calib_data.emplace_back(t0AC, uint64_t(creationTime)); - } - } - } -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - Inputs inputs{}; - inputs.push_back(InputSpec{{"recpoints"}, "FT0", "RECPOINTS"}); - - DataProcessorSpec dataProcessorSpec{ - "collect-global-offsets", - inputs, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h b/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h deleted file mode 100644 index 6dfaa636e1322..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/LHCClockCalibratorSpec.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H -#define O2_CALIBRATION_LHCCLOCK_CALIBRATOR_H - -/// @file LHCClockCalibratorSpec.h -/// @brief Device to calibrate LHC clock phase using FT0 data - -#include "Framework/DataProcessorSpec.h" -#include "FT0Calibration/LHCClockDataHisto.h" -#include "FT0Calibration/LHCphaseCalibrationObject.h" -#include "FT0Calibration/FT0CalibrationInfoObject.h" -#include "FITCalibration/FITCalibrationDevice.h" - -using namespace o2::framework; -namespace o2::ft0 -{ -o2::framework::DataProcessorSpec getLHCClockCalibDeviceSpec() -{ - using CalibrationDeviceType = o2::fit::FITCalibrationDevice; - - constexpr const char* DEFAULT_INPUT_LABEL = "calib"; - - std::vector inputs; - inputs.emplace_back(DEFAULT_INPUT_LABEL, "FT0", "CALIB_INFO"); - auto ccdbRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs); - std::vector outputs; - outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "FIT_CALIB"}, o2::framework::Lifetime::Sporadic); - outputs.emplace_back(o2::framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "FIT_CALIB"}, o2::framework::Lifetime::Sporadic); - return o2::framework::DataProcessorSpec{ - "ft0-lhcphase-calibration", - inputs, // o2::framework::Inputs{{"input", "FT0", "CALIBDATA"}}, - outputs, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(ccdbRequest, DEFAULT_INPUT_LABEL)}, - Options{ - {"tf-per-slot", VariantType::UInt32, 26000u, {"number of TFs per calibration time slot"}}, - {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, - {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}}}; -} - -} // namespace o2::ft0 - -#endif diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx deleted file mode 100644 index 1a77e43542816..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/RecoCalibInfoWorkflow.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/ChannelData.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" - -using namespace o2::framework; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - using DataRequest = o2::globaltracking::DataRequest; - - public: - /* void collectBCs(gsl::span& ft0RecPoints, */ - /* gsl::span& primVertices, */ - /* std::map& bcsMap); */ - void run(o2::framework::ProcessingContext& pc) final - { - - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - LOG(info) << " @@@read RecoContainer"; - auto primVertices = recoData.getPrimaryVertices(); - LOG(info) << "@@@ primVertices "; - auto ft0RecPoints = recoData.getFT0RecPoints(); - LOG(info) << "@@@@ read T0 recpoints "; - std::map bcsMap; - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) - iter->second = nullptr; - } - /* collectBCs(ft0RecPoints, primVertices, bcsMap); */ - - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - /* uint64_t bc = globalBC; */ - auto item = bcsMap.find(bc); - if (item == bcsMap.end() || item->second == nullptr) { - LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - /* int bcID = -1; */ - /* if (item != bcsMap.end()) { */ - /* bcID = item->second; */ - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(info) << "@@@ currentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont == 0) - continue; - auto shift = currentVertex / TMath::C(); - LOG(info) << " BC t0 " << bc; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - - /* auto recpoints = */ - /* pc.inputs().get>("recpoints"); */ - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.emplace_back(t0A, t0C, t0AC); - } - } - - private: - std::shared_ptr mDataRequest; -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - - DataProcessorSpec dataProcessorSpec{ - "RecoCalibInfoWorkflow", - Inputs{ - {{"recpoints"}, "FT0", "FT0CLUSTER"}, - }, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx deleted file mode 100644 index 1a77e43542816..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/RecoQAInfoWorkflow.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include -#include "Framework/DeviceSpec.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/Task.h" -#include "DataFormatsFT0/ChannelData.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsFT0/RecPoints.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "CommonDataFormat/TimeStamp.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "ReconstructionDataFormats/PrimaryVertex.h" -#include "DataFormatsFT0/RecoCalibInfoObject.h" - -using namespace o2::framework; - -namespace o2::ft0 -{ - -class RecoCalibInfoWorkflow final : public o2::framework::Task -{ - using DataRequest = o2::globaltracking::DataRequest; - - public: - /* void collectBCs(gsl::span& ft0RecPoints, */ - /* gsl::span& primVertices, */ - /* std::map& bcsMap); */ - void run(o2::framework::ProcessingContext& pc) final - { - - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); - LOG(info) << " @@@read RecoContainer"; - auto primVertices = recoData.getPrimaryVertices(); - LOG(info) << "@@@ primVertices "; - auto ft0RecPoints = recoData.getFT0RecPoints(); - LOG(info) << "@@@@ read T0 recpoints "; - std::map bcsMap; - for (auto& vertex : primVertices) { - auto& timeStamp = vertex.getTimeStamp(); - double tsTimeStamp = timeStamp.getTimeStamp() * 1E3; // mus to ns - uint64_t globalBC = std::round(tsTimeStamp / o2::constants::lhc::LHCBunchSpacingNS); - auto [iter, inserted] = bcsMap.try_emplace(globalBC, &vertex); - if (!inserted) - iter->second = nullptr; - } - /* collectBCs(ft0RecPoints, primVertices, bcsMap); */ - - for (auto& ft0RecPoint : ft0RecPoints) { - uint64_t bc = ft0RecPoint.getInteractionRecord().toLong(); - /* uint64_t bc = globalBC; */ - auto item = bcsMap.find(bc); - if (item == bcsMap.end() || item->second == nullptr) { - LOG(fatal) << "Error: could not find a corresponding BC ID for a FT0 rec. point; BC = " << bc; - continue; - } - auto& vertex = *item->second; - /* int bcID = -1; */ - /* if (item != bcsMap.end()) { */ - /* bcID = item->second; */ - auto currentVertex = vertex.getZ(); - ushort ncont = vertex.getNContributors(); - LOG(info) << "@@@ currentVertex " << currentVertex << " ncont " << int(ncont); - if (ncont == 0) - continue; - auto shift = currentVertex / TMath::C(); - LOG(info) << " BC t0 " << bc; - short t0A = ft0RecPoint.getCollisionTimeA() + shift; - short t0C = ft0RecPoint.getCollisionTimeC() - shift; - short t0AC = ft0RecPoint.getCollisionTimeMean(); - - /* auto recpoints = */ - /* pc.inputs().get>("recpoints"); */ - auto& calib_data = pc.outputs().make>(o2::framework::OutputRef{"calib", 0}); - calib_data.emplace_back(t0A, t0C, t0AC); - } - } - - private: - std::shared_ptr mDataRequest; -}; - -} // namespace o2::ft0 - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const&) -{ - - DataProcessorSpec dataProcessorSpec{ - "RecoCalibInfoWorkflow", - Inputs{ - {{"recpoints"}, "FT0", "FT0CLUSTER"}, - }, - Outputs{ - {{"calib"}, "FT0", "CALIB_INFO"}}, - AlgorithmSpec{adaptFromTask()}, - Options{}}; - - WorkflowSpec workflow; - workflow.emplace_back(dataProcessorSpec); - return workflow; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx deleted file mode 100644 index 1f70538c2880b..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/calib-global-offsets.cxx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file calib-global-offsets.cxx - -#include "FT0Calibration/GlobalOffsetsCalibInfoWorkflow.h" -#include "Framework/CompletionPolicy.h" -#include "CommonUtils/ConfigurableParam.h" -#include "GlobalTrackingWorkflowHelpers/InputHelper.h" -#include "DetectorsCommonDataFormats/DetID.h" -#include - -using namespace o2::framework; - -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters - std::vector options{ - {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; - - std::swap(workflowOptions, options); -} -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - auto useMC = !configcontext.options().get("disable-mc"); - - WorkflowSpec specs; - specs.emplace_back(o2::ft0::getRecoCalibInfoWorkflow(useMC)); - - return std::move(specs); -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx deleted file mode 100644 index 29c7d9719e0c1..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/ft0-collect-calib-workflow.cxx +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0CalibSlewingCollectorSpec.h" -#include "FT0CalibCollectorWriterSpec.h" -#include "Framework/DataProcessorSpec.h" - -using namespace o2::framework; - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters -} - -// ------------------------------------------------------------------ - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - WorkflowSpec specs; - specs.emplace_back(getFT0CalibCollectorDeviceSpec()); - specs.emplace_back(getFT0CalibCollectorWriterSpec()); - - return specs; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx deleted file mode 100644 index 4384bfb1c6e3c..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/lhc-clockphase-workflow.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/DataProcessorSpec.h" -#include "LHCClockCalibratorSpec.h" - -using namespace o2::framework; - -// we need to add workflow options before including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - // option allowing to set parameters -} - -// ------------------------------------------------------------------ - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) -{ - WorkflowSpec specs; - specs.emplace_back(o2::ft0::getLHCClockCalibDeviceSpec()); - return specs; -} diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx b/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx deleted file mode 100644 index b5a60c68f37f1..0000000000000 --- a/Detectors/FIT/FT0/calibration/testWorkflow/slew_upload.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file slew_upload.cxx -/// \author Alla.Maevskaya@cern.ch - -#include -#include -#include -#include -#include "Framework/Logger.h" -#include -#include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/NameConf.h" -#include "CCDB/BasicCCDBManager.h" -#include "CCDB/CcdbApi.h" -#include "FT0Calibration/FT0CalibTimeSlewing.h" -#include -#include -#include -#include -#include - -using o2::ccdb::BasicCCDBManager; -using o2::ccdb::CcdbApi; -using namespace o2::ft0; - -namespace bpo = boost::program_options; - -void slew_upload(const std::string& inFileName, const std::string& mergedFileName, int nFiles); - -int main(int argc, char** argv) -{ - bpo::variables_map vm; - bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + - "Upload FT0 slewing corrections\n"); - bpo::options_description opt_hidden(""); - bpo::options_description opt_all; - bpo::positional_options_description opt_pos; - - try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("input-file", bpo::value()->default_value("collFT0.root"), "verbosity level"); - add_option("merged-file", bpo::value()->default_value("FT0slewGraphs.root"), "input merged file"); - add_option("number-of-files", bpo::value()->default_value(1), "number of files to merge"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - - opt_all.add(opt_general).add(opt_hidden); - bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); - - if (vm.count("help")) { - std::cout << opt_general << std::endl; - exit(0); - } - - bpo::notify(vm); - } catch (bpo::error& e) { - std::cerr << "ERROR: " << e.what() << std::endl - << std::endl; - std::cerr << opt_general << std::endl; - exit(1); - } catch (std::exception& e) { - std::cerr << e.what() << ", application will now exit" << std::endl; - exit(2); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - // o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - slew_upload(vm["input-file"].as(), - vm["merged-file"].as(), - vm["number-of-files"].as()); - - return 0; -} - -void slew_upload(const std::string& inFileName, const std::string& mergedFileName, int nFiles) -{ - TStopwatch swTot; - swTot.Start(); - - o2::ft0::FT0CalibTimeSlewing sl; - sl.setSingleFileName(inFileName); - sl.setMergedFileName(mergedFileName); - sl.setNfiles(nFiles); - sl.mergeFilesWithTree(); - for (int iCh = 0; iCh < o2::ft0 ::Geometry::Nchannels; ++iCh) { - TH2F* hist = sl.getTimeAmpHist(iCh); - if (hist->GetEntries() < 100000) { - continue; - } - sl.fillGraph(iCh, hist); - } - std::array graphs = sl.getGraphs(); - TGraph& gr = graphs.at(29); - gr.Print(); - CcdbApi api; - std::map metadata; // can be empty - api.init(o2::base::NameConf::getCCDBServer()); // or http://localhost:8080 for a local installation - // store abitrary user object in strongly typed manner - api.storeAsTFileAny(&graphs, "FT0/Calib/SlewingCorr", metadata); - - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx new file mode 100644 index 0000000000000..5cef707da2cca --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -0,0 +1,48 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FT0EventsPerBcSpec.h" +#include "Framework/Lifetime.h" +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + using namespace o2::framework; + using o2::calibration::FT0EventsPerBcProcessor; + std::vector inputs; + inputs.emplace_back("digits", "FT0", "DIGITSBC", Lifetime::Timeframe); + auto ccdbRequest = std::make_shared(true, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs); + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc"}, Lifetime::Timeframe); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc"}, Lifetime::Timeframe); + DataProcessorSpec dataProcessorSpec{ + "FT0EventsPerBcProcessor", + inputs, + outputs, + AlgorithmSpec(adaptFromTask(ccdbRequest)), + Options{ + {"slot-len-sec", VariantType::UInt32, 3600u, {"Duration of each slot in seconds"}}, + {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, + {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, + {"min-ampl-side-a", VariantType::Int, 0, {"Amplitude threshold for Side A events"}}, + {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}, + {"min-sum-of-ampl", VariantType::Int, 0, {"Amplitude threshold for sum of A-side and C-side amplitudes"}}}}; + + WorkflowSpec workflow; + workflow.emplace_back(dataProcessorSpec); + return workflow; +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h new file mode 100644 index 0000000000000..d493e2a606613 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -0,0 +1,128 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H +#define O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H + +#include "Framework/runDataProcessing.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include +#include "Framework/DeviceSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include "DataFormatsFT0/Digit.h" +#include "FT0Calibration/EventsPerBcCalibrator.h" + +namespace o2::calibration +{ +class FT0EventsPerBcProcessor final : public o2::framework::Task +{ + public: + FT0EventsPerBcProcessor(std::shared_ptr request) : mCCDBRequest(request) {} + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + if (ic.options().hasOption("slot-len-sec")) { + mSlotLenSec = ic.options().get("slot-len-sec"); + } + if (ic.options().hasOption("one-object-per-run")) { + mOneObjectPerRun = ic.options().get("one-object-per-run"); + } + if (ic.options().hasOption("min-entries-number")) { + mMinNumberOfEntries = ic.options().get("min-entries-number"); + } + if (ic.options().hasOption("min-ampl-side-a")) { + mMinAmplitudeSideA = ic.options().get("min-ampl-side-a"); + } + if (ic.options().hasOption("min-ampl-side-c")) { + mMinAmplitudeSideC = ic.options().get("min-ampl-side-c"); + } + if (ic.options().hasOption("min-sum-of-ampl")) { + mMinSumOfAmplitude = ic.options().get("min-sum-of-ampl"); + } + + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude); + + if (mOneObjectPerRun) { + LOG(info) << "Only one object will be created at the end of run"; + mCalibrator->setUpdateAtTheEndOfRunOnly(); + } + if (mOneObjectPerRun == false) { + LOG(info) << "Defined slot interval to " << mSlotLenSec << " seconds"; + mCalibrator->setSlotLengthInSeconds(mSlotLenSec); + } + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + } + + void run(o2::framework::ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto digits = pc.inputs().get>("digits"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); + if (digits.size() == 0) { + return; + } + mCalibrator->process(digits); + if (mOneObjectPerRun == false) { + sendOutput(pc.outputs()); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(info) << "Received end-of-stream, checking for slot to finalize..."; + mCalibrator->checkSlotsToFinalize(); + sendOutput(ec.outputs()); + mCalibrator->initOutput(); + } + + void sendOutput(o2::framework::DataAllocator& output) + { + using o2::framework::Output; + const auto& tvxHists = mCalibrator->getTvxPerBc(); + auto& infos = mCalibrator->getTvxPerBcCcdbInfo(); + for (unsigned int idx = 0; idx < tvxHists.size(); idx++) { + auto& info = infos[idx]; + const auto& payload = tvxHists[idx]; + + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, info.get()); + LOG(info) << "Sending object " << info->getPath() << "/" << info->getFileName() << " of size " << image->size() + << " bytes, valid for " << info->getStartValidityTimestamp() << " : " << info->getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc", idx}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc", idx}, *info.get()); + } + + if (tvxHists.size()) { + mCalibrator->initOutput(); + } + } + + private: + std::shared_ptr mCCDBRequest; + std::unique_ptr mCalibrator; + bool mOneObjectPerRun; + uint32_t mSlotLenSec; + uint32_t mMinNumberOfEntries; + int32_t mMinAmplitudeSideA; + int32_t mMinAmplitudeSideC; + int32_t mMinSumOfAmplitude; +}; +} // namespace o2::calibration +#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0TimeOffsetCalibration-Workflow.cxx similarity index 100% rename from Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeOffsetCalibration-Workflow.cxx rename to Detectors/FIT/FT0/calibration/workflow/FT0TimeOffsetCalibration-Workflow.cxx diff --git a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx similarity index 95% rename from Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx rename to Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx index 541f5a9827a4a..214d919196763 100644 --- a/Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx @@ -40,8 +40,8 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task int mTimeWindow{153}; uint8_t mPMbitsToCheck{0b11111110}; uint8_t mPMbitsGood{0b01001000}; - uint8_t mTrgBitsToCheck{0b11110000}; - uint8_t mTrgBitsGood{0b10010000}; + uint64_t mTrgBitsToCheck{0b11110000}; + uint64_t mTrgBitsGood{0b10010000}; void init(o2::framework::InitContext& ic) final { mNbinsY = ic.options().get("number-bins"); @@ -69,7 +69,8 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task auto channels = pc.inputs().get>("channels"); o2::dataformats::FlatHisto2D timeSpectraInfoObject(sNCHANNELS + nExtraSpectraSlots, 0, sNCHANNELS + nExtraSpectraSlots, mNbinsY, mMinY, mMaxY); for (const auto& digit : digits) { - if ((digit.mTriggers.getTriggersignals() & mTrgBitsToCheck) != mTrgBitsGood) { + const uint64_t trgWordExt = digit.mTriggers.getExtendedTrgWordFT0(); + if ((trgWordExt & mTrgBitsToCheck) != mTrgBitsGood) { continue; } const auto& chan = digit.getBunchChannelData(channels); @@ -101,7 +102,7 @@ class FT0TimeSpectraProcessor final : public o2::framework::Task } } - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TIME_SPECTRA", 0, o2::framework::Lifetime::Timeframe}, timeSpectraInfoObject.getBase()); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TIME_SPECTRA", 0}, timeSpectraInfoObject.getBase()); } }; diff --git a/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C b/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C index 73a768ce03502..a5a1441d81726 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C +++ b/Detectors/FIT/FT0/dcsmonitoring/macros/makeFT0CCDBEntryForDCS.C @@ -54,7 +54,8 @@ int makeFT0CCDBEntryForDCS(const std::string ccdbUrl = "http://localhost:8080", "FT0/Trigger4_OrC/CNT_RATE", "FT0/Trigger5_OrA/CNT_RATE", "FT0/Background/[0..9]/CNT_RATE", - "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE"}; + "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", + "FT0/SecondaryCounter/CEplusSC/CNT_RATE"}; std::vector expAliasesHV = o2::dcs::expandAliases(aliasesHV); std::vector expAliasesADC = o2::dcs::expandAlias(aliasesADC); diff --git a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx index c525ac8357708..c5049c3401365 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx +++ b/Detectors/FIT/FT0/dcsmonitoring/src/FT0DCSDataProcessor.cxx @@ -47,7 +47,8 @@ std::vector o2::ft0::FT0DCSDataProcessor::getHardC "FT0/Trigger4_OrC/CNT_RATE", "FT0/Trigger5_OrA/CNT_RATE", "FT0/Background/[0..9]/CNT_RATE", - "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE"}; + "FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", + "FT0/SecondaryCounter/CEplusSC/CNT_RATE"}; std::vector expAliasesHV = o2::dcs::expandAliases(aliasesHV); std::vector expAliasesADC = o2::dcs::expandAlias(aliasesADC); std::vector expAliasesRates = o2::dcs::expandAliases(aliasesRates); diff --git a/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h b/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h index 62133231b8ff3..fe6701344530b 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h +++ b/Detectors/FIT/FT0/dcsmonitoring/workflow/FT0DCSConfigProcessorSpec.h @@ -59,8 +59,8 @@ DataProcessorSpec getFT0DCSConfigProcessorSpec() return DataProcessorSpec{ "ft0-dcs-config-processor", - Inputs{{"inputConfig", o2::header::gDataOriginFT0, "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", o2::header::gDataOriginFT0, "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", o2::header::gDataOriginFT0, "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", o2::header::gDataOriginFT0, "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask("FT0", ddDChM)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, diff --git a/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx b/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx index b728a1c4c85b2..58802f0e51d16 100644 --- a/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx +++ b/Detectors/FIT/FT0/dcsmonitoring/workflow/ft0-dcs-sim-workflow.cxx @@ -43,6 +43,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Trigger5_OrA/CNT_RATE", 0, 5000000}); dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Background/[0..9]/CNT_RATE", 0, 50000}); dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/Background/[A,B,C,D,E,F,G,H]/CNT_RATE", 0, 50000}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"FT0/SecondaryCounter/CEplusSC/CNT_RATE", 0, 5000000}); o2::framework::WorkflowSpec specs; specs.emplace_back(o2::dcs::test::getDCSRandomDataGeneratorSpec(dphints, "FT0")); diff --git a/Detectors/FIT/FT0/macros/CMakeLists.txt b/Detectors/FIT/FT0/macros/CMakeLists.txt index c4ed27d2513ba..17491ca4962c1 100644 --- a/Detectors/FIT/FT0/macros/CMakeLists.txt +++ b/Detectors/FIT/FT0/macros/CMakeLists.txt @@ -1,14 +1,21 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". # # In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization or -# submit itself to any jurisdiction. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_test_root_macro(FT0Misaligner.C PUBLIC_LINK_LIBRARIES O2::CCDB O2::FT0Simulation LABELS ft0) + +o2_add_test_root_macro(FT0readEventsPerBc.C + PUBLIC_LINK_LIBRARIES + O2::CCDB + O2::DataFormatsFT0 + LABELS ft0) diff --git a/Detectors/FIT/FT0/macros/FT0Misaligner.C b/Detectors/FIT/FT0/macros/FT0Misaligner.C index 7585411066934..16476ae3b8ccc 100644 --- a/Detectors/FIT/FT0/macros/FT0Misaligner.C +++ b/Detectors/FIT/FT0/macros/FT0Misaligner.C @@ -1,15 +1,30 @@ +// Copyright 2021-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FT0Misaligner.C +/// \brief ROOT macro for creating an FT0 geometry alignment object. Based on ITSMisaligner.C +/// +/// \author Andreas Molander andreas.molander@cern.ch, Alla Maevskaya + #if !defined(__CLING__) || defined(__ROOTCLING__) -//#define ENABLE_UPGRADES + +#include "CCDB/CcdbApi.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/AlignParam.h" -#include "DetectorsBase/GeometryManager.h" -#include "CCDB/CcdbApi.h" -#include "FT0Base/Geometry.h" -#include + #include #include #include + #endif using AlgPar = std::array; @@ -23,19 +38,15 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" const std::string& fileName = "FT0Alignment.root") { std::vector params; - o2::base::GeometryManager::loadGeometry("", false); - // auto geom = o2::ft0::Geometry::Instance(); AlgPar pars; bool glo = true; o2::detectors::DetID detFT0("FT0"); - // FT0 detector - //set A side std::string symNameA = "FT0A"; pars = generateMisalignment(xA, yA, zA, psiA, thetaA, phiA); params.emplace_back(symNameA.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); - //set C side + std::string symNameC = "FT0C"; pars = generateMisalignment(xC, yC, zC, psiC, thetaC, phiC); params.emplace_back(symNameC.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); @@ -44,7 +55,7 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detFT0) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); @@ -57,14 +68,15 @@ void FT0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" algFile.Close(); } } + AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi) { AlgPar pars; - pars[0] = gRandom->Gaus(0, x); - pars[1] = gRandom->Gaus(0, y); - pars[2] = gRandom->Gaus(0, z); - pars[3] = gRandom->Gaus(0, psi); - pars[4] = gRandom->Gaus(0, theta); - pars[5] = gRandom->Gaus(0, phi); + pars[0] = x; + pars[1] = y; + pars[2] = z; + pars[3] = psi; + pars[4] = theta; + pars[5] = phi; return std::move(pars); } diff --git a/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C new file mode 100644 index 0000000000000..c6afc86389b9b --- /dev/null +++ b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C @@ -0,0 +1,52 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#endif + +#include "CCDB/CcdbApi.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "TH1F.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "Framework/Logger.h" +#include "CommonConstants/LHCConstants.h" + +std::unique_ptr hist; +std::unique_ptr canvas; + +void FT0readEventsPerBc(std::string ccdbUrl, long timestamp) +{ + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbUrl); + const std::string ccdbPath = "FT0/Calib/EventsPerBc"; + std::map metadata; + + if (timestamp < 0) { + timestamp = o2::ccdb::getCurrentTimestamp(); + } + + std::unique_ptr events(ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, timestamp)); + + if (!events) { + LOGP(fatal, "EventsPerBc object not found in {}/{} for timestamp {}.", ccdbUrl, ccdbPath, timestamp); + return; + } + + hist = std::make_unique("eventsPerBcHist", "Events per BC", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches - 1); + for (int idx = 0; idx < o2::constants::lhc::LHCMaxBunches; idx++) { + hist->Fill(idx, events->histogram[idx]); + } + canvas = std::make_unique(); + hist->Draw(); + canvas->Draw(); +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h index 716d6795e5ad2..84d2d1a0bcd3a 100644 --- a/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h +++ b/Detectors/FIT/FT0/raw/include/FT0Raw/DigitBlockFT0.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -//file DigitBlockFT0.h class for proccessing RAW data into Digits +// file DigitBlockFT0.h class for proccessing RAW data into Digits // // Artur.Furs // afurs@cern.ch @@ -25,10 +25,10 @@ namespace o2 { namespace ft0 { -//Normal data taking mode -using DigitBlockFT0 = DigitBlockFIT; -//TCM extended data taking mode -using DigitBlockFT0ext = DigitBlockFIText; +// Normal data taking mode +using DigitBlockFT0 = o2::fit::DigitBlockFIT; +// TCM extended data taking mode +using DigitBlockFT0ext = o2::fit::DigitBlockFIText; } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx index f08f0bc1042db..69be24e0e0f9b 100644 --- a/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/DataBlockFT0.cxx @@ -10,3 +10,7 @@ // or submit itself to any jurisdiction. #include "FT0Raw/DataBlockFT0.h" + +template class o2::fit::DataBlockPM, o2::ft0::RawHeaderPM, o2::ft0::RawDataPM>; +template class o2::fit::DataBlockTCM, o2::ft0::RawHeaderTCM, o2::ft0::RawDataTCM>; +template class o2::fit::DataBlockTCMext, o2::ft0::RawHeaderTCMext, o2::ft0::RawDataTCM, o2::ft0::RawDataTCMext>; diff --git a/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx index b474afff032bd..80ecb470f0a62 100644 --- a/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/DigitBlockFT0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/DigitBlockFT0.h" + +template class o2::fit::DigitBlockFIT; +template class o2::fit::DigitBlockFIText; \ No newline at end of file diff --git a/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx index dea616c706ccc..1e5984c5aea3e 100644 --- a/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx +++ b/Detectors/FIT/FT0/raw/src/RawReaderFT0Base.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/RawReaderFT0Base.h" + +template class o2::fit::RawReaderBaseFIT; +template class o2::fit::RawReaderBaseFIT; diff --git a/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx b/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx index cd57b0ea4fe0e..f563e9de8e1fc 100644 --- a/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx +++ b/Detectors/FIT/FT0/raw/src/RawWriterFT0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FT0Raw/RawWriterFT0.h" + +template class o2::fit::RawWriterFIT; +template class o2::fit::RawWriterFIT; diff --git a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt index e76155eebd158..9a4a14af6aca6 100644 --- a/Detectors/FIT/FT0/reconstruction/CMakeLists.txt +++ b/Detectors/FIT/FT0/reconstruction/CMakeLists.txt @@ -25,8 +25,7 @@ o2_add_library(FT0Reconstruction O2::DetectorsCalibration) o2_target_root_dictionary(FT0Reconstruction - HEADERS include/FT0Reconstruction/CollisionTimeRecoTask.h - include/FT0Reconstruction/InteractionTag.h) + HEADERS include/FT0Reconstruction/InteractionTag.h) o2_add_executable( test-raw-conversion diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 0566bf9883baa..5dc367204e1a3 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -26,7 +26,6 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "FT0Base/FT0DigParam.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" class TTree; @@ -35,10 +34,10 @@ namespace o2 namespace ft0 { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF @@ -73,15 +72,15 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig using MD = o2::ctf::Metadata::OptStore; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { - MD::EENCODE, // BLC_trigger - MD::EENCODE, // BLC_bcInc - MD::EENCODE, // BLC_orbitInc - MD::EENCODE, // BLC_nChan - MD::EENCODE, // BLC_status - MD::EENCODE, // BLC_idChan - MD::EENCODE, // BLC_qtcChain - MD::EENCODE, // BLC_cfdTime - MD::EENCODE // BLC_qtcAmpl + MD::EENCODE_OR_PACK, // BLC_trigger + MD::EENCODE_OR_PACK, // BLC_bcInc + MD::EENCODE_OR_PACK, // BLC_orbitInc + MD::EENCODE_OR_PACK, // BLC_nChan + MD::EENCODE_OR_PACK, // BLC_status + MD::EENCODE_OR_PACK, // BLC_idChan + MD::EENCODE_OR_PACK, // BLC_qtcChain + MD::EENCODE_OR_PACK, // BLC_cfdTime + MD::EENCODE_OR_PACK // BLC_qtcAmpl }; CompressedDigits cd; if (mExtHeader.isValidDictTimeStamp()) { @@ -102,11 +101,10 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig ec->setHeader(cd.header); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEFT0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEFT0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEFT0(cd.trigger, CTF::BLC_trigger, 0); iosize += ENCODEFT0(cd.bcInc, CTF::BLC_bcInc, 0); @@ -134,7 +132,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& checkDictVersion(hd); ec.print(getPrefix(), mVerbosity); o2::ctf::CTFIOSize iosize; -#define DECODEFT0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEFT0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEFT0(cd.trigger, CTF::BLC_trigger); iosize += DECODEFT0(cd.bcInc, CTF::BLC_bcInc); diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h index b866043afaa58..9f6cd500b9e74 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h @@ -20,6 +20,8 @@ #include "DataFormatsFT0/RecPoints.h" #include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" #include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/SlewingCoef.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include #include #include @@ -51,18 +53,22 @@ class CollisionTimeRecoTask std::vector& outChData); void FinishTask(); void SetTimeCalibObject(o2::ft0::TimeSpectraInfoObject const* timeCalibObject) { mTimeCalibObject = timeCalibObject; }; - void SetSlew(std::array* calibslew) + void SetSlewingCalibObject(o2::ft0::SlewingCoef const* calibSlew) { - LOG(info) << "@@@SetSlew " << calibslew->size(); - mCalibSlew = calibslew; + LOG(info) << "Init for slewing calib object"; + mCalibSlew = calibSlew->makeSlewingPlots(); }; + void SetDeadChannelMap(const o2::fit::DeadChannelMap* deadChannelMap) + { + LOG(info) << "Updated dead channel map for CollisionTimeRecoTask"; + mDeadChannelMap = deadChannelMap; + } float getTimeInPS(const o2::ft0::ChannelData& channelData); private: o2::ft0::TimeSpectraInfoObject const* mTimeCalibObject = nullptr; - std::array* mCalibSlew = nullptr; - - ClassDefNV(CollisionTimeRecoTask, 3); + const o2::fit::DeadChannelMap* mDeadChannelMap = nullptr; + typename o2::ft0::SlewingCoef::SlewingPlots_t mCalibSlew{}; }; } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h index 408eed72e3af1..c0007ac3a4188 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h @@ -26,11 +26,12 @@ namespace ft0 // These are configurable params for FT0 selection as interaction tag struct InteractionTag : public o2::conf::ConfigurableParamHelper { - int minAmplitudeAC = 0; ///< use only FT0 triggers with high enough amplitude - + int minAmplitudeAC = 2; ///< use only FT0 triggers with high enough amplitude + int minAmplitudeA = 1; + int minAmplitudeC = 1; bool isSelected(const RecPoints& rp) const { - return rp.getTrigger().getVertex() && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) > minAmplitudeAC; + return rp.getTrigger().getVertex() && rp.getTrigger().getAmplA() >= minAmplitudeA && rp.getTrigger().getAmplC() >= minAmplitudeC && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) >= minAmplitudeAC; } O2ParamDef(InteractionTag, "ft0tag"); diff --git a/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx index 6e840c52772cb..349c2edf796a6 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CTFCoder.cxx @@ -53,7 +53,7 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); CompressedDigits cd; // just to get member types -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cd.trigger, CTF::BLC_trigger); MAKECODER(cd.bcInc, CTF::BLC_bcInc); @@ -71,22 +71,18 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa size_t CTFCoder::estimateCompressedSize(const CompressedDigits& cd) { size_t sz = 0; - // clang-format off // RS FIXME this is very crude estimate, instead, an empirical values should be used -#define VTP(vec) typename std::remove_reference::type::value_type -#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ - rans::calculateMaxBufferSize(vec.size(), reinterpret_cast*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) - sz += ESTSIZE(cd.trigger, CTF::BLC_trigger); - sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); - sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); - sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); - sz += ESTSIZE(cd.eventStatus, CTF::BLC_status); - sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); - sz += ESTSIZE(cd.qtcChain, CTF::BLC_qtcChain); - sz += ESTSIZE(cd.cfdTime, CTF::BLC_cfdTime); - sz += ESTSIZE(cd.qtcAmpl, CTF::BLC_qtcAmpl); + sz += estimateBufferSize(static_cast(CTF::BLC_trigger), cd.trigger); + sz += estimateBufferSize(static_cast(CTF::BLC_bcInc), cd.bcInc); + sz += estimateBufferSize(static_cast(CTF::BLC_orbitInc), cd.orbitInc); + sz += estimateBufferSize(static_cast(CTF::BLC_nChan), cd.nChan); + sz += estimateBufferSize(static_cast(CTF::BLC_status), cd.eventStatus); + sz += estimateBufferSize(static_cast(CTF::BLC_idChan), cd.idChan); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcChain), cd.qtcChain); + sz += estimateBufferSize(static_cast(CTF::BLC_cfdTime), cd.cfdTime); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcAmpl), cd.qtcAmpl); // clang-format on - LOG(info) << "Estimated output size is " << sz << " bytes"; + LOG(debug) << "Estimated output size is " << sz << " bytes"; return sz; -} +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index c981fee23ce93..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -36,8 +36,6 @@ void CollisionTimeRecoTask::processTF(const gsl::span& dig std::vector& vecRecPoints, std::vector& vecChData) { - // vecRecPoints.reserve(digits.size()); - // vecChData.reserve(channels.size()); for (const auto& digit : digits) { if (!ChannelFilterParam::Instance().checkTCMbits(digit.getTriggers().getTriggersignals())) { continue; @@ -58,30 +56,49 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, float sideCtime = 0; constexpr int nMCPsA = 4 * Geometry::NCellsA; - const auto parInv = FT0DigParam::Instance().mMV_2_NchannelsInverse; int nch{0}; + bool isActiveA = false; + bool isActiveC = false; + bool isFlangeEvent = false; + for (const auto& channelData : inChData) { + if (channelData.ChId >= NCHANNELS) { + // Reference channels shouldn't participate in reco at all! + continue; + } + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(channelData.ChId)) { + LOG(debug) << "Channel " << channelData.ChId << " is dead - discarding data"; + continue; + } const float timeInPS = getTimeInPS(channelData); if (ChannelFilterParam::Instance().checkAll(channelData)) { outChData.emplace_back(channelData.ChId, timeInPS, (float)channelData.QTCAmpl, channelData.ChainQTC); nch++; } - // only signals with amplitude participate in collision time - if (TimeFilterParam::Instance().checkAll(channelData)) { - if (channelData.ChId < nMCPsA) { + const bool isOkForTimeCalc = TimeFilterParam::Instance().checkAll(channelData); + // only signals which satisfy conditions may participate in time calculation + if (channelData.ChId < nMCPsA) { + // A-side + if (isOkForTimeCalc) { sideAtime += timeInPS; ndigitsA++; - } else if (channelData.ChId < NCHANNELS) { + } + isActiveA = true; + } else { + // C-side + if (isOkForTimeCalc) { sideCtime += timeInPS; ndigitsC++; } + isActiveC = true; + isFlangeEvent |= channelData.CFDTime < -350 && channelData.CFDTime > -450; } } std::array mCollisionTime = {RP::sDummyCollissionTime, RP::sDummyCollissionTime, RP::sDummyCollissionTime, RP::sDummyCollissionTime}; - // !!!! tobe done::should be fix with ITS vertex - mCollisionTime[TimeA] = (ndigitsA > 0) ? sideAtime / ndigitsA : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; - mCollisionTime[TimeC] = (ndigitsC > 0) ? sideCtime / ndigitsC : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; + + mCollisionTime[TimeA] = (ndigitsA > 0) ? std::round(sideAtime / ndigitsA) : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; + mCollisionTime[TimeC] = (ndigitsC > 0) ? std::round(sideCtime / ndigitsC) : RP::sDummyCollissionTime; // 2 * o2::InteractionRecord::DummyTime; if (ndigitsA > 0 && ndigitsC > 0) { mCollisionTime[Vertex] = (mCollisionTime[TimeA] - mCollisionTime[TimeC]) / 2.; @@ -89,7 +106,8 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, } else { mCollisionTime[TimeMean] = std::min(mCollisionTime[TimeA], mCollisionTime[TimeC]); } - return RecPoints{mCollisionTime, firstEntry, nch, digit.mIntRecord, digit.mTriggers}; + const uint8_t extraTrgWord = RecPoints::makeExtraTrgWord(isActiveA, isActiveC, isFlangeEvent); + return RecPoints(firstEntry, nch, digit.mIntRecord, mCollisionTime, digit.mTriggers, extraTrgWord); } //______________________________________________________ void CollisionTimeRecoTask::FinishTask() @@ -100,9 +118,9 @@ void CollisionTimeRecoTask::FinishTask() float CollisionTimeRecoTask::getTimeInPS(const o2::ft0::ChannelData& channelData) { + // Getting time offset float offsetChannel{0}; - float slewoffset{0}; - if (mTimeCalibObject && channelData.ChId < NCHANNELS) { + if (mTimeCalibObject) { // Temporary, will be changed to status bit checking // Check statistics const auto& stat = mTimeCalibObject->mTime[channelData.ChId].mStat; @@ -122,12 +140,12 @@ float CollisionTimeRecoTask::getTimeInPS(const o2::ft0::ChannelData& channelData offsetChannel = meanHist; } } - /* - if (mCalibSlew && channelData.ChId < NCHANNELS) { - TGraph& gr = mCalibSlew->at(channelData.ChId); - slewoffset = gr.Eval(channelData.QTCAmpl); - } - */ - const float globalOffset = (offsetChannel + slewoffset) * Geometry::ChannelWidth; - return float(channelData.CFDTime) * Geometry::ChannelWidth - globalOffset; + // Getting slewing offset + float slewoffset{0}; + const auto& gr = mCalibSlew[static_cast(channelData.getFlag(o2::ft0::ChannelData::EEventDataBit::kNumberADC))][channelData.ChId]; + slewoffset = gr.Eval(channelData.QTCAmpl); + + // Final calculation + const float globalOffset = offsetChannel + slewoffset; + return (static_cast(channelData.CFDTime) - globalOffset) * Geometry::ChannelWidth; } diff --git a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h index 81153dfff9132..e35f871e91c7c 100644 --- a/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h +++ b/Detectors/FIT/FT0/reconstruction/src/FT0ReconstructionLinkDef.h @@ -15,7 +15,6 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::ft0::CollisionTimeRecoTask + ; #pragma link C++ class o2::ft0::InteractionTag + ; #endif diff --git a/Detectors/FIT/FT0/simulation/CMakeLists.txt b/Detectors/FIT/FT0/simulation/CMakeLists.txt index f94d085d5b48e..1559649755c4b 100644 --- a/Detectors/FIT/FT0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FT0/simulation/CMakeLists.txt @@ -31,12 +31,8 @@ o2_target_root_dictionary(FT0Simulation o2_add_executable(digi2raw COMPONENT_NAME ft0 SOURCES src/digit2raw.cxx - PUBLIC_LINK_LIBRARIES O2::FT0Simulation - O2::DetectorsRaw - O2::DetectorsCommonDataFormats - O2::CommonUtils - Boost::program_options - O2::FT0Raw) + PUBLIC_LINK_LIBRARIES O2::FT0Raw + Boost::program_options) o2_data_file(COPY data DESTINATION Detectors/FT0/simulation) diff --git a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h index bd5693707673f..3a035a247c6ef 100644 --- a/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h +++ b/Detectors/FIT/FT0/simulation/include/FT0Simulation/Digitizer.h @@ -13,6 +13,7 @@ #define ALICEO2_FT0_DIGITIZER_H #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "DataFormatsFT0/Digit.h" #include "DataFormatsFT0/ChannelData.h" #include "DataFormatsFT0/MCLabel.h" @@ -73,6 +74,7 @@ class Digitizer void SetChannelOffset(o2::ft0::FT0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; }; double getTimeOffsetWrtBC() const { return mIntRecord.getTimeOffsetWrtBC(); } struct CFDOutput { @@ -165,6 +167,7 @@ class Digitizer o2::dataformats::MCTruthContainer& labels); o2::ft0::FT0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Digitizer, 3); }; diff --git a/Detectors/FIT/FT0/simulation/src/Detector.cxx b/Detectors/FIT/FT0/simulation/src/Detector.cxx index 015b709355f98..ea856eb204802 100644 --- a/Detectors/FIT/FT0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FT0/simulation/src/Detector.cxx @@ -882,18 +882,10 @@ TGeoVolume* Detector::constructFrameCGeometry() Double_t yPMT[NCellsC]; Double_t zPMT[NCellsC]; - Double_t aPMT[NCellsC]; - Double_t bPMT[NCellsC]; - Double_t gPMT[NCellsC]; - Double_t xQrad[NCellsC]; Double_t yQrad[NCellsC]; Double_t zQrad[NCellsC]; - Double_t aQrad[NCellsC]; - Double_t bQrad[NCellsC]; - Double_t gQrad[NCellsC]; - Double_t rotC[NCellsC]; Double_t comC[NCellsC]; @@ -903,31 +895,10 @@ TGeoVolume* Detector::constructFrameCGeometry() yPMT[i] = scalePMT * yc2[i]; zPMT[i] = scalePMT * zc2[i]; - aPMT[i] = TMath::ATan(yPMT[i] / xPMT[i]) - TMath::Pi() / 2 + 2 * TMath::Pi(); - if (xPMT[i] < 0) { - bPMT[i] = TMath::ACos(zPMT[i] / crad); - } else { - bPMT[i] = -1 * TMath::ACos(zPMT[i] / crad); - } - - aPMT[i] *= 180 / TMath::Pi(); - bPMT[i] *= 180 / TMath::Pi(); - gPMT[i] = -1 * aPMT[i]; - // Quartz radiator transformations xQrad[i] = scaleQrad * xc2[i]; yQrad[i] = scaleQrad * yc2[i]; zQrad[i] = scaleQrad * zc2[i]; - - aQrad[i] = TMath::ATan(yQrad[i] / xQrad[i]) - TMath::Pi() / 2 + 2 * TMath::Pi(); - if (xQrad[i] < 0) { - bQrad[i] = TMath::ACos(zQrad[i] / crad); - } else { - bQrad[i] = -1 * TMath::ACos(zQrad[i] / crad); - } - aQrad[i] *= 180 / TMath::Pi(); - bQrad[i] *= 180 / TMath::Pi(); - gQrad[i] = -1 * aQrad[i]; } TString nameRot; diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx index 583f62019f8a0..aca012f1bc5a9 100644 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -13,7 +13,8 @@ #include "FT0Simulation/DigitizationConstants.h" #include "FT0Base/FT0DigParam.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include +#include "CommonConstants/PhysicsConstants.h" +#include "CommonDataFormat/InteractionRecord.h" #include "TMath.h" #include "TRandom.h" @@ -204,14 +205,27 @@ void Digitizer::process(const std::vector* hits, if (hit.GetEnergyLoss() > 0) { continue; } - const auto& params = FT0DigParam::Instance(); + Int_t hit_ch = hit.GetDetectorID(); + + // If the dead channel map is used, and the channel with ID 'hit_ch' is dead, don't process this hit. + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(hit_ch)) { + continue; + } + + const auto& params = FT0DigParam::Instance(); + Bool_t is_A_side = (hit_ch < 4 * mGeometry.NCellsA); - Float_t time_compensate = is_A_side ? params.mA_side_cable_cmps : params.mC_side_cable_cmps; - Double_t hit_time = hit.GetTime() - time_compensate; + + // Subtract time-of-flight from hit time + const Float_t timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS; + const Float_t timeOffset = is_A_side ? params.hitTimeOffsetA : params.hitTimeOffsetC; + Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC(); + if (hit_time > 150) { continue; // not collect very slow particles } + auto relBC = o2::InteractionRecord{hit_time}; if (mCache.size() <= relBC.bc) { mCache.resize(relBC.bc + 1); @@ -248,6 +262,9 @@ 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; }); + + // The hits between 'channel_begin' and 'channel_end' now contains all hits for channel 'ipmt' + if (channel_end - channel_begin < params.mAmp_trsh) { continue; } @@ -268,7 +285,7 @@ void Digitizer::storeBC(BCCache& bc, if (mCalibOffset) { miscalib = mCalibOffset->mTimeOffsets[ipmt]; } - int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib + int(1000. * mIntRecord.getTimeOffsetWrtBC() * params.mChannelWidthInverse); + int smeared_time = 1000. * (*cfd.particle - params.mCfdShift) * params.mChannelWidthInverse + miscalib; // + int(1000. * mIntRecord.getTimeOffsetWrtBC() * params.mChannelWidthInverse); bool is_time_in_signal_gate = (smeared_time > -params.mTime_trg_gate && smeared_time < params.mTime_trg_gate); float charge = measure_amplitude(channel_times) * params.mCharge2amp; float amp = is_time_in_signal_gate ? params.mMV_2_Nchannels * charge : 0; diff --git a/Detectors/FIT/FT0/simulation/src/digit2raw.cxx b/Detectors/FIT/FT0/simulation/src/digit2raw.cxx index 9e02dfa914f46..b700f06e9985f 100644 --- a/Detectors/FIT/FT0/simulation/src/digit2raw.cxx +++ b/Detectors/FIT/FT0/simulation/src/digit2raw.cxx @@ -13,52 +13,12 @@ /// \author ruben.shahoyan@cern.ch afurs@cern.ch #include -#include #include -#include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "DetectorsRaw/HBFUtils.h" #include "FT0Raw/RawWriterFT0.h" -#include "DataFormatsParameters/GRPObject.h" /// MC->raw conversion for FT0 namespace bpo = boost::program_options; -struct Configuration { - Configuration(const bpo::variables_map& vm) - { - mInputFile = vm["input-file"].as(); - mOutputDir = vm["output-dir"].as(); - mVerbosity = vm["verbosity"].as(); - mFileFor = vm["file-for"].as(); - mRdhVersion = vm["rdh-version"].as(); - mEnablePadding = vm["enable-padding"].as(); - mNoEmptyHBF = vm["no-empty-hbf"].as(); - mFlpName = vm["flp-name"].as(); - mCcdbPath = vm["ccdb-path"].as(); - mChannelMapPath = vm["lut-path"].as(); - if (mRdhVersion < 7 && !mEnablePadding) { - mEnablePadding = true; - LOG(info) << "padding is always ON for RDH version " << mRdhVersion; - } - mDataFormat = mEnablePadding ? 0 : 2; - } - bool mNoEmptyHBF; - bool mEnablePadding; - int mVerbosity; - uint32_t mRdhVersion; - uint32_t mDataFormat; - std::string mInputFile; - std::string mOutputDir; - std::string mFileFor; - std::string mFlpName; - std::string mCcdbPath; - std::string mChannelMapPath; -}; - -template -void digit2raw(const Configuration& cfg); int main(int argc, char** argv) { @@ -70,21 +30,10 @@ int main(int argc, char** argv) bpo::positional_options_description opt_pos; try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); - add_option("input-file,i", bpo::value()->default_value("ft0digits.root"), "input FT0 digits file"); - add_option("flp-name", bpo::value()->default_value("alio2-cr1-flp200"), "single file per: all,flp,cru,link"); - add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); - add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); - uint32_t defRDH = o2::raw::RDHUtils::getVersion(); - add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); - add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); - add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); - add_option("lut-path", bpo::value()->default_value(""), "LookupTable path, e.g. FT0/LookupTable"); - add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + opt_general.add_options()("help,h", "Print this help message"); + // config with FT0 defaults + o2::fit::DigitToRawConfig::configureExecOptions(opt_general, "ft0digits.root", "alio2-cr1-flp200"); + // common part opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -103,63 +52,12 @@ int main(int argc, char** argv) std::cerr << e.what() << ", application will now exit" << std::endl; exit(2); } - - std::string confDig = vm["hbfutils-config"].as(); - if (!confDig.empty() && confDig != "none") { - o2::conf::ConfigurableParam::updateFromFile(confDig, "HBFUtils"); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - const Configuration cfg(vm); + const o2::fit::DigitToRawConfig cfg(vm); if (!cfg.mEnablePadding) { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } else { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } o2::raw::HBFUtils::Instance().print(); - return 0; } - -template -void digit2raw(const Configuration& cfg) -{ - TStopwatch swTot; - swTot.Start(); - RawWriterType m2r; - m2r.setFileFor(cfg.mFileFor); - m2r.setFlpName(cfg.mFlpName); - m2r.setVerbosity(cfg.mVerbosity); - if (cfg.mCcdbPath != "") { - m2r.setCCDBurl(cfg.mCcdbPath); - } - if (cfg.mChannelMapPath != "") { - m2r.setLUTpath(cfg.mChannelMapPath); - } - auto& wr = m2r.getWriter(); - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::FT0)); // must be set explicitly - const int superPageSizeInB = 1024 * 1024; - wr.setSuperPageSize(superPageSizeInB); - wr.useRDHVersion(cfg.mRdhVersion); - wr.setDontFillEmptyHBF(cfg.mNoEmptyHBF); - wr.useRDHDataFormat(cfg.mDataFormat); - if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used - wr.setAlignmentSize(16); // change to constexpr static field from class? - wr.setAlignmentPaddingFiller(0xff); - } - o2::raw::assertOutputDirectory(cfg.mOutputDir); - - std::string outDirName(cfg.mOutputDir); - if (outDirName.back() != '/') { - outDirName += '/'; - } - const auto& hbfu = o2::raw::HBFUtils::Instance(); - long startTime = hbfu.startTime > 0 ? hbfu.startTime : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - m2r.convertDigitsToRaw(outDirName, cfg.mInputFile, startTime); - wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FT0/workflow/CMakeLists.txt b/Detectors/FIT/FT0/workflow/CMakeLists.txt index 2dbbbae41e261..123a29293e2fb 100644 --- a/Detectors/FIT/FT0/workflow/CMakeLists.txt +++ b/Detectors/FIT/FT0/workflow/CMakeLists.txt @@ -98,6 +98,11 @@ o2_add_executable(recpoints-reader-workflow COMPONENT_NAME ft0 PUBLIC_LINK_LIBRARIES O2::FT0Workflow) +o2_add_executable(recpoints-writer-workflow + SOURCES src/recpoints-writer-workflow.cxx + COMPONENT_NAME ft0 + PUBLIC_LINK_LIBRARIES O2::FT0Workflow) + o2_add_executable(integrate-cluster-workflow SOURCES src/cluster-integrator.cxx COMPONENT_NAME ft0 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h index 4f8e8b5e9be63..d6009accfa45b 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h index 8fd597af8629d..a1b3714fdbb26 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace ft0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h index e039f18c47bc9..b78d0435d3636 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataDecoderDPLSpec.h @@ -110,7 +110,7 @@ framework::DataProcessorSpec getFT0DataDecoderDPLSpec(bool askSTFDist) std::vector outputSpec; outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{"FT0", "RAWDATA"}, Lifetime::Optional}}; + std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{"FT0", "RAWDATA"}, Lifetime::Timeframe}}; if (askSTFDist) { inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h deleted file mode 100644 index 7b7e98d50368e..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.h - -#ifndef O2_FT0DATAPROCESSDPLSPEC_H -#define O2_FT0DATAPROCESSDPLSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FT0Raw/DigitBlockFT0.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0DataProcessDPLSpec : public Task -{ - public: - FT0DataProcessDPLSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~FT0DataProcessDPLSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -framework::DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h deleted file mode 100644 index f7db7f3a0e7cb..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.h - -#ifndef O2_FT0DATAREADERDPLSPEC_H -#define O2_FT0DATAREADERDPLSPEC_H -#include "DataFormatsFT0/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "Framework/InputRecordWalker.h" -#include -#include -#include -#include "CommonUtils/VerbosityConfig.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -template -class FT0DataReaderDPLSpec : public Task -{ - public: - FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - FT0DataReaderDPLSpec() = default; - ~FT0DataReaderDPLSpec() override = default; - typedef RawReader RawReader_t; - void init(InitContext& ic) final { o2::ft0::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" - // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow - { - static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously - std::vector dummy{InputSpec{"dummy", ConcreteDataMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData, 0xDEADBEEF}}}; - for (const auto& ref : InputRecordWalker(pc.inputs(), dummy)) { - const auto dh = o2::framework::DataRefUtils::getHeader(ref); - auto payloadSize = DataRefUtils::getPayloadSize(ref); - if (payloadSize == 0) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", - dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, - contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - mRawReader.makeSnapshot(pc); // send empty output - return; - } - } - contDeadBeef = 0; // if good data, reset the counter - } - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData}, Lifetime::Timeframe}}; - DPLRawParser parser(pc.inputs(), filter); - std::size_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - mRawReader.clear(); - } - RawReader_t mRawReader; -}; - -template -framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader, bool askSTFDist) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, "RAWDATA"}, Lifetime::Optional}}; - if (askSTFDist) { - inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - return DataProcessorSpec{ - "ft0-datareader-dpl", - inputSpec, - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h deleted file mode 100644 index a4988b2c18fc7..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H - -/// @file FT0Workflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace ft0 -{ -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 -} // namespace o2 -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h deleted file mode 100644 index f50b3d224b32c..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -//file RawReaderFT0.h class for RAW data reading -// -// Artur.Furs -// afurs@cern.ch -// -//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for proccess -//TODO: prepare wrappers for containers with digits and combine classes below into one template class? -#ifndef ALICEO2_FIT_RAWREADERFT0_H_ -#define ALICEO2_FIT_RAWREADERFT0_H_ -#include -#include -#include -#include "FT0Raw/RawReaderFT0Base.h" - -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include "Framework/ProcessingContext.h" -#include "Framework/DataAllocator.h" -#include "Framework/OutputSpec.h" -#include - -namespace o2 -{ -namespace ft0 -{ -//Normal TCM mode -template -class RawReaderFT0 : public RawReaderFT0BaseNorm -{ - public: - RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0(const RawReaderFT0&) = default; - - RawReaderFT0() = default; - ~RawReaderFT0() = default; - static constexpr bool sUseTrgInput = useTrgInput; - void clear() - { - mVecDigits.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - mVecChannelData.clear(); - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - if constexpr (sUseTrgInput) { - LOG(info) << "Number of TriggerInput: " << mVecTriggerInput.size(); - } - if (mDumpData) { - DigitBlockFT0::print(mVecDigits, mVecChannelData); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecTriggerInput; - std::vector mVecChannelData; -}; - -//Extended TCM mode (additional raw data struct) -template -class RawReaderFT0ext : public RawReaderFT0BaseExt -{ - public: - RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0ext(const RawReaderFT0ext&) = default; - static constexpr bool sUseTrgInput = useTrgInput; - RawReaderFT0ext() = default; - ~RawReaderFT0ext() = default; - void clear() - { - mVecDigits.clear(); - mVecChannelData.clear(); - mVecTrgExt.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - LOG(info) << "Number of TriggerExt: " << mVecTrgExt.size(); - if (mDumpData) { - DigitBlockFT0ext::print(mVecDigits, mVecChannelData, mVecTrgExt); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h index 0df02634472ee..6de23a1c66bfd 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h index ee41569682a47..307b2109fe35f 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib), mUseDeadChannelMap(useDeadChannelMap) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,10 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseTimeOffsetCalib = true; + bool mUseSlewingCalib = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +57,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx index e4f11d3f5bd1e..09586d778ac15 100644 --- a/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/DigitReaderSpec.cxx @@ -64,13 +64,13 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "FT0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; - pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0, Lifetime::Timeframe}, digits); - pc.outputs().snapshot(Output{"FT0", "DIGITSCH", 0, Lifetime::Timeframe}, channels); + pc.outputs().snapshot(Output{"FT0", "DIGITSBC", 0}, digits); + pc.outputs().snapshot(Output{"FT0", "DIGITSCH", 0}, channels); if (mUseMC) { - pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + pc.outputs().snapshot(Output{"FT0", "DIGITSMCTR", 0}, labels); } if (mUseTrgInput) { - pc.outputs().snapshot(Output{"FT0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, trgInput); + pc.outputs().snapshot(Output{"FT0", "TRIGGERINPUT", 0}, trgInput); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 887d1f8c2d6dc..066c5cc547c2e 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -24,12 +24,12 @@ namespace o2 { namespace ft0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setDictBinding("ctfdict_FT0"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -50,8 +50,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_FT0"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& channels = pc.outputs().make>(OutputRef{"channels"}); @@ -72,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FT0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -80,17 +80,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "FT0", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionary")); + inputs.emplace_back("ctf_FT0", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FT0", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "ft0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx index 8f9e865ae8f99..7be6618a61103 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace ft0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,14 +47,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); auto channels = pc.inputs().get>("channels"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"FT0", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"FT0", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, channels); if (mSelIR) { mCTFCoder.getIRFramesSelector().clear(); @@ -70,12 +69,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FT0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FT0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -83,12 +85,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) "ft0-entropy-encoder", inputs, Outputs{{"FT0", "CTFDATA", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx index 945e7ad9d84d1..6d81740dd2173 100644 --- a/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/FT0DataDecoderDPLSpec.cxx @@ -28,12 +28,11 @@ namespace ft0 void FT0DataDecoderDPLSpec::run(ProcessingContext& pc) { auto t1 = std::chrono::high_resolution_clock::now(); - auto dummyOutput = [&pc, this]() { this->mVecDigits.resize(0); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); this->mVecChannelData.resize(0); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); }; // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" @@ -529,8 +528,8 @@ void FT0DataDecoderDPLSpec::run(ProcessingContext& pc) // Due to empty Digit container this dummy object will never participate in any further tasks. mVecChannelData.emplace_back(); } - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe}, mVecChannelData); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); + pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); auto t2 = std::chrono::high_resolution_clock::now(); auto delay = std::chrono::duration_cast(t2 - t1); LOG(debug) << "Decoder delay: " << delay.count(); diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx deleted file mode 100644 index d7a7a689d402f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.cxx - -#include "FT0Workflow/FT0DataProcessDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -using namespace std; -void FT0DataProcessDPLSpec::init(InitContext& ic) -{ -} - -void FT0DataProcessDPLSpec::run(ProcessingContext& pc) -{ - LOG(info) << "FT0DataProcessDPLSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFT0::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getFT0DataProcessDPLSpec"; - return DataProcessorSpec{ - "ft0-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx deleted file mode 100644 index caa642794b561..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.cxx - -#include "FT0Workflow/FT0DataReaderDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx deleted file mode 100644 index 156feb7dd3e2f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0Workflow.cxx - -#include "FT0Workflow/FT0Workflow.h" -#include "FT0Workflow/FT0DataProcessDPLSpec.h" -#include "FT0Workflow/FT0DataReaderDPLSpec.h" -#include "FT0Workflow/FT0DigitWriterSpec.h" -#include "FT0Workflow/RawReaderFT0.h" -namespace o2 -{ -namespace ft0 -{ - -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist) -{ - LOG(info) << "framework::WorkflowSpec getFT0Workflow"; - framework::WorkflowSpec specs; - if (isExtendedMode) { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader}, askSTFDist)); - } else { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader}, askSTFDist)); - } - if (useProcess) { - specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0DigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx deleted file mode 100644 index b2ef17e540112..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Workflow/RawReaderFT0.h" -using namespace o2::ft0; diff --git a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx index 69f21abc369c6..ba5ae4aa1356c 100644 --- a/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecPointReaderSpec.cxx @@ -49,8 +49,8 @@ void RecPointReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(debug) << "FT0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, *mChannelData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, *mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, *mChannelData); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx index d5cb7cdf980e9..6993c46197dac 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoQCworkflow.cxx @@ -132,7 +132,7 @@ DataProcessorSpec getRecoQCworkflow(GID::mask_t src) { auto dataRequest = std::make_shared(); LOG(info) << "@@ request primary vertex"; - dataRequest->requestPrimaryVertertices(false); + dataRequest->requestPrimaryVertices(false); dataRequest->requestFT0RecPoints(false); LOG(info) << "@@@ requested T0"; std::vector outputs; // empty diff --git a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx index 8c5ab34166760..2231011febd7f 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx @@ -22,13 +22,13 @@ namespace o2 namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::ft0::getDigitReaderSpec(useMC)); } - specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath)); + specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::ft0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx index 95ee4f3ff4b20..bc5217c8d7471 100644 --- a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx @@ -44,6 +44,7 @@ void ReconstructionDPL::init(InitContext& ic) LOG(info) << "FT0 param mMinRMS: " << CalibParam::Instance().mMinRMS; LOG(info) << "FT0 param mMaxSigma: " << CalibParam::Instance().mMaxSigma; LOG(info) << "FT0 param mMaxDiffMean: " << CalibParam::Instance().mMaxDiffMean; + LOG(info) << "FT0 dead channel map will be applied " << mUseDeadChannelMap; } void ReconstructionDPL::run(ProcessingContext& pc) @@ -59,25 +60,29 @@ void ReconstructionDPL::run(ProcessingContext& pc) if (mUseMC) { LOG(info) << "Ignoring MC info"; } - if (mUpdateCCDB) { - auto timeCalibObject = pc.inputs().get("ft0_timespectra"); - mReco.SetTimeCalibObject(timeCalibObject.get()); + if (mUseTimeOffsetCalib) { + auto timeOffsetCalibObject = pc.inputs().get("ft0_timespectra"); + mReco.SetTimeCalibObject(timeOffsetCalibObject.get()); } - /* - auto calibslew = mCCDBManager.get>("FT0/SlewingCorr"); - LOG(debug) << " calibslew " << calibslew; - if (calibslew) { - mReco.SetSlew(calibslew); - LOG(info) << " calibslew set slew " << calibslew; + + if (mUseSlewingCalib) { + auto slewingCalibObject = pc.inputs().get("ft0_slewing_coef"); + mReco.SetSlewingCalibObject(slewingCalibObject.get()); + } + + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(debug) << "Applying dead channel map"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); } - */ + mRecPoints.reserve(digits.size()); mRecChData.reserve(channels.size()); mReco.processTF(digits, channels, mRecPoints, mRecChData); // do we ignore MC in this task? LOG(debug) << "FT0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, mRecChData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); mTimer.Stop(); } @@ -85,7 +90,17 @@ void ReconstructionDPL::run(ProcessingContext& pc) void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (matcher == ConcreteDataMatcher("FT0", "TimeSpectraInfo", 0)) { - mUpdateCCDB = false; + LOG(debug) << "New TimeSpectraInfo is uploaded"; + return; + } + if (matcher == ConcreteDataMatcher("FT0", "SlewingCoef", 0)) { + LOG(debug) << "New SlewingCoef is uploaded"; + mUseSlewingCalib = false; // upload only once, slewing should be stable during the run + return; + } + if (matcher == ConcreteDataMatcher("FT0", "DeadChannelMap", 0)) { + LOG(debug) << "New DeadChannelMap is uploaded"; + mUpdateDeadChannelMap = false; return; } } @@ -96,19 +111,33 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); } - inputSpec.emplace_back("ft0_timespectra", "FT0", "TimeSpectraInfo", 0, - Lifetime::Condition, - ccdbParamSpec("FT0/Calib/TimeSpectraInfo")); + if (useTimeOffsetCalib) { + inputSpec.emplace_back("ft0_timespectra", "FT0", "TimeSpectraInfo", 0, + Lifetime::Condition, + ccdbParamSpec("FT0/Calib/TimeSpectraInfo", {}, 1)); + } + + if (useSlewingCalib) { + inputSpec.emplace_back("ft0_slewing_coef", "FT0", "SlewingCoef", 0, + Lifetime::Condition, + ccdbParamSpec("FT0/Calib/SlewingCoef")); + } + + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFT0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); @@ -117,7 +146,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "ft0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx index 6a98bbdafd53b..144d27abeda9c 100644 --- a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::ft0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx index ee84c8b35a5f6..ab39068aedb38 100644 --- a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -39,7 +39,10 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + {"disable-time-offset-calib", o2::framework::VariantType::Bool, false, {"disable timeoffset calibration"}}, + {"disable-slewing-calib", o2::framework::VariantType::Bool, false, {"disable slewing calibration"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"disable-dead-channel-map", VariantType::Bool, false, {"disable dead channel map"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -60,9 +63,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto ccdbpath = o2::base::NameConf::getCCDBServer(); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + const auto useTimeOffsetCalib = !configcontext.options().get("disable-time-offset-calib"); + const auto useSlewingCalib = !configcontext.options().get("disable-slewing-calib"); + const auto useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC << " CCDB " << ccdbpath; - auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, disableRootInp, disableRootOut); + auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx b/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx index 376a8385941ce..b1d824e10687e 100644 --- a/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx @@ -9,29 +9,35 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file recpoints-reader-workflow.cxx -/// \brief Implementation of FT0 digits reader +/// \file recpoints-reader-workflow.cxx +/// \brief FT0 RecPoints reader workflow /// -/// \author ruben.shahoyan@cern.ch +/// \author ruben.shahoyan@cern.ch, Andreas Molander andreas.molander@cern.ch -#include "Framework/CallbackService.h" -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/Task.h" -#include "FT0Workflow/RecPointReaderSpec.h" #include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FT0Workflow/RecPointReaderSpec.h" + +#include using namespace o2::framework; +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { - // option allowing to set parameters - - std::vector options{ - {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}}; - std::string keyvaluehelp("Semicolon separated key=value strings"); - options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {keyvaluehelp}}); + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -39,9 +45,14 @@ void customize(std::vector& workflowOptions) WorkflowSpec defineDataProcessing(const ConfigContext& ctx) { - WorkflowSpec specs; o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); - DataProcessorSpec producer = o2::ft0::getRecPointReaderSpec(ctx.options().get("disable-mc")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::ft0::getRecPointReaderSpec(!disableMC); specs.push_back(producer); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(ctx, specs); return specs; } diff --git a/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx b/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx new file mode 100644 index 0000000000000..c8baef76b5ccd --- /dev/null +++ b/Detectors/FIT/FT0/workflow/src/recpoints-writer-workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-writer-workflow.cxx +/// \brief FT0 RecPoints writer workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FT0Workflow/RecPointWriterSpec.h" + +#include + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::ft0::getRecPointWriterSpec(!disableMC); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/FV0/base/README.md b/Detectors/FIT/FV0/base/README.md new file mode 100644 index 0000000000000..1fba33c47b690 --- /dev/null +++ b/Detectors/FIT/FV0/base/README.md @@ -0,0 +1,17 @@ + + +# FV0 Base + +## Geometry + +The `o2::fv0::Geometry` class represents the geometry of the FV0 detector as used in simulation. It also provides utility methods for retrieving the center locations of the detector cells. See the below example for how to use the `Geometry` class to query the FV0 cell locations. Note that these are the ideal locations, and that any misalignment is not considered. + +```cpp +o2::fv0::Geometry* fv0Geometry = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); +o2::fv0::Point3Dsimple cellPosition = fv0Geometry->getReadoutCenter(chId); +float x = cellPosition.x; +float y = cellPosition.y; +float z = cellPosition.z; +``` diff --git a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h index 3b50be7441ec2..ec87c07c57c45 100644 --- a/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h +++ b/Detectors/FIT/FV0/base/include/FV0Base/Geometry.h @@ -133,6 +133,16 @@ class Geometry return o2::base::GeometryManager::getPNEntry(getDetID(), index); } + static std::string getDetectorRightSymName() + { + return sDetectorRightName + "_0"; + } + + static std::string getDetectorLeftSymName() + { + return sDetectorLeftName + "_1"; + } + /// Get the density of the PMTs. static constexpr float getPmtDensity() { @@ -143,6 +153,8 @@ class Geometry explicit Geometry(EGeoType initType); inline static const std::string sDetectorName = "FV0"; + inline static const std::string sDetectorRightName = sDetectorName + "RIGHT"; + inline static const std::string sDetectorLeftName = sDetectorName + "LEFT"; // General geometry constants static constexpr float sEpsilon = 0.01; ///< Used to make one spatial dimension infinitesimally larger than other diff --git a/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C b/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C index 06b86e3c5015d..3f42c0219b101 100644 --- a/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C +++ b/Detectors/FIT/FV0/calibration/macros/readChannelTimeOffsetFV0CalibObjectFromCCDB.C @@ -22,8 +22,8 @@ int readChannelTimeOffsetFV0CalibObjectFromCCDB(const std::string url = "http:// { o2::ccdb::CcdbApi api; api.init(url); - map metadata; - map headers; + std::map metadata; + std::map headers; auto retrieved = api.retrieveFromTFileAny("FV0/Calib/ChannelTimeOffset", metadata, -1, &headers); std::cout << "--- HEADERS ---" << std::endl; diff --git a/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h b/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h index a35750cab8a1b..7c5a888312713 100644 --- a/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h +++ b/Detectors/FIT/FV0/dcsmonitoring/workflow/FV0DCSConfigProcessorSpec.h @@ -40,8 +40,8 @@ DataProcessorSpec getFV0DCSConfigProcessorSpec() return DataProcessorSpec{ "fv0-dcs-config-processor", - Inputs{{"inputConfig", o2::header::gDataOriginFV0, "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", o2::header::gDataOriginFV0, "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", o2::header::gDataOriginFV0, "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", o2::header::gDataOriginFV0, "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask("FV0", ddDChM)}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, diff --git a/Detectors/FIT/FV0/macros/FV0Misaligner.C b/Detectors/FIT/FV0/macros/FV0Misaligner.C index 500bdaf565965..61be50b48dede 100644 --- a/Detectors/FIT/FV0/macros/FV0Misaligner.C +++ b/Detectors/FIT/FV0/macros/FV0Misaligner.C @@ -1,13 +1,32 @@ +// Copyright 2021-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FV0Misaligner.C +/// \brief ROOT macro for creating an FV0 geometry alignment object. The alignment object will align both +/// detector halves in the same way. Based on ITSMisaligner.C +/// +/// \author Andreas Molander andreas.molander@cern.ch, Alla Maevskaya + #if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "CCDB/CcdbApi.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/AlignParam.h" -#include "DetectorsBase/GeometryManager.h" -#include "CCDB/CcdbApi.h" -#include +#include "FV0Base/Geometry.h" + #include #include #include + #endif using AlgPar = std::array; @@ -20,16 +39,14 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" const std::string& fileName = "FV0Alignment.root") { std::vector params; - o2::base::GeometryManager::loadGeometry("", false); AlgPar pars; bool glo = true; o2::detectors::DetID detFV0("FV0"); - // FV0 detector - for (int ihalf = 1; ihalf < 3; ihalf++) { - std::string symName = Form("FV0half_%i", ihalf); - pars = generateMisalignment(x, y, z, psi, theta, phi); + pars = generateMisalignment(x, y, z, psi, theta, phi); + + for (auto& symName : {o2::fv0::Geometry::getDetectorRightSymName(), o2::fv0::Geometry::getDetectorLeftSymName()}) { params.emplace_back(symName.c_str(), -1, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); } @@ -37,7 +54,7 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detFV0) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); @@ -50,14 +67,15 @@ void FV0Misaligner(const std::string& ccdbHost = "http://ccdb-test.cern.ch:8080" algFile.Close(); } } + AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi) { AlgPar pars; - pars[0] = gRandom->Gaus(0, x); - pars[1] = gRandom->Gaus(0, y); - pars[2] = gRandom->Gaus(0, z); - pars[3] = gRandom->Gaus(0, psi); - pars[4] = gRandom->Gaus(0, theta); - pars[5] = gRandom->Gaus(0, phi); + pars[0] = x; + pars[1] = y; + pars[2] = z; + pars[3] = psi; + pars[4] = theta; + pars[5] = phi; return std::move(pars); } diff --git a/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h b/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h index 081ebac1c9a40..bfd95d5ff5c29 100644 --- a/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h +++ b/Detectors/FIT/FV0/raw/include/FV0Raw/DigitBlockFV0.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -//file DigitBlockFV0.h class for proccessing RAW data into Digits +// file DigitBlockFV0.h class for proccessing RAW data into Digits // // Artur.Furs // afurs@cern.ch @@ -25,10 +25,10 @@ namespace o2 { namespace fv0 { -//Normal data taking mode -using DigitBlockFV0 = DigitBlockFIT; -//TCM extended data taking mode -using DigitBlockFV0ext = DigitBlockFIText; +// Normal data taking mode +using DigitBlockFV0 = o2::fit::DigitBlockFIT; +// TCM extended data taking mode +using DigitBlockFV0ext = o2::fit::DigitBlockFIText; } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx b/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx index 4f53af0c80f79..37027f629550e 100644 --- a/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/DataBlockFV0.cxx @@ -10,3 +10,7 @@ // or submit itself to any jurisdiction. #include "FV0Raw/DataBlockFV0.h" + +template class o2::fit::DataBlockPM, o2::fv0::RawHeaderPM, o2::fv0::RawDataPM>; +template class o2::fit::DataBlockTCM, o2::fv0::RawHeaderTCM, o2::fv0::RawDataTCM>; +template class o2::fit::DataBlockTCMext, o2::fv0::RawHeaderTCMext, o2::fv0::RawDataTCM, o2::fv0::RawDataTCMext>; diff --git a/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx b/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx index 8da75f3bdbcff..367c57686ab95 100644 --- a/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/DigitBlockFV0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/DigitBlockFV0.h" + +template class o2::fit::DigitBlockFIT; +template class o2::fit::DigitBlockFIText; \ No newline at end of file diff --git a/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx b/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx index 7bba6ea3937c9..bbf2a780de0a1 100644 --- a/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx +++ b/Detectors/FIT/FV0/raw/src/RawReaderFV0Base.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/RawReaderFV0Base.h" + +template class o2::fit::RawReaderBaseFIT; +template class o2::fit::RawReaderBaseFIT; diff --git a/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx b/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx index 64d75091cbe80..d52e1117460a8 100644 --- a/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx +++ b/Detectors/FIT/FV0/raw/src/RawWriterFV0.cxx @@ -10,3 +10,6 @@ // or submit itself to any jurisdiction. #include "FV0Raw/RawWriterFV0.h" + +template class o2::fit::RawWriterFIT; +template class o2::fit::RawWriterFIT; diff --git a/Detectors/FIT/FV0/reconstruction/README.md b/Detectors/FIT/FV0/reconstruction/README.md new file mode 100644 index 0000000000000..46de9800fda92 --- /dev/null +++ b/Detectors/FIT/FV0/reconstruction/README.md @@ -0,0 +1,36 @@ + + +# FV0 reconstruction + +Please note that this readme shows how the reconstruction is done _at the moment_. The current reconstruction is not necessarily done the way it _should be_. The algorithm is under review and subject to change. + +The FV0 reconstruction workflow is + +- [O2/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx](../workflow/src/fv0-reco-workflow.cxx) + +and the actual reconstruction happens in + +- [src/BaseRecoTask.cxx](src/BaseRecoTask.cxx) ([include/FV0Reconstruction/BaseRecoTask.h](include/BaseRecoTask.h)) + +## Channel data reconstruction + +Currently, there's no channel data filtering applied in the FV0 reconstruction. All digit channel data are propagated to RecPoints. The following conversions are made: + +- Channel ID (`uint8_t`) -> Channel ID (`int`) +- CFD time (TDC units, `int16_t`) -> Time in ns (`double`) + - Furthermore, if available and enabled, a time offset correction is applied. For FV0 it is not available nor enabled at the moment. +- Amplitude (ADC channels, `int16_t`) -> Ampltidue (ADC channels, `double`) +- Channel bits (`uint8_t`) -> Channel bits (`int`) + +## RecPoint reconstruction + +The reconstructed RecPoints contain: + +- BC information (`o2::InteractionRecord`) +- Channel data +- Three different times: + - "TimeFirst": time of first signal (w/ charge > `FV0DigParam::chargeThrForMeanTime`) + - "TimeGlobalMean": average of all signals passing some filtering (including CFD in gate) + - "TimeSelectedMean": average of all signals passing a bit stricter filtering (includling CFD in gate) diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h index 12d89b82a13cc..c5cb5b0da6d05 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h @@ -18,6 +18,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/RecPoints.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include namespace o2 @@ -33,14 +34,15 @@ class BaseRecoTask ~BaseRecoTask() = default; o2::fv0::RecPoints process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData); + std::vector& outChData); void FinishTask(); void SetChannelOffset(o2::fv0::FV0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void SetDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; } int getOffset(int channel); private: o2::fv0::FV0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; - + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(BaseRecoTask, 3); }; } // namespace fv0 diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index f2b9c8e02c7f1..80dcd6060455b 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -30,10 +30,10 @@ namespace o2 namespace fv0 { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF @@ -68,15 +68,15 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig using MD = o2::ctf::Metadata::OptStore; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { - MD::EENCODE, // BLC_bcInc - MD::EENCODE, // BLC_orbitInc - MD::EENCODE, // BLC_nChan - MD::EENCODE, // BLC_idChan - MD::EENCODE, // BLC_cfdTime - MD::EENCODE, // BLC_qtcAmpl + MD::EENCODE_OR_PACK, // BLC_bcInc + MD::EENCODE_OR_PACK, // BLC_orbitInc + MD::EENCODE_OR_PACK, // BLC_nChan + MD::EENCODE_OR_PACK, // BLC_idChan + MD::EENCODE_OR_PACK, // BLC_cfdTime + MD::EENCODE_OR_PACK, // BLC_qtcAmpl // extra slot was added in the end - MD::EENCODE, // BLC_trigger - MD::EENCODE // BLC_qtcChain + MD::EENCODE_OR_PACK, // BLC_trigger + MD::EENCODE_OR_PACK // BLC_qtcChain }; CompressedDigits cd; if (mExtHeader.isValidDictTimeStamp()) { @@ -97,11 +97,10 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& dig ec->setHeader(cd.header); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEFV0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEFV0(part, slot, bits) CTF::get(buff.data())->encode(part, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEFV0(cd.bcInc, CTF::BLC_bcInc, 0); iosize += ENCODEFV0(cd.orbitInc, CTF::BLC_orbitInc, 0); @@ -129,7 +128,7 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VDIG& digitVec, VCHAN& checkDictVersion(hd); ec.print(getPrefix(), mVerbosity); o2::ctf::CTFIOSize iosize; -#define DECODEFV0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEFV0(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEFV0(cd.bcInc, CTF::BLC_bcInc); iosize += DECODEFV0(cd.orbitInc, CTF::BLC_orbitInc); diff --git a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx index 8a217232592df..8032220f8996d 100644 --- a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx @@ -27,7 +27,7 @@ using RP = o2::fv0::RecPoints; RP BaseRecoTask::process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData) + std::vector& outChData) { LOG(debug) << "Running reconstruction on new event"; @@ -44,22 +44,27 @@ RP BaseRecoTask::process(o2::fv0::Digit const& bcd, int nch = inChData.size(); for (int ich = 0; ich < nch; ich++) { LOG(debug) << " channel " << ich << " / " << nch; + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(inChData[ich].ChId)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } int offsetChannel = getOffset(int(inChData[ich].ChId)); - outChData[ich] = o2::fv0::ChannelDataFloat{inChData[ich].ChId, - (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, - (float)inChData[ich].QTCAmpl, - inChData[ich].ChainQTC}; + outChData.emplace_back(o2::fv0::ChannelDataFloat{inChData[ich].ChId, + (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, + (float)inChData[ich].QTCAmpl, + inChData[ich].ChainQTC}); + const auto& currentOutCh = outChData.back(); // Conditions for reconstructing collision time (3 variants: first, average-relaxed and average-tight) - if (outChData[ich].charge > FV0DigParam::Instance().chargeThrForMeanTime) { - sideAtimeFirst = std::min(static_cast(sideAtimeFirst), outChData[ich].time); + if (currentOutCh.charge > FV0DigParam::Instance().chargeThrForMeanTime) { + sideAtimeFirst = std::min(static_cast(sideAtimeFirst), currentOutCh.time); if (inChData[ich].areAllFlagsGood()) { - if (std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvg += outChData[ich].time; + if (std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvg += currentOutCh.time; ndigitsA++; } - if (outChData[ich].charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvgSelected += outChData[ich].time; + if (currentOutCh.charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvgSelected += currentOutCh.time; ndigitsASelected++; } } diff --git a/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx index e718dbd3c07f0..f2e4f59e10c97 100644 --- a/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/CTFCoder.cxx @@ -53,7 +53,7 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); CompressedDigits cd; // just to get member types -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cd.bcInc, CTF::BLC_bcInc); MAKECODER(cd.orbitInc, CTF::BLC_orbitInc); @@ -73,22 +73,17 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa size_t CTFCoder::estimateCompressedSize(const CompressedDigits& cd) { size_t sz = 0; - // clang-format off // RS FIXME this is very crude estimate, instead, an empirical values should be used -#define VTP(vec) typename std::remove_reference::type::value_type -#define ESTSIZE(vec, slot) mCoders[int(slot)] ? \ - rans::calculateMaxBufferSize(vec.size(), reinterpret_cast*>(mCoders[int(slot)].get())->getAlphabetRangeBits(), sizeof(VTP(vec)) ) : vec.size()*sizeof(VTP(vec)) - sz += ESTSIZE(cd.trigger, CTF::BLC_trigger); - sz += ESTSIZE(cd.bcInc, CTF::BLC_bcInc); - sz += ESTSIZE(cd.orbitInc, CTF::BLC_orbitInc); - sz += ESTSIZE(cd.nChan, CTF::BLC_nChan); + sz += estimateBufferSize(static_cast(CTF::BLC_trigger), cd.trigger); + sz += estimateBufferSize(static_cast(CTF::BLC_bcInc), cd.bcInc); + sz += estimateBufferSize(static_cast(CTF::BLC_orbitInc), cd.orbitInc); + sz += estimateBufferSize(static_cast(CTF::BLC_nChan), cd.nChan); - sz += ESTSIZE(cd.idChan, CTF::BLC_idChan); - sz += ESTSIZE(cd.qtcChain, CTF::BLC_qtcChain); - sz += ESTSIZE(cd.cfdTime, CTF::BLC_cfdTime); - sz += ESTSIZE(cd.qtcAmpl, CTF::BLC_qtcAmpl); - // clang-format on + sz += estimateBufferSize(static_cast(CTF::BLC_idChan), cd.idChan); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcChain), cd.qtcChain); + sz += estimateBufferSize(static_cast(CTF::BLC_cfdTime), cd.cfdTime); + sz += estimateBufferSize(static_cast(CTF::BLC_qtcAmpl), cd.qtcAmpl); - LOG(info) << "Estimated output size is " << sz << " bytes"; + LOG(debug) << "Estimated output size is " << sz << " bytes"; return sz; -} +}; \ No newline at end of file diff --git a/Detectors/FIT/FV0/simulation/CMakeLists.txt b/Detectors/FIT/FV0/simulation/CMakeLists.txt index d14b72450ff8f..6c696c52523ae 100644 --- a/Detectors/FIT/FV0/simulation/CMakeLists.txt +++ b/Detectors/FIT/FV0/simulation/CMakeLists.txt @@ -29,12 +29,8 @@ o2_target_root_dictionary(FV0Simulation o2_add_executable(digi2raw COMPONENT_NAME fv0 SOURCES src/digit2raw.cxx - PUBLIC_LINK_LIBRARIES O2::FV0Simulation - O2::DetectorsRaw - O2::DetectorsCommonDataFormats - O2::CommonUtils - Boost::program_options - O2::FV0Raw) + PUBLIC_LINK_LIBRARIES O2::FV0Raw + Boost::program_options) o2_data_file(COPY data DESTINATION Detectors/FV0/simulation) diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h index 3d36f255d5094..b97893822c9d8 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h @@ -14,6 +14,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsFV0/Digit.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "FV0Simulation/Detector.h" @@ -51,6 +52,7 @@ class Digitizer void setEventId(Int_t id) { mEventId = id; } void setSrcId(Int_t id) { mSrcId = id; } void setInteractionRecord(const InteractionTimeRecord& ir) { mIntRecord = ir; } + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; }; void process(const std::vector& hits, std::vector& digitsBC, std::vector& digitsCh, std::vector& digitsTrig, @@ -90,8 +92,16 @@ class Digitizer private: static constexpr int BCCacheMin = 0, BCCacheMax = 7, NBC2Cache = 1 + BCCacheMax - BCCacheMin; - void createPulse(float mipFraction, int parID, const double hitTime, std::array const& cachedIR, - int nCachedIR, const int detID); + /// Create signal pulse based on MC hit + /// \param mipFraction Fraction of the MIP energy deposited in the cell + /// \param parID Particle ID + /// \param hitTime Time of the hit + /// \param hitR Length to IP from the position of the hit + /// \param cachedIR Cached interaction records + /// \param nCachedIR Number of cached interaction records + /// \param detID Detector cell ID + void createPulse(float mipFraction, int parID, const double hitTime, const float hitR, + std::array const& cachedIR, int nCachedIR, const int detID); long mTimeStamp; // TF (run) timestamp InteractionTimeRecord mIntRecord; // Interaction record (orbit, bc) -> InteractionTimeRecord @@ -124,6 +134,8 @@ class Digitizer BCCache mLastBCCache; // buffer for the last BC std::array mCfdStartIndex; // start indices for the CFD detector + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; + /// Internal helper methods related to conversion of energy-deposition into el. signal Int_t SimulateLightYield(Int_t pmt, Int_t nPhot) const; Float_t SimulateTimeCfd(int& startIndex, const ChannelDigitF& pulseLast, const ChannelDigitF& pulse) const; diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h index 347f2e7bb707a..383fa4cb494c1 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FV0DigParam.h +/// \file FV0DigParam.h /// \brief Configurable digitization parameters #ifndef ALICEO2_FV0_DIG_PARAM @@ -22,9 +22,11 @@ namespace o2::fv0 { // parameters of FV0 digitization / transport simulation struct FV0DigParam : o2::conf::ConfigurableParamHelper { - float photoCathodeEfficiency = 0.23; // quantum efficiency = nOfPhotoE_emitted_by_photocathode / nIncidentPhotons - float lightYield = 0.01; // light collection efficiency to be tuned using collision data [1%] - float adcChannelsPerMip = 16; // Default: 16 for pp and 8 for PbPb + float hitTimeOffset = 0.0; ///< Hit time offset [ns] + + float photoCathodeEfficiency = 0.23; // quantum efficiency = nOfPhotoE_emitted_by_photocathode / nIncidentPhotons + float lightYield = 0.01; // light collection efficiency to be tuned using collision data [1%] + float adcChannelsPerMip = 16; // Default: 16 for pp and 8 for PbPb float getChannelsPerMilivolt() const { return adcChannelsPerMip / 7.5; } // Non-trivial conversion depending on the pulseshape: amplitude to charge float chargeThrForMeanTime = 5; // Charge threshold, only above which the time is taken into account in calculating the mean time of all qualifying channels @@ -55,19 +57,18 @@ struct FV0DigParam : o2::conf::ConfigurableParamHelper { bool isIntegrateFull = false; // Full charge integration widow in 25 ns float cfdCheckWindow = 2.5; // time window for the cfd in ns to trigger the charge integration int avgNumberPhElectronPerMip = 201; // avg number of photo-electrons per MIP - float globalTimeOfFlight = 315.0 / o2::constants::physics::LightSpeedCm2NS; // TODO [check the correct value for distance of FV0 to IP] float mCfdDeadTime = 15.6; // [ns] float mCFD_trsh = 3.; // [mV] float getCFDTrshInAdc() const { return mCFD_trsh * getChannelsPerMilivolt(); } // [ADC channels] /// Parameters for trigger simulation - bool useMaxChInAdc = true; // default = true - int adcChargeCenThr = 3 * 498; // threshold value of ADC charge for Central trigger - int adcChargeSCenThr = 1 * 498; // threshold value of ADC charge for Semi-central trigger - int maxCountInAdc = 4095; // to take care adc ADC overflow - short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) - uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC - static constexpr float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time - static constexpr short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + bool useMaxChInAdc = true; // default = true + int adcChargeCenThr = 3 * 498; // threshold value of ADC charge for Central trigger + int adcChargeSCenThr = 1 * 498; // threshold value of ADC charge for Semi-central trigger + int maxCountInAdc = 4095; // to take care adc ADC overflow + short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC + float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time + short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time O2ParamDef(FV0DigParam, "FV0DigParam"); }; diff --git a/Detectors/FIT/FV0/simulation/src/Detector.cxx b/Detectors/FIT/FV0/simulation/src/Detector.cxx index 8cf1f5530e93d..07eb9053bf3b8 100644 --- a/Detectors/FIT/FV0/simulation/src/Detector.cxx +++ b/Detectors/FIT/FV0/simulation/src/Detector.cxx @@ -280,6 +280,7 @@ void Detector::ConstructGeometry() // mGeometry->enableComponent(Geometry::eAluminiumContainer, false); mGeometry->buildGeometry(); } + void Detector::addAlignableVolumes() const { // @@ -292,19 +293,19 @@ void Detector::addAlignableVolumes() const LOG(info) << "FV0: Add alignable volumes"; if (!gGeoManager) { - LOG(fatal) << "TGeoManager doesn't exist !"; + LOG(fatal) << "TGeoManager doesn't exist!"; return; } - TString volPath, symName; - for (auto& half : {"RIGHT_0", "LEFT_1"}) { - volPath = Form("/cave_1/barrel_1/FV0_1/FV0%s", half); - symName = Form("FV0%s", half); - LOG(info) << "FV0: Add alignable volume: " << symName << ": " << volPath; - if (!gGeoManager->SetAlignableEntry(symName.Data(), volPath.Data())) { - LOG(fatal) << "FV0: Unable to set alignable entry! " << symName << ": " << volPath; + auto addAlignabelVolume = [](const std::string& volPath, const std::string& symName) -> void { + LOG(info) << "FV0: Add alignable volume: " << symName << " <-> " << volPath; + if (!gGeoManager->SetAlignableEntry(symName.c_str(), volPath.c_str())) { + LOG(fatal) << "FV0: Unable to set alignable entry! " << symName << " <-> " << volPath; } - } + }; + + addAlignabelVolume("/cave_1/barrel_1/FV0_1/FV0RIGHT_0", Geometry::getDetectorRightSymName()); + addAlignabelVolume("/cave_1/barrel_1/FV0_1/FV0LEFT_1", Geometry::getDetectorLeftSymName()); } o2::fv0::Hit* Detector::addHit(Int_t trackId, Int_t cellId, diff --git a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx index 184e6dbbd09f8..8c1d2dc8824e2 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -98,8 +98,13 @@ void Digitizer::process(const std::vector& hits, for (auto ids : hitIdx) { const auto& hit = hits[ids]; Int_t detId = hit.GetDetectorID(); - Double_t hitEdep = hit.GetHitValue() * 1e3; //convert to MeV - Float_t const hitTime = hit.GetTime() * 1e9; + + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(detId)) { + continue; + } + + Double_t hitEdep = hit.GetHitValue() * 1e3; // convert to MeV + Float_t const hitTime = hit.GetTime() * 1e9; // convert to ns // TODO: check how big is inaccuracy if more than 1 'below-threshold' particles hit the same detector cell if (hitEdep < FV0DigParam::Instance().singleMipThreshold || hitTime > FV0DigParam::Instance().singleHitTimeThreshold) { continue; @@ -138,15 +143,17 @@ void Digitizer::process(const std::vector& hits, if (tNS < 0 && cachedIR[nCachedIR] > irHit) { continue; // don't go to negative BC/orbit (it will wrap) } - setBCCache(cachedIR[nCachedIR++]); // ensure existence of cached container - } //BCCache loop - createPulse(mipFraction, hit.GetTrackID(), hitTime, cachedIR, nCachedIR, detId); + // ensure existence of cached container + setBCCache(cachedIR[nCachedIR++]); + } // BCCache loop + + createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId); } //while loop } //hitloop } -void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, +void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, const float hitR, std::array const& cachedIR, int nCachedIR, const int detId) { @@ -160,8 +167,9 @@ void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, } } - ///Time of flight subtracted from Hit time //TODO have different TOF according to thr ring number - Int_t const NBinShift = std::lround((hitTime - FV0DigParam::Instance().globalTimeOfFlight) / FV0DigParam::Instance().waveformBinWidth); + // Subtract time-of-flight from hit time + const float timeOfFlight = hitR / o2::constants::physics::LightSpeedCm2NS; + Int_t const NBinShift = std::lround((hitTime - timeOfFlight + FV0DigParam::Instance().hitTimeOffset) / FV0DigParam::Instance().waveformBinWidth); if (NBinShift >= 0 && NBinShift < FV0DigParam::Instance().waveformNbins) { mPmtResponseTemp.resize(FV0DigParam::Instance().waveformNbins, 0.); diff --git a/Detectors/FIT/FV0/simulation/src/digit2raw.cxx b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx index c75445648220c..c7441407093ab 100644 --- a/Detectors/FIT/FV0/simulation/src/digit2raw.cxx +++ b/Detectors/FIT/FV0/simulation/src/digit2raw.cxx @@ -13,52 +13,12 @@ /// \author ruben.shahoyan@cern.ch afurs@cern.ch #include -#include #include -#include -#include "CommonUtils/StringUtils.h" -#include "CommonUtils/ConfigurableParam.h" -#include "DetectorsRaw/HBFUtils.h" #include "FV0Raw/RawWriterFV0.h" -#include "DataFormatsParameters/GRPObject.h" -/// MC->raw conversion for FDD +/// MC->raw conversion for FV0 namespace bpo = boost::program_options; -struct Configuration { - Configuration(const bpo::variables_map& vm) - { - mInputFile = vm["input-file"].as(); - mOutputDir = vm["output-dir"].as(); - mVerbosity = vm["verbosity"].as(); - mFileFor = vm["file-for"].as(); - mRdhVersion = vm["rdh-version"].as(); - mEnablePadding = vm["enable-padding"].as(); - mNoEmptyHBF = vm["no-empty-hbf"].as(); - mFlpName = vm["flp-name"].as(); - mCcdbPath = vm["ccdb-path"].as(); - mChannelMapPath = vm["lut-path"].as(); - if (mRdhVersion < 7 && !mEnablePadding) { - mEnablePadding = true; - LOG(info) << "padding is always ON for RDH version " << mRdhVersion; - } - mDataFormat = mEnablePadding ? 0 : 2; - } - bool mNoEmptyHBF; - bool mEnablePadding; - int mVerbosity; - uint32_t mRdhVersion; - uint32_t mDataFormat; - std::string mInputFile; - std::string mOutputDir; - std::string mFileFor; - std::string mFlpName; - std::string mCcdbPath; - std::string mChannelMapPath; -}; - -template -void digit2raw(const Configuration& cfg); int main(int argc, char** argv) { @@ -70,21 +30,10 @@ int main(int argc, char** argv) bpo::positional_options_description opt_pos; try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); - add_option("input-file,i", bpo::value()->default_value("fv0digits.root"), "input FV0 digits file"); - add_option("flp-name", bpo::value()->default_value("alio2-cr1-flp180"), "single file per: all,flp,cru,link"); - add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); - add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); - uint32_t defRDH = o2::raw::RDHUtils::getVersion(); - add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); - add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); - add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); - add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); - add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); - add_option("lut-path", bpo::value()->default_value(""), "LookupTable path, e.g. FV0/LookupTable"); - add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + opt_general.add_options()("help,h", "Print this help message"); + // config with FV0 defaults + o2::fit::DigitToRawConfig::configureExecOptions(opt_general, "fv0digits.root", "alio2-cr1-flp180"); + // common part opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -103,63 +52,12 @@ int main(int argc, char** argv) std::cerr << e.what() << ", application will now exit" << std::endl; exit(2); } - - std::string confDig = vm["hbfutils-config"].as(); - if (!confDig.empty() && confDig != "none") { - o2::conf::ConfigurableParam::updateFromFile(confDig, "HBFUtils"); - } - o2::conf::ConfigurableParam::updateFromString(vm["configKeyValues"].as()); - - const Configuration cfg(vm); + const o2::fit::DigitToRawConfig cfg(vm); if (!cfg.mEnablePadding) { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } else { - digit2raw(cfg); + o2::fit::DigitToRawDevice::digit2raw(cfg); } o2::raw::HBFUtils::Instance().print(); - return 0; } - -template -void digit2raw(const Configuration& cfg) -{ - TStopwatch swTot; - swTot.Start(); - RawWriterType m2r; - m2r.setFileFor(cfg.mFileFor); - m2r.setFlpName(cfg.mFlpName); - m2r.setVerbosity(cfg.mVerbosity); - if (cfg.mCcdbPath != "") { - m2r.setCCDBurl(cfg.mCcdbPath); - } - if (cfg.mChannelMapPath != "") { - m2r.setLUTpath(cfg.mChannelMapPath); - } - auto& wr = m2r.getWriter(); - std::string inputGRP = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); - wr.setContinuousReadout(grp->isDetContinuousReadOut(o2::detectors::DetID::FV0)); // must be set explicitly - const int superPageSizeInB = 1024 * 1024; - wr.setSuperPageSize(superPageSizeInB); - wr.useRDHVersion(cfg.mRdhVersion); - wr.setDontFillEmptyHBF(cfg.mNoEmptyHBF); - wr.useRDHDataFormat(cfg.mDataFormat); - if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used - wr.setAlignmentSize(16); // change to constexpr static field from class? - wr.setAlignmentPaddingFiller(0xff); - } - o2::raw::assertOutputDirectory(cfg.mOutputDir); - - std::string outDirName(cfg.mOutputDir); - if (outDirName.back() != '/') { - outDirName += '/'; - } - const auto& hbfu = o2::raw::HBFUtils::Instance(); - long startTime = hbfu.startTime > 0 ? hbfu.startTime : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - m2r.convertDigitsToRaw(outDirName, cfg.mInputFile, startTime); - wr.writeConfFile(wr.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, wr.getOrigin().str, "raw.cfg")); - // - swTot.Stop(); - swTot.Print(); -} diff --git a/Detectors/FIT/FV0/workflow/CMakeLists.txt b/Detectors/FIT/FV0/workflow/CMakeLists.txt index eec745d5fdf1e..a304adc61b5fd 100644 --- a/Detectors/FIT/FV0/workflow/CMakeLists.txt +++ b/Detectors/FIT/FV0/workflow/CMakeLists.txt @@ -53,6 +53,16 @@ o2_add_executable(flp-dpl-workflow PUBLIC_LINK_LIBRARIES O2::FV0Workflow O2::FITWorkflow O2::FV0Raw TARGETVARNAME fv0flpexe) +o2_add_executable(recpoints-reader-workflow + SOURCES src/recpoints-reader-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + +o2_add_executable(recpoints-writer-workflow + SOURCES src/recpoints-writer-workflow.cxx + COMPONENT_NAME fv0 + PUBLIC_LINK_LIBRARIES O2::FV0Workflow) + o2_add_executable(integrate-cluster-workflow SOURCES src/cluster-integrator.cxx COMPONENT_NAME fv0 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h index 67b74f45e42bf..76f1aae5e728d 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyDecoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -41,7 +41,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h index db4f154a302c7..0df9403a88a12 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/EntropyEncoderSpec.h @@ -28,7 +28,7 @@ namespace fv0 class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,7 +42,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h index 015870d9178e2..f035b2406e5ba 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h index d71e154280e3d..934ce4d2c4a66 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::fv0::Constants::nFv0Channels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap), mCCDBpath(ccdbpath) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,8 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +55,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, bool useDeadChannelMap = true, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx index 08a6c4cbb2e24..a49bda2cec18b 100644 --- a/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/DigitReaderSpec.cxx @@ -65,13 +65,13 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "FV0DigitReader pushed " << channels.size() << " channels in " << digits.size() << " digits"; - pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0, Lifetime::Timeframe}, digits); - pc.outputs().snapshot(Output{"FV0", "DIGITSCH", 0, Lifetime::Timeframe}, channels); + pc.outputs().snapshot(Output{"FV0", "DIGITSBC", 0}, digits); + pc.outputs().snapshot(Output{"FV0", "DIGITSCH", 0}, channels); if (mUseMC) { - pc.outputs().snapshot(Output{"FV0", "DIGITSMCTR", 0, Lifetime::Timeframe}, labels); + pc.outputs().snapshot(Output{"FV0", "DIGITSMCTR", 0}, labels); } if (mUseTrgInput) { - pc.outputs().snapshot(Output{"FV0", "TRIGGERINPUT", 0, Lifetime::Timeframe}, trgInput); + pc.outputs().snapshot(Output{"FV0", "TRIGGERINPUT", 0}, trgInput); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 479ff466d8477..7babe9fdea6ed 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -24,12 +24,12 @@ namespace o2 { namespace fv0 { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setDictBinding("ctfdict_FV0"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -50,8 +50,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_FV0"); auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& channels = pc.outputs().make>(OutputRef{"channels"}); @@ -72,7 +72,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digits"}, "FV0", "DIGITSBC", 0, Lifetime::Timeframe}, @@ -80,17 +80,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "FV0", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionary")); + inputs.emplace_back("ctf_FV0", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_FV0", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "fv0-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx index a4a34313107d1..2448af09fac4e 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace fv0 { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -48,14 +47,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); auto channels = pc.inputs().get>("channels"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"FV0", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"FV0", "CTFDATA", 0}); auto iosize = mCTFCoder.encode(buffer, digits, channels); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -71,12 +70,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("digits", "FV0", "DIGITSBC", 0, Lifetime::Timeframe); inputs.emplace_back("channels", "FV0", "DIGITSCH", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -85,12 +87,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"FV0", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "FV0", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx b/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx index eda1f5061e6f2..57ecfe368625c 100644 --- a/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/FV0DigitWriterSpec.cxx @@ -51,19 +51,19 @@ o2::framework::DataProcessorSpec getFV0DigitWriterSpec(bool mctruth, bool trigIn "fv0digits.root", "o2sim", MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", "fv0-digits-branch-name", 1, + BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", 1, logger}, - BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh", "fv0-chhdata-branch-name"}, - BranchDefinition>{InputSpec{"digitTrinput", "FV0", "TRIGGERINPUT"}, "TRIGGERINPUT", "fv0-triggerinput-branch-name"}, + BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh"}, + BranchDefinition>{InputSpec{"digitTrinput", "FV0", "TRIGGERINPUT"}, "TRIGGERINPUT"}, std::move(labelsdef))(); } else { return MakeRootTreeWriterSpec("FV0DigitWriterRaw", "o2_fv0digits.root", "o2sim", MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", "fv0-digits-branch-name", 1, + BranchDefinition>{InputSpec{"digitBCinput", "FV0", "DIGITSBC"}, "FV0DigitBC", 1, logger}, - BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh", "fv0-chhdata-branch-name"}, + BranchDefinition>{InputSpec{"digitChinput", "FV0", "DIGITSCH"}, "FV0DigitCh"}, std::move(labelsdef))(); } } diff --git a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx index 163beacf0c140..5997cac500ee6 100644 --- a/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecPointReaderSpec.cxx @@ -49,8 +49,8 @@ void RecPointReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(debug) << "FV0 RecPointReader pushes " << mRecPoints->size() << " recpoints with " << mChannelData->size() << " channels at entry " << ent; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, *mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, *mChannelData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, *mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, *mChannelData); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx index 6bfc5479303d1..a0ef71b75765a 100644 --- a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,13 @@ namespace o2 namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fv0::getDigitReaderSpec(useMC)); } - - specs.emplace_back(o2::fv0::getReconstructionSpec(useMC)); + specs.emplace_back(o2::fv0::getReconstructionSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fv0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index d48b96fd4e626..cdf297b334588 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -21,6 +21,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -41,6 +42,7 @@ void ReconstructionDPL::run(ProcessingContext& pc) { mTimer.Start(false); mRecPoints.clear(); + mRecChData.clear(); auto digits = pc.inputs().get>("digits"); auto digch = pc.inputs().get>("digch"); // RS: if we need to process MC truth, uncomment lines below @@ -53,23 +55,24 @@ void ReconstructionDPL::run(ProcessingContext& pc) auto caliboffsets = pc.inputs().get("fv0offsets"); mReco.SetChannelOffset(caliboffsets.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } int nDig = digits.size(); LOG(debug) << " nDig " << nDig << " | ndigch " << digch.size(); mRecPoints.reserve(nDig); - mRecChData.resize(digch.size()); for (int id = 0; id < nDig; id++) { const auto& digit = digits[id]; LOG(debug) << " ndig " << id << " bc " << digit.getBC() << " orbit " << digit.getOrbit(); auto channels = digit.getBunchChannelData(digch); - gsl::span out_ch(mRecChData); - out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); - mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); + mRecPoints.emplace_back(mReco.process(digit, channels, mRecChData)); } LOG(debug) << "FV0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; - pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0, Lifetime::Timeframe}, mRecPoints); - pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0, Lifetime::Timeframe}, mRecChData); + pc.outputs().snapshot(Output{mOrigin, "RECPOINTS", 0}, mRecPoints); + pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); mTimer.Stop(); } @@ -80,6 +83,9 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUpdateCCDB = false; return; } + if (matcher == ConcreteDataMatcher(o2::header::gDataOriginFV0, "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -88,7 +94,7 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) { std::vector inputSpec; std::vector outputSpec; @@ -98,6 +104,10 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFV0, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFV0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/DeadChannelMap")); + } inputSpec.emplace_back("fv0offsets", "FV0", "TimeOffset", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/ChannelTimeOffset")); @@ -109,7 +119,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "fv0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap, ccdbpath)}, Options{}}; } diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx index 90f37996b55b7..932e0a37ee376 100644 --- a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::fv0::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx index 16d1383c7e8c4..309560e2d6b36 100644 --- a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -59,9 +60,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC; - auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx b/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx new file mode 100644 index 0000000000000..ecbe89b8bbed3 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/recpoints-reader-workflow.cxx @@ -0,0 +1,58 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-reader-workflow.cxx +/// \brief FV0 RecPoints reader workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include "FV0Workflow/RecPointReaderSpec.h" + +#include + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::fv0::getRecPointReaderSpec(!disableMC); + specs.push_back(producer); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(ctx, specs); + return specs; +} diff --git a/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx b/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx new file mode 100644 index 0000000000000..0fd3bd3bef2e8 --- /dev/null +++ b/Detectors/FIT/FV0/workflow/src/recpoints-writer-workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file recpoints-writer-workflow.cxx +/// \brief FV0 RecPoints writer workflow +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "FV0Workflow/RecPointWriterSpec.h" + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Variant.h" + +#include + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& ctx) +{ + o2::conf::ConfigurableParam::updateFromString(ctx.options().get("configKeyValues")); + bool disableMC = ctx.options().get("disable-mc"); + + WorkflowSpec specs; + DataProcessorSpec producer = o2::fv0::getRecPointWriterSpec(!disableMC); + specs.push_back(producer); + return specs; +} diff --git a/Detectors/FIT/README.md b/Detectors/FIT/README.md index b079dce2cc972..8da3f32d7f4e2 100644 --- a/Detectors/FIT/README.md +++ b/Detectors/FIT/README.md @@ -9,6 +9,9 @@ This is a top page for the FIT detector documentation. diff --git a/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h b/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h index f059d1f3af088..dc70d6a521a77 100644 --- a/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h +++ b/Detectors/FIT/common/calibration/include/FITCalibration/FITCalibrator.h @@ -63,13 +63,12 @@ class FITCalibrator final : public o2::calibration::TimeSlotCalibration md; auto* container = slot.getContainer(); - static const double TFlength = 1E-3 * o2::raw::HBFUtils::Instance().getNOrbitsPerTF() * o2::constants::lhc::LHCOrbitMUS; // in ms - auto starting = slot.getStartTimeMS(); - auto stopping = slot.getEndTimeMS(); - LOGP(info, "!!!! {}({})<=TF<={}({}), starting: {} stopping {}", slot.getTFStart(), slot.getStartTimeMS(), slot.getTFEnd(), slot.getEndTimeMS(), starting, stopping); - auto calibrationObject = container->generateCalibrationObject(slot.getStartTimeMS(), slot.getEndTimeMS(), mExtraInfo); + const auto startValidity = slot.getStartTimeMS() - o2::ccdb::CcdbObjectInfo::SECOND * 10; + const auto endValidity = slot.getEndTimeMS() + o2::ccdb::CcdbObjectInfo::MONTH; + LOGP(info, "!!!! {}<=TF<={}, startValidity: {} endValidity: {}", slot.getTFStart(), slot.getTFEnd(), startValidity, endValidity); + auto calibrationObject = container->generateCalibrationObject(startValidity, endValidity, mExtraInfo); std::vector preparedCalibObjects; - preparedCalibObjects.emplace_back(doSerializationAndPrepareObjectInfo(calibrationObject, starting, stopping)); + preparedCalibObjects.emplace_back(doSerializationAndPrepareObjectInfo(calibrationObject, startValidity, endValidity)); mStoredCalibrationObjects.insert(mStoredCalibrationObjects.end(), std::make_move_iterator(preparedCalibObjects.begin()), std::make_move_iterator(preparedCalibObjects.end())); diff --git a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h index f3ed3229d9e55..18c0b593b0a02 100644 --- a/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h +++ b/Detectors/FIT/common/dcsmonitoring/include/FITDCSMonitoring/FITDCSConfigProcessorSpec.h @@ -47,7 +47,7 @@ class FITDCSConfigProcessor : public o2::framework::Task void init(o2::framework::InitContext& ic) final { initDCSConfigReader(); - mDCSConfigReader->setFileNameDChM(ic.options().get("filename-dchm")); + mDCSConfigReader->setFileNameDChM(ic.options().get("filename-dchm")); mDCSConfigReader->setValidDaysDChM(ic.options().get("valid-days-dchm")); mDCSConfigReader->setCcdbPathDChM(mDetectorName + "/Calib/DeadChannelMap"); mVerbose = ic.options().get("use-verbose-mode"); diff --git a/Detectors/FIT/macros/CMakeLists.txt b/Detectors/FIT/macros/CMakeLists.txt index c956392e5a1cd..a6bf1799a5dde 100644 --- a/Detectors/FIT/macros/CMakeLists.txt +++ b/Detectors/FIT/macros/CMakeLists.txt @@ -38,4 +38,16 @@ o2_add_test_root_macro(readFITDeadChannelMap.C o2_add_test_root_macro(readFITDCSdata.C PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB - LABELS fit) \ No newline at end of file + LABELS fit) + +o2_add_test_root_macro(compareRecPoints.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsFT0 + O2::DataFormatsFIT + LABELS fit) + +o2_add_test_root_macro(readAlignParam.C + PUBLIC_LINK_LIBRARIES O2::CCDB + LABELS fit) + +o2_data_file(COPY readFITDCSdata.C DESTINATION Detectors/FIT/macros/) +o2_data_file(COPY readFITDeadChannelMap.C DESTINATION Detectors/FIT/macros/) \ No newline at end of file diff --git a/Detectors/FIT/macros/compareRecPoints.C b/Detectors/FIT/macros/compareRecPoints.C new file mode 100644 index 0000000000000..0ce077bc616ba --- /dev/null +++ b/Detectors/FIT/macros/compareRecPoints.C @@ -0,0 +1,110 @@ +// Copyright 2019-2024 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file compareRecPoints.C +/// \brief ROOT macro to compare two trees with RecPoints +/// +/// \author Artur Furs artur.furs@cern.ch, Andreas Molander andreas.molander@cern.ch + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsFV0/RecPoints.h" +#include "DataFormatsFDD/RecPoint.h" + +#include "TFile.h" +#include "TTree.h" + +#include +#include +#include +#include +#endif + +void compareRecPoints(std::string filename1, std::string filename2) +{ + std::unique_ptr file1(TFile::Open(filename1.c_str(), "READ")); + TTree* tree1 = (TTree*)file1->Get("o2sim"); + + std::unique_ptr file2(TFile::Open(filename2.c_str(), "READ")); + TTree* tree2 = (TTree*)file2->Get("o2sim"); + + if (tree1->GetEntries() != tree2->GetEntries()) { + std::cout << "Non equal number of entries in trees!" << std::endl; + return; + } + + typedef typename o2::ft0::RecPoints RecPoint; + typedef typename o2::ft0::ChannelDataFloat ChannelDataFloat; + + std::vector vecRecPoints1; + std::vector* ptrVecRecPoints1 = &vecRecPoints1; + + std::vector vecChannelDataFloat1; + std::vector* ptrVecChannelDataFloat1 = &vecChannelDataFloat1; + + tree1->SetBranchAddress("FT0Cluster", &ptrVecRecPoints1); + tree1->SetBranchAddress("FT0RecChData", &ptrVecChannelDataFloat1); + + std::vector vecRecPoints2; + std::vector* ptrVecRecPoints2 = &vecRecPoints2; + + std::vector vecChannelDataFloat2; + std::vector* ptrVecChannelDataFloat2 = &vecChannelDataFloat2; + + tree2->SetBranchAddress("FT0Cluster", &ptrVecRecPoints2); + tree2->SetBranchAddress("FT0RecChData", &ptrVecChannelDataFloat2); + + for (int iEntry = 0; iEntry < tree1->GetEntries(); iEntry++) { + tree1->GetEntry(iEntry); + tree2->GetEntry(iEntry); + + if (vecRecPoints1 != vecRecPoints2) { + std::cout << "Non equal RecPoints vector!" << std::endl; + + if (vecRecPoints1.size() == vecRecPoints2.size()) { + for (int iEvent = 0; iEvent < vecRecPoints1.size(); iEvent++) { + const auto& recPoint1 = vecRecPoints1[iEvent]; + const auto& recPoint2 = vecRecPoints2[iEvent]; + + if (!(recPoint1 == recPoint2)) { + std::cout << "First RecPoint" << std::endl; + recPoint1.print(); + std::cout << "Second RecPoint" << std::endl; + recPoint2.print(); + } + } + } else { + std::cout << "Non equal number of RecPoints!" << std::endl; + } + } + if (vecChannelDataFloat1 != vecChannelDataFloat2) { + std::cout << "Non equal ChannelDataFloat vector!" << std::endl; + + if (vecChannelDataFloat1.size() == vecChannelDataFloat2.size()) { + for (int iEvent = 0; iEvent < vecChannelDataFloat1.size(); iEvent++) { + const auto& channelDataFloat1 = vecChannelDataFloat1[iEvent]; + const auto& channelDataFloat2 = vecChannelDataFloat2[iEvent]; + + if (!(channelDataFloat1 == channelDataFloat2)) { + std::cout << "First ChannelDataFloat" << std::endl; + channelDataFloat1.print(); + std::cout << "Second ChannelDataFloat" << std::endl; + channelDataFloat2.print(); + } + } + } else { + std::cout << "Non equal number of ChannelDataFloat!" << std::endl; + } + } + } + + return; +} \ No newline at end of file diff --git a/Detectors/FIT/macros/readAlignParam.C b/Detectors/FIT/macros/readAlignParam.C new file mode 100644 index 0000000000000..c438e7a0c86a5 --- /dev/null +++ b/Detectors/FIT/macros/readAlignParam.C @@ -0,0 +1,51 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file readAlignParam.C +/// \brief ROOT macro for reading geometry alignment parameters +/// +/// \author Andreas Molander + +#if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "CCDB/BasicCCDBManager.h" +#include "DetectorsCommonDataFormats/AlignParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" + +#include +#include + +#endif + +int readAlignParam(const std::string& detectorName = "FT0", + long timestamp = -1, + const std::string& ccdbUrl = "https://alice-ccdb.cern.ch") +{ + o2::ccdb::BasicCCDBManager& ccdbManager = o2::ccdb::BasicCCDBManager::instance(); + ccdbManager.setURL(ccdbUrl); + ccdbManager.setTimestamp(timestamp); + + const o2::detectors::DetID detID(detectorName.c_str()); + const std::string alignmentPath = o2::base::DetectorNameConf::getAlignmentPath(detID); + const auto alignments = ccdbManager.get>(alignmentPath); + + if (!alignments) { + std::cerr << "No alignment parameters found at " << alignmentPath << std::endl; + return 1; + } + + for (auto alignment : *alignments) { + alignment.print(); + } + + return 0; +} \ No newline at end of file diff --git a/Detectors/FIT/macros/readFITDCSdata.C b/Detectors/FIT/macros/readFITDCSdata.C index e531e393f8bf6..43f59ca8d0cfa 100644 --- a/Detectors/FIT/macros/readFITDCSdata.C +++ b/Detectors/FIT/macros/readFITDCSdata.C @@ -84,7 +84,7 @@ void readFITDCSdata(std::string detectorName = "FT0", const std::string& rootOutput = "", const bool print = false, const std::string& textOutput = "", - const bool verbose = true) + const bool verbose = false) { // Parse and check detector name boost::to_upper(detectorName); @@ -181,7 +181,7 @@ void readFITDCSdata(std::string detectorName = "FT0", } // The CCDB object should always contain values for all datapoints. This is just to check that. - if ((detectorName == "FT0" && ccdbMap->size() != 477) || (detectorName == "FV0" && ccdbMap->size() != 147) || (detectorName == "FDD" && ccdbMap->size() != 76)) { + if (verbose && ((detectorName == "FT0" && ccdbMap->size() != 501) || (detectorName == "FV0" && ccdbMap->size() != 147) || (detectorName == "FDD" && ccdbMap->size() != 76))) { LOGP(error, "Wrong number of DCS datapoints fetched for {}, got {}. There is a bug, please send output of this script, with input parameters, to andreas.molander@cern.ch.", detectorName, ccdbMap->size()); @@ -230,16 +230,19 @@ void readFITDCSdata(std::string detectorName = "FT0", LOG(info) << "Printing data point values:"; for (auto& it : dataSeries) { LOGP(info, "{}", it.first); - LOGP(info, "{} value(s):", it.second.values.size()); - if (verbose) { - for (auto& value : it.second.values) { - LOGP(info, "TIME = {} ({}), VALUE = {}", value.first, epochToReadable(value.first), value.second); + int nValues = it.second.values.size(); + LOGP(info, "{} value(s):", nValues); + if (nValues) { + if (verbose) { + for (auto& value : it.second.values) { + LOGP(info, "TIME = {} ({}), VALUE = {}", value.first, epochToReadable(value.first), value.second); + } + } else { + LOGP(info, "First value:"); + LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.front().first, epochToReadable(it.second.values.front().first), it.second.values.front().second); + LOGP(info, "Last value:"); + LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.back().first, epochToReadable(it.second.values.back().first), it.second.values.back().second); } - } else { - LOGP(info, "First value:"); - LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.front().first, epochToReadable(it.second.values.front().first), it.second.values.front().second); - LOGP(info, "Last value:"); - LOGP(info, "TIME = {} ({}), VALUE = {}", it.second.values.back().first, epochToReadable(it.second.values.back().first), it.second.values.back().second); } LOG(info); } diff --git a/Detectors/FIT/macros/readFT0hits.C b/Detectors/FIT/macros/readFT0hits.C index 14d25fa4a99a8..fafcaac570311 100644 --- a/Detectors/FIT/macros/readFT0hits.C +++ b/Detectors/FIT/macros/readFT0hits.C @@ -1,13 +1,29 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + #if !defined(__CLING__) || defined(__ROOTCLING__) + +#include "DataFormatsFIT/Triggers.h" #include "DataFormatsFT0/Digit.h" #include "DataFormatsFT0/HitType.h" #include "SimulationDataFormat/MCEventHeader.h" #include +#include #include #include #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" +#endif + void readFT0hits() { @@ -24,6 +40,8 @@ void readFT0hits() TH2F* hPel = new TH2F("hPelDig", "N p.e. ", 220, 0, 220, 500, 0, 10000); TH2F* hXYA = new TH2F("hXYA", "X vs Y A side", 400, -20, 20, 400, -20, 20); TH2F* hXYC = new TH2F("hXYC", "X vs Y C side", 400, -20, 20, 400, -20, 20); + TH1F* hZA = new TH1F("hZA", "Z A side", 200, 330, 340); + TH1F* hZC = new TH1F("hZC", "Z C side", 200, -90, -80); gDirectory = cwd; @@ -59,10 +77,13 @@ void readFT0hits() hTimeHitA->Fill(detID, hit_time[detID] - 11.04); hTimeHitC->Fill(detID, hit_time[detID] - 2.91); countE[detID]++; - if (detID < 96) + if (detID < 96) { hXYA->Fill(hit.GetX(), hit.GetY()); - if (detID > 95) + hZA->Fill(hit.GetZ()); + } else { hXYC->Fill(hit.GetX(), hit.GetY()); + hZC->Fill(hit.GetZ()); + } } for (int ii = 0; ii < 220; ii++) { if (countE[ii] > 100) { @@ -82,6 +103,6 @@ void readFT0hits() hMultHit->Write(); hXYA->Write(); hXYC->Write(); - + hZA->Write(); + hZC->Write(); } // end of macro -#endif diff --git a/Detectors/FIT/macros/readFV0hits.C b/Detectors/FIT/macros/readFV0hits.C index 5b0dfa8428dc7..933138fb1434b 100644 --- a/Detectors/FIT/macros/readFV0hits.C +++ b/Detectors/FIT/macros/readFV0hits.C @@ -1,3 +1,14 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -22,6 +33,8 @@ #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DetectorsCommonDataFormats/DetID.h" +#endif + void AdjustStatBox(TH1* h, float x1ndc, float x2ndc, float y1ndc, float y2ndc) { gPad->Update(); @@ -54,6 +67,9 @@ void InitHistoNames(std::vector& vhName, std::vector& vPdg) vhName.push_back("hElossDet"); vhName.push_back("hEtotVsR"); vhName.push_back("hEtotVsEloss"); + vhName.push_back("hXY"); + vhName.push_back("hXYzoom"); + vhName.push_back("hZ"); for (UInt_t ipdg = 0; ipdg < vPdg.size(); ipdg++) { std::stringstream ss; @@ -63,7 +79,7 @@ void InitHistoNames(std::vector& vhName, std::vector& vPdg) } } -void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) +void readFV0hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) { using namespace o2::detectors; std::string simFName(o2::base::DetectorNameConf::getHitsFileName(DetID::FV0, simPrefix)); @@ -85,6 +101,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) TH2F* hElossDet = new TH2F(vHistoNames.at(8).c_str(), "", nEl, 0, el1, nCells, 0, nCells); TH2F* hEtotVsR = new TH2F(vHistoNames.at(9).c_str(), "", 30000, 0, 300, 80, 0, 80); TH2F* hEtotVsEloss = new TH2F(vHistoNames.at(10).c_str(), "", 30000, 0, 300, nEl, 0, el1); + TH2F* hXY = new TH2F(vHistoNames.at(11).c_str(), "", 200, -100, 100, 200, -100, 100); + TH2F* hXYzoom = new TH2F(vHistoNames.at(12).c_str(), "", 200, -20, 20, 200, -20, 20); + TH1F* hZ = new TH1F(vHistoNames.at(13).c_str(), "", 200, 315, 325); // Setup histo properties hElossDet->SetXTitle("Energy loss [MeV]"); @@ -96,6 +115,14 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) hEtotVsEloss->SetXTitle("Total energy at entrance [MeV]"); hEtotVsEloss->SetYTitle("Energy loss [MeV]"); hEtotVsEloss->SetZTitle("Counts"); + hXY->SetXTitle("X [cm]"); + hXY->SetYTitle("Y [cm]"); + hXY->SetZTitle("Counts"); + hXYzoom->SetXTitle("X [cm]"); + hXYzoom->SetYTitle("Y [cm]"); + hXYzoom->SetZTitle("Counts"); + hZ->SetXTitle("Hit Z-coordinate [cm]"); + hZ->SetYTitle("Counts"); for (UInt_t ih = 0; ih < vhElossVsDistance.size(); ih++) { TH2F* h = vhElossVsDistance.at(ih); std::stringstream ss; @@ -124,6 +151,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) vh.push_back(hEtotVsEloss); vh.insert(vh.end(), vhElossVsDistance.begin(), vhElossVsDistance.end()); vh.insert(vh.end(), vhElossVsEtot.begin(), vhElossVsEtot.end()); + vh.push_back(hXY); + vh.push_back(hXYzoom); + vh.push_back(hZ); for (UInt_t ih = 0; ih < vh.size(); ih++) { vh[ih]->SetDirectory(0); vh[ih]->GetXaxis()->SetTitleSize(fontsize); @@ -177,6 +207,9 @@ void readFV0Hits(std::string simPrefix = "o2sim", UInt_t rebin = 1) vhElossVsDistance.at(vhElossVsDistance.size() - 1)->Fill(hit->GetEnergyLoss() * 1e3, distance); vhElossVsEtot.at(vhElossVsEtot.size() - 1)->Fill(hit->GetEnergyLoss() * 1e3, hit->GetTotalEnergyAtEntrance() * 1e3); } + hXY->Fill(hit->GetX(), hit->GetY()); + hXYzoom->Fill(hit->GetX(), hit->GetY()); + hZ->Fill(hit->GetZ()); } } @@ -323,5 +356,3 @@ int compareFV0Hits(std::string simFName1 = "fv0hit-rawhistos.root", std::string } return 0; } - -#endif diff --git a/Detectors/FIT/raw/CMakeLists.txt b/Detectors/FIT/raw/CMakeLists.txt index 9090f54029017..d8ab9964dbee4 100644 --- a/Detectors/FIT/raw/CMakeLists.txt +++ b/Detectors/FIT/raw/CMakeLists.txt @@ -10,5 +10,19 @@ # or submit itself to any jurisdiction. o2_add_library(FITRaw - SOURCES src/DataBlockBase.cxx src/DataBlockFIT.cxx src/DigitBlockBase.cxx src/DigitBlockFIT.cxx src/RawReaderBase.cxx src/RawReaderBaseFIT.cxx src/RawWriterFIT.cxx - PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers Microsoft.GSL::GSL O2::DetectorsRaw O2::CommonUtils) + SOURCES src/DataBlockBase.cxx + src/DataBlockFIT.cxx + src/DigitBlockBase.cxx + src/DigitBlockFIT.cxx + src/RawReaderBase.cxx + src/RawReaderBaseFIT.cxx + src/RawWriterFIT.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::Headers + Microsoft.GSL::GSL + O2::DetectorsRaw + O2::CommonUtils + O2::DataFormatsFIT + O2::DetectorsCommonDataFormats + O2::DataFormatsParameters + Boost::program_options) diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h index 6320ab4d64a82..1c95f354c6292 100644 --- a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h @@ -41,6 +41,7 @@ #include #include #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFIT/RawDataMetric.h" #include "Headers/RAWDataHeader.h" #include #include @@ -57,8 +58,6 @@ namespace o2 namespace fit { -using namespace std; - static constexpr size_t SIZE_WORD = 16; static constexpr size_t SIZE_WORD_GBT = 10; // should be changed to gloabal variable static constexpr size_t SIZE_MAX_PAGE = 8192; // should be changed to gloabal variable @@ -145,49 +144,50 @@ struct DataBlockWrapper { constexpr static std::size_t sSizeWord = getWordSize(); void serialize(std::vector& vecBytes, size_t nWords, size_t& destPos) const { - const uint8_t* srcAddress = (uint8_t*)mData; - if (nWords == 0 || nWords > MaxNwords || vecBytes.size() - destPos < nWords * sSizeWord) { + const auto nBytesToWrite = nWords * sSizeWord; + if ((vecBytes.size() - destPos) < nBytesToWrite || nWords < MinNwords || nWords > MaxNwords) { LOG(info) << "Warning! Incorrect serialisation procedure!"; return; + } else if (nWords == 0) { + // nothing to do + return; } + const uint8_t* srcAddress = (uint8_t*)mData; gsl::span serializedBytes(vecBytes); if constexpr (sIsPadded) { // in case of padding one need to use byte map int nSteps = std::get(sReadingLookupTable[nWords]); for (int iStep = 0; iStep < nSteps; iStep++) { memcpy(serializedBytes.data() + std::get(sByteLookupTable[iStep]) + destPos, srcAddress + std::get(sByteLookupTable[iStep]), std::get(sByteLookupTable[iStep])); } - destPos += nWords * sSizeWord; } else { // no need in byte map - const std::size_t nBytes = nWords * sSizeWord; - memcpy(serializedBytes.data() + destPos, srcAddress, nBytes); - destPos += nBytes; + memcpy(serializedBytes.data() + destPos, srcAddress, nBytesToWrite); } + destPos += nBytesToWrite; } void deserialize(const gsl::span inputBytes, size_t nWords, size_t& srcPos) { mNelements = 0; - mNwords = 0; - if (nWords < MinNwords || nWords > MaxNwords || inputBytes.size() - srcPos < nWords * sSizeWord) { + const auto nBytesToRead = nWords * sSizeWord; + if ((inputBytes.size() - srcPos) < nBytesToRead || nWords < MinNwords || nWords > MaxNwords) { // in case of bad fields responsible for deserialization logic, byte position will be pushed to the end of binary sequence + LOG(error) << "Incomplete payload! |N GBT words " << nWords << " |bytes to read " << nBytesToRead << " |src pos " << srcPos << " |payload size " << inputBytes.size() << " |"; srcPos = inputBytes.size(); + RawDataMetric::setStatusBit(mStatusBits, RawDataMetric::EStatusBits::kIncompletePayload); mIsIncorrect = true; return; } uint8_t* destAddress = (uint8_t*)mData; - mNwords = nWords; mNelements = std::get(sReadingLookupTable[nWords]); if constexpr (sIsPadded) { // in case of padding one need to use byte map int nSteps = std::get(sReadingLookupTable[nWords]); for (int iStep = 0; iStep < nSteps; iStep++) { memcpy(destAddress + std::get(sByteLookupTable[iStep]), inputBytes.data() + std::get(sByteLookupTable[iStep]) + srcPos, std::get(sByteLookupTable[iStep])); } - srcPos += mNwords * sSizeWord; } else { // no need in byte map - const std::size_t nBytes = mNwords * sSizeWord; - memcpy(destAddress, inputBytes.data() + srcPos, nBytes); - srcPos += nBytes; + memcpy(destAddress, inputBytes.data() + srcPos, nBytesToRead); } + srcPos += nBytesToRead; } static constexpr int MaxNwords = Data_t::PayloadSize * Data_t::MaxNelements / Data_t::PayloadPerGBTword + (Data_t::PayloadSize * Data_t::MaxNelements % Data_t::PayloadPerGBTword > 0); // calculating max GBT words per block @@ -376,8 +376,8 @@ struct DataBlockWrapper { } } Data_t mData[Data_t::MaxNelements]; - unsigned int mNelements; // number of deserialized elements; - unsigned int mNwords; // number of deserialized GBT words; //can be excluded + unsigned int mNelements; // number of deserialized elements; + typename RawDataMetric::Status_t mStatusBits{}; // Contains status bits bool mIsIncorrect; }; @@ -445,16 +445,20 @@ class DataBlockBase : public boost::mpl::inherit::mIsIncorrect), ...); // checking deserialization status for sub-block - static_cast(this)->sanityCheck(mIsCorrect); + mergeStatusBits(mStatusBitsAll, DataBlockWrapperHeader_t::mStatusBits); // checking deserialization status for header + (mergeStatusBits(mStatusBitsAll, DataBlockWrapper::mStatusBits), ...); // checking deserialization status for sub-block + static_cast(this)->sanityCheck(mIsCorrect, mStatusBitsAll); } - size_t mSize; // deserialized size - bool mIsCorrect; + size_t mSize{0}; // deserialized size + bool mIsCorrect{false}; + typename RawDataMetric::Status_t mStatusBitsAll{}; protected: // check if there are sub blocks with zero number of elements void isNonZeroBlockSizes(bool& flag, unsigned int nElements) { flag &= (bool)nElements; } void checkDeserialization(bool& flag, bool isIncorrect) { flag &= !(isIncorrect); } + void mergeStatusBits(uint8_t& statusBitsResult, uint8_t statusBits) { statusBitsResult |= statusBits; } }; } // namespace fit diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h b/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h index c00e66cbac17e..676ed276f675f 100644 --- a/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockFIT.h @@ -20,11 +20,11 @@ #include #include #include +#include #include #include #include -using namespace o2::fit; namespace o2 { namespace fit @@ -52,18 +52,31 @@ class DataBlockPM : public DataBlockBase serialize() const { std::size_t nBytes = HeaderPM::MaxNwords * HeaderPM::sSizeWord; - nBytes += HeaderPM::mData[0].nGBTWords * HeaderPM::sSizeWord; + const std::size_t nGBTWords = HeaderPM::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderPM::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); - DataPM::serialize(vecBytes, HeaderPM::mData[0].nGBTWords, destBytes); + const auto statusBits = HeaderPM::mStatusBits | DataPM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderPM::mData[0].nGBTWords = DataPM::MaxNwords + 1; + dataBlockTmp.HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); + } else { + HeaderPM::serialize(vecBytes, HeaderPM::MaxNwords, destBytes); + } + DataPM::serialize(vecBytes, nGBTWords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { + if (HeaderPM::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } if (DataPM::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); flag = false; return; } @@ -95,18 +108,34 @@ class DataBlockTCM : public DataBlockBase serialize() const { std::size_t nBytes = HeaderTCM::MaxNwords * HeaderTCM::sSizeWord; - nBytes += HeaderTCM::mData[0].nGBTWords * HeaderTCM::sSizeWord; + const std::size_t nGBTWords = HeaderTCM::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderTCM::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); - DataTCM::serialize(vecBytes, HeaderTCM::mData[0].nGBTWords, destBytes); + const auto statusBits = HeaderTCM::mStatusBits | DataTCM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderTCM::mData[0].nGBTWords = DataTCM::MaxNwords + 1; + dataBlockTmp.HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); + } else { + HeaderTCM::serialize(vecBytes, HeaderTCM::MaxNwords, destBytes); + } + DataTCM::serialize(vecBytes, nGBTWords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { - // TODO, Descriptor checking + if (HeaderTCM::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } + if (DataTCM::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); + flag = false; + return; + } } }; @@ -135,20 +164,34 @@ class DataBlockTCMext : public DataBlockBase serialize() const { std::size_t nBytes = HeaderTCMext::MaxNwords * HeaderTCMext::sSizeWord; - nBytes += HeaderTCMext::mData[0].nGBTWords * HeaderTCMext::sSizeWord; + const std::size_t nGBTWords = HeaderTCMext::mData[0].nGBTWords; + nBytes += nGBTWords * HeaderTCMext::sSizeWord; std::vector vecBytes(nBytes); std::size_t destBytes = 0; - HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + const auto statusBits = HeaderTCMext::mStatusBits | DataTCM::mStatusBits; + if (RawDataMetric::isBitActive(statusBits, RawDataMetric::EStatusBits::kIncompletePayload)) { + // Emulation for IncompletePayload + auto dataBlockTmp = (*this); + dataBlockTmp.HeaderTCMext::mData[0].nGBTWords = DataTCM::MaxNwords + 1; + dataBlockTmp.HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + } else { + HeaderTCMext::serialize(vecBytes, HeaderTCMext::MaxNwords, destBytes); + } DataTCM::serialize(vecBytes, DataTCM::MaxNwords, destBytes); - DataTCMext::serialize(vecBytes, HeaderTCMext::mData[0].nGBTWords - DataTCM::MaxNwords, destBytes); + DataTCMext::serialize(vecBytes, nGBTWords - DataTCM::MaxNwords, destBytes); return vecBytes; } // Custom sanity checking for current deserialized block // put here code for raw data checking - void sanityCheck(bool& flag) + void sanityCheck(bool& flag, typename RawDataMetric::Status_t& metric) { - - // TODO, Descriptor checking + if (HeaderTCMext::mData[0].isBadDescriptor()) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kWrongDescriptor); + } + if (DataTCMext::mNelements == 0) { + RawDataMetric::setStatusBit(metric, RawDataMetric::EStatusBits::kEmptyDataBlock); + flag = false; + } } }; diff --git a/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h b/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h index 3f9419b1dfb04..3afd0e6f70976 100644 --- a/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/DigitBlockFIT.h @@ -24,6 +24,7 @@ #include "FITRaw/DigitBlockBase.h" #include +#include #include "TTree.h" @@ -102,8 +103,8 @@ auto ConvertTCMData2Digit(DigitType& digit, const TCMDataType& tcmData) // PM to ChannelData convertation // FT0 and FV0 -template -auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, int linkID, int ep) -> std::enable_if_t().QTCAmpl), int16_t>::value> +template +auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t().QTCAmpl), int16_t>::value> { bool isValid{}; const auto globalChID = LookupTableType::Instance().getChannel(linkID, ep, pmData.channelID, isValid); @@ -111,20 +112,22 @@ auto ConvertEventData2ChData(std::vector& vecChData, const PMDa vecChData.emplace_back(static_cast(globalChID), static_cast(pmData.time), static_cast(pmData.charge), static_cast(pmData.pmBits)); } else { static int warningCount = 0; + metric.addStatusBit(RawDataMetricType::EStatusBits::kWrongChannelMapping); if (warningCount++ < 100) { LOG(warning) << "Incorrect global channel! linkID: " << linkID << " | EndPoint: " << ep << " | LocalChID: " << static_cast(pmData.channelID); } } } // FDD -template -auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, int linkID, int ep) -> std::enable_if_t().mChargeADC), int16_t>::value> +template +auto ConvertEventData2ChData(std::vector& vecChData, const PMDataType& pmData, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t().mChargeADC), int16_t>::value> { bool isValid{}; const auto globalChID = LookupTableType::Instance().getChannel(linkID, ep, pmData.channelID, isValid); if (isValid) { vecChData.emplace_back(static_cast(globalChID), static_cast(pmData.time), static_cast(pmData.charge), static_cast(pmData.pmBits)); } else { + metric.addStatusBit(RawDataMetricType::EStatusBits::kWrongChannelMapping); static int warningCount = 0; if (warningCount++ < 100) { LOG(warning) << "Incorrect global channel! linkID: " << linkID << " | EndPoint: " << ep << " | LocalChID: " << static_cast(pmData.channelID); @@ -155,18 +158,18 @@ class DigitBlockFIT : public DigitBlockBase DigitBlockFIT(const DigitBlockFIT& other) = default; ~DigitBlockFIT() = default; // Filling data from PM - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { const int nElements = dataBlock.DataPM::mNelements; for (int iEventData = 0; iEventData < nElements; iEventData++) { const auto& pmData = dataBlock.DataPM::mData[iEventData]; - DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, linkID, ep); + DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, metric, linkID, ep); } } // Filling data from TCM (normal mode) - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { auto& tcmData = dataBlock.DataTCM::mData[0]; @@ -175,7 +178,7 @@ class DigitBlockFIT : public DigitBlockBase // Decompose digits into DataBlocks // DataBlockPM template - auto decomposeDigits() const -> std::enable_if_t::value, std::map> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::map> { using Topo_t = typename LookupTable_t::Topo_t; @@ -195,32 +198,62 @@ class DigitBlockFIT : public DigitBlockBase DataBlockType& refDataBlock = pairInserted.first->second; if (pairInserted.second) { // Header preparation + refDataBlock.HeaderPM::mStatusBits |= status; refDataBlock.HeaderPM::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - refDataBlock.HeaderPM::mData[0].startDescriptor = 0xf; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + refDataBlock.HeaderPM::mData[0].startDescriptor = 0x0; + } else { + refDataBlock.HeaderPM::mData[0].startDescriptor = 0xf; + } std::size_t nElements = mapTopoCounter.find(pairInserted.first->first)->second; std::size_t nWords = nElements / 2 + nElements % 2; - refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + refDataBlock.HeaderPM::mData[0].nGBTWords = 0; + } else { + refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + } refDataBlock.HeaderPM::mNelements = 1; } // Data preparation + refDataBlock.DataPM::mStatusBits |= status; auto& refPos = refDataBlock.DataPM::mNelements; auto& refData = refDataBlock.DataPM::mData[refPos]; refPos++; - DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, LookupTable_t::Instance().getLocalChannelID(entry.first)); + int localChannelID = LookupTable_t::Instance().getLocalChannelID(entry.first); + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongChannelMapping)) { + // Emulation for WrongChannelMapping + localChannelID = 0xf; + } + DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, localChannelID); } return mapResult; } // DataBlockTCM template - auto decomposeDigits() const -> std::enable_if_t::value, std::pair> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::pair> { DataBlockType dataBlockTCM{}; // Header preparation + dataBlockTCM.HeaderTCM::mStatusBits |= status; dataBlockTCM.HeaderTCM::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0xf; - dataBlockTCM.HeaderTCM::mData[0].nGBTWords = dataBlockTCM.DataTCM::MaxNwords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0x0; + } else { + dataBlockTCM.HeaderTCM::mData[0].startDescriptor = 0xf; + } + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + dataBlockTCM.HeaderTCM::mData[0].nGBTWords = 0; + } else { + dataBlockTCM.HeaderTCM::mData[0].nGBTWords = 1; + } + dataBlockTCM.HeaderTCM::mNelements = 1; auto& refTCMdata = dataBlockTCM.DataTCM::mData[0]; + dataBlockTCM.DataTCM::mStatusBits |= status; dataBlockTCM.DataTCM::mNelements = 1; // Data preparation DigitBlockFIThelper::ConvertDigit2TCMData(DigitBlockBase_t::mDigit, refTCMdata); @@ -240,7 +273,7 @@ class DigitBlockFIT : public DigitBlockBase inputTree->SetBranchAddress(decltype(vecChannelData)::value_type::sDigitBranchName, &ptrVecChannelData); for (int iEntry = 0; iEntry < inputTree->GetEntries(); iEntry++) { inputTree->GetEntry(iEntry); - LOG(info) << "Processing TF " << iEntry; + LOG(detail) << "Processing TF " << iEntry; digitBlockProc.processDigitBlockPerTF(DigitBlockBase_t::template makeDigitBlock(vecDigit, vecChannelData)); } } @@ -289,18 +322,18 @@ class DigitBlockFIText : public DigitBlockBase - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { const int nElements = dataBlock.DataPM::mNelements; for (int iEventData = 0; iEventData < nElements; iEventData++) { const auto& pmData = dataBlock.DataPM::mData[iEventData]; - DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, linkID, ep); + DigitBlockFIThelper::ConvertEventData2ChData(DigitBlockBase_t::mSubDigit, pmData, metric, linkID, ep); } } // Filling data from TCM (extended mode) - template - auto processDigits(const DataBlockType& dataBlock, int linkID, int ep) -> std::enable_if_t::value> + template + auto processDigits(const DataBlockType& dataBlock, RawDataMetricType& metric, int linkID, int ep) -> std::enable_if_t::value> { auto& tcmData = dataBlock.DataTCM::mData[0]; DigitBlockFIThelper::ConvertTCMData2Digit(DigitBlockBase_t::mDigit, tcmData); @@ -313,7 +346,7 @@ class DigitBlockFIText : public DigitBlockBase - auto decomposeDigits() const -> std::enable_if_t::value, std::map> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::map> { using Topo_t = typename LookupTable_t::Topo_t; std::map mapResult; @@ -333,29 +366,54 @@ class DigitBlockFIText : public DigitBlockBasefirst)->second; std::size_t nWords = nElements / 2 + nElements % 2; - refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + refDataBlock.HeaderPM::mData[0].nGBTWords = 0; + } else { + refDataBlock.HeaderPM::mData[0].nGBTWords = nWords; + } refDataBlock.HeaderPM::mNelements = 1; } // Data preparation auto& refPos = refDataBlock.DataPM::mNelements; auto& refData = refDataBlock.DataPM::mData[refPos]; refPos++; - DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, LookupTable_t::Instance().getLocalChannelID(entry.first)); + int localChannelID = LookupTable_t::Instance().getLocalChannelID(entry.first); + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongChannelMapping)) { + // Emulation for WrongChannelMapping + localChannelID = 0xf; + } + DigitBlockFIThelper::ConvertChData2EventData(entry.second.get(), refData, localChannelID); } return mapResult; } // DataBlockTCM template - auto decomposeDigits() const -> std::enable_if_t::value, std::pair> + auto decomposeDigits(const typename RawDataMetric::Status_t& status) const -> std::enable_if_t::value, std::pair> { DataBlockType dataBlockTCM{}; // Header preparation dataBlockTCM.HeaderTCMext::mData[0].setIntRec(DigitBlockFIThelper::GetIntRecord(DigitBlockBase_t::mDigit)); - dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0xf; - dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = dataBlockTCM.DataTCM::MaxNwords + dataBlockTCM.DataTCMext::MaxNwords; + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kWrongDescriptor)) { + // Emulation for WrongDescriptor + dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0x0; + } else { + dataBlockTCM.HeaderTCMext::mData[0].startDescriptor = 0xf; + } + if (RawDataMetric::isBitActive(status, RawDataMetric::EStatusBits::kEmptyDataBlock)) { + // Emulation for EmptyDataBlock + dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = 0; + } else { + dataBlockTCM.HeaderTCMext::mData[0].nGBTWords = 1; + } dataBlockTCM.HeaderTCMext::mNelements = 1; auto& refTCMdata = dataBlockTCM.DataTCM::mData[0]; // Data preparation diff --git a/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h index 5edf7ceec4757..4c281c5559ee1 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h +++ b/Detectors/FIT/raw/include/FITRaw/RawReaderBase.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,7 @@ #include "Headers/RAWDataHeader.h" #include #include +#include #include namespace o2 @@ -47,9 +49,13 @@ class RawReaderBase RawReaderBase() = default; ~RawReaderBase() = default; typedef DigitBlockType DigitBlock_t; + typedef RawDataMetric RawDataMetric_t; + using LookupTable_t = typename DigitBlock_t::LookupTable_t; + using EntryCRU_t = typename LookupTable_t::EntryCRU_t; + using HashTableCRU_t = std::unordered_map; + + HashTableCRU_t mHashTableMetrics{}; typedef boost::mpl::vector VecDataBlocks_t; - // typedef boost::mpl::vector VecDataBlocks_t; - // std::tuple...> mTupleVecDataBlocks; std::tuple..., std::vector...> mTupleVecDataBlocks; std::map mMapDigits; @@ -61,27 +67,19 @@ class RawReaderBase } // decoding binary data into data blocks template - size_t decodeBlocks(const gsl::span binaryPayload, std::vector& vecDataBlocks) + size_t decodeBlocks(const gsl::span binaryPayload, RawDataMetric& metric, std::vector& vecDataBlocks) { size_t srcPos = 0; - const auto payloadSize = binaryPayload.size(); - const int padding = payloadSize % DataBlockType::DataBlockWrapperHeader_t::sSizeWord; - const int nCruWords = payloadSize / SIZE_WORD; - const int pageSizeThreshold = SIZE_WORD * (nCruWords - int(padding > 0)); + const auto& payloadSize = binaryPayload.size(); + const int nWords = payloadSize / DataBlockType::DataBlockWrapperHeader_t::sSizeWord; + const int pageSizeThreshold = DataBlockType::DataBlockWrapperHeader_t::sSizeWord * (nWords - int(nWords > 1)); // no need in reading last GBT word, this will be 0xff... or empty header while (srcPos < pageSizeThreshold) { auto& refDataBlock = vecDataBlocks.emplace_back(); refDataBlock.decodeBlock(binaryPayload, srcPos); srcPos += refDataBlock.mSize; - if (refDataBlock.isOnlyHeader()) { + if (metric.checkBadDataBlock(refDataBlock.mStatusBitsAll)) { // exclude data block in case of single header(no data, total size == 16 bytes) vecDataBlocks.pop_back(); - continue; - } - if (!refDataBlock.isCorrect()) { - LOG(warning) << "INCORRECT DATA BLOCK! Byte position: " << srcPos - refDataBlock.mSize << " | Payload size: " << binaryPayload.size() << " | DataBlock size: " << refDataBlock.mSize; - refDataBlock.print(); - vecDataBlocks.pop_back(); - return srcPos; } } return srcPos; @@ -89,32 +87,24 @@ class RawReaderBase // processing data blocks into digits template - void processBinaryData(gsl::span payload, T&&... feeParameters) + void processBinaryData(gsl::span payload, uint16_t feeID, uint8_t linkID, uint8_t epID) { auto& vecDataBlocks = getVecDataBlocks(); - auto srcPos = decodeBlocks(payload, vecDataBlocks); + auto& metric = addMetric(feeID, linkID, epID); + auto srcPos = decodeBlocks(payload, metric, vecDataBlocks); for (const auto& dataBlock : vecDataBlocks) { auto intRec = dataBlock.getInteractionRecord(); auto [digitIter, isNew] = mMapDigits.try_emplace(intRec, intRec); - digitIter->second.template processDigits(dataBlock, std::forward(feeParameters)...); + digitIter->second.template processDigits(dataBlock, metric, static_cast(linkID), static_cast(epID)); + metric.addStatusBit(RawDataMetric::EStatusBits::kDecodedDataBlock); } vecDataBlocks.clear(); - /* //Must be checked for perfomance - std::size_t srcPos = 0; - while (srcPos < payload.size()) { - DataBlockType dataBlock{}; - dataBlock.decodeBlock(payload, srcPos); - srcPos += dataBlock.mSize; - if (!dataBlock.isCorrect()) { - LOG(warning) << "INCORRECT DATA BLOCK! Byte position: " << srcPos << " | Payload size: " << payload.size() << " | DataBlock size: " << dataBlock.mSize; - //dataBlock.print(); - return; - } - auto intRec = dataBlock.getInteractionRecord(); - auto [digitIter, isNew] = mMapDigits.try_emplace(intRec, intRec); - digitIter->second.template processDigits(dataBlock, std::forward(feeParameters)...); - } - */ + } + RawDataMetric& addMetric(uint16_t feeID, uint8_t linkID, uint8_t epID, bool isRegisteredFEE = true) + { + auto metricPair = mHashTableMetrics.try_emplace(EntryCRU_t{static_cast(linkID), static_cast(epID)}, linkID, epID, feeID, isRegisteredFEE); + auto& metric = metricPair.first->second; + return metric; } // pop digits template @@ -127,6 +117,13 @@ class RawReaderBase mMapDigits.clear(); return digitCounter; } + void getMetrics(std::vector& vecMetrics) + { + for (const auto& en : mHashTableMetrics) { + vecMetrics.push_back(en.second); + } + mHashTableMetrics.clear(); + } private: // Check for unique DataBlock classes diff --git a/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h b/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h index 09b43cca24607..7a5c98aa1834c 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/RawReaderBaseFIT.h @@ -25,6 +25,8 @@ #include "FITRaw/RawReaderBase.h" #include #include "Headers/RAWDataHeader.h" +#include +#include #include @@ -64,22 +66,27 @@ class RawReaderBaseFIT : public RawReaderBase - void process(bool isPadded, gsl::span payload, T&&... feeParameters) + void process(bool isPadded, gsl::span payload, uint16_t feeID, T&&... feeParameters) { if (LookupTable_t::Instance().isTCM(std::forward(feeParameters)...)) { // TCM data proccessing if (isPadded) { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } else { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } - } else { + } else if (LookupTable_t::Instance().isPM(std::forward(feeParameters)...)) { // PM data proccessing if (isPadded) { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } else { - RawReaderBase_t::template processBinaryData(payload, std::forward(feeParameters)...); + RawReaderBase_t::template processBinaryData(payload, feeID, std::forward(feeParameters)...); } + } else { + auto& metric = RawReaderBase_t::addMetric(feeID, std::forward(feeParameters)..., false); + metric.addStatusBit(RawDataMetric::EStatusBits::kWrongChannelMapping); + LOG(error) << "Unregistered in ChannelMap link!"; + metric.print(); } } }; diff --git a/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h b/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h index c06cb95d2509e..cad08318dc7f7 100644 --- a/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h +++ b/Detectors/FIT/raw/include/FITRaw/RawWriterFIT.h @@ -18,16 +18,24 @@ #define ALICEO2_FIT_RAWREADERBASEFIT_H_ #include #include +#include #include +#include +#include +#include +#include + #include "FITRaw/DataBlockFIT.h" #include "FITRaw/DigitBlockFIT.h" +#include #include #include "Headers/RAWDataHeader.h" #include "DetectorsRaw/HBFUtils.h" #include "DetectorsRaw/RawFileWriter.h" #include "CommonUtils/StringUtils.h" -#include -#include +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsParameters/GRPObject.h" namespace o2 { @@ -43,6 +51,7 @@ class RawWriterFIT typedef typename LookupTable_t::Topo_t Topo_t; typedef DataBlockPMtype DataBlockPM_t; typedef DataBlockTCMtype DataBlockTCM_t; + typedef typename RawDataMetric::Status_t MetricStatus_t; using RDHUtils = o2::raw::RDHUtils; RawWriterFIT() = default; ~RawWriterFIT() = default; @@ -50,6 +59,9 @@ class RawWriterFIT void setFileFor(const std::string& fileFor) { mFileFor = fileFor; } void setFlpName(const std::string& flpName) { mFlpName = flpName; } bool getFilePerLink() const { return mOutputPerLink; } + void setStatusEmu(MetricStatus_t statusEmu) { mStatusEmu = statusEmu; } + void setRandomEmu(bool isRandomStatusEmu) { mIsRandomStatusEmu = isRandomStatusEmu; } + void setVerbosity(int verbosityLevel) { mVerbosity = verbosityLevel; } void setCCDBurl(const std::string& ccdbPath) { LookupTable_t::setCCDBurl(ccdbPath); } void setLUTpath(const std::string& lutPath) { LookupTable_t::setLUTpath(lutPath); } @@ -63,6 +75,13 @@ class RawWriterFIT void convertDigitsToRaw(const std::string& outputDir, const std::string& filenameDigits, long timestamp = -1) { LOG(info) << "Converting Digits to Raw data..."; + if (mStatusEmu > 0 || mIsRandomStatusEmu) { + LOG(warning) << "Raw data metric emulation applied: isRandom " << mIsRandomStatusEmu << "; metricStatus: " << static_cast(mStatusEmu); + if (mStatusEmu == 0) { + LOG(warning) << "Metric status is not specified, all bits will be randomly generated"; + mStatusEmu = RawDataMetric::getAllBitsActivated(); + } + } mWriter.setCarryOverCallBack(this); LookupTable_t::Instance(nullptr, timestamp).printFullMap(); // Preparing topo2FEEmetadata map @@ -106,15 +125,19 @@ class RawWriterFIT void processDigitBlockPerTF(const std::vector& vecDigitBlock) // Is used in DigitBlockFIT_t::processDigitBlocks for each TF (TTree entry) { for (const auto& digitBlock : vecDigitBlock) { + MetricStatus_t statusEmu{mStatusEmu}; // Processing PM data - auto mapDataBlockPM = digitBlock.template decomposeDigits(); + if (mIsRandomStatusEmu) { + statusEmu = static_cast(std::rand()) & mStatusEmu; + } + const auto mapDataBlockPM = digitBlock.template decomposeDigits(statusEmu); if (mVerbosity > 0) { digitBlock.print(); } for (const auto& dataBlockPair : mapDataBlockPM) { const auto& topo = dataBlockPair.first; const auto& dataBlock = dataBlockPair.second; - const auto itRdh = mMapTopo2FEEmetadata.find(topo); + const auto& itRdh = mMapTopo2FEEmetadata.find(topo); if (itRdh == mMapTopo2FEEmetadata.end()) { LOG(warning) << "No CRU entry in map! Data block: "; dataBlock.print(); @@ -128,10 +151,13 @@ class RawWriterFIT mWriter.addData(RDHUtils::getFEEID(rdh), RDHUtils::getCRUID(rdh), RDHUtils::getLinkID(rdh), RDHUtils::getEndPointID(rdh), dataBlock.getInteractionRecord(), data); } // Processing TCM data - const auto dataBlockPair = digitBlock.template decomposeDigits(); + if (mIsRandomStatusEmu) { + statusEmu = static_cast(std::rand()) & mStatusEmu; + } + const auto dataBlockPair = digitBlock.template decomposeDigits(statusEmu); const auto& topo = dataBlockPair.first; const auto& dataBlock = dataBlockPair.second; - const auto itRdh = mMapTopo2FEEmetadata.find(topo); + const auto& itRdh = mMapTopo2FEEmetadata.find(topo); if (itRdh == mMapTopo2FEEmetadata.end()) { LOG(warning) << "No CRU entry in map! Data block: "; dataBlock.print(); @@ -145,16 +171,189 @@ class RawWriterFIT mWriter.addData(RDHUtils::getFEEID(rdh), RDHUtils::getCRUID(rdh), RDHUtils::getLinkID(rdh), RDHUtils::getEndPointID(rdh), dataBlock.getInteractionRecord(), data); } } - - o2::raw::RawFileWriter mWriter{LookupTable_t::sDetectorName}; + static constexpr auto getDetID() { return LookupTable_t::sDetID; }; + o2::raw::RawFileWriter mWriter{getDetID().getDataOrigin()}; std::string mFlpName{}; std::string mFileFor{}; - int mDataFormat{0}; // RDH::dataFormat field, 0 - padded, 2 - no padding + MetricStatus_t mStatusEmu{}; // for metrics emulation, contains bits RawDataMetric::EStatusBits + bool mIsRandomStatusEmu{false}; // to use status bits randomly + int mDataFormat{0}; // RDH::dataFormat field, 0 - padded, 2 - no padding std::map mMapTopo2FEEmetadata; // const o2::raw::HBFUtils& mSampler = o2::raw::HBFUtils::Instance(); bool mOutputPerLink = false; int mVerbosity = 0; }; + +/* + * Special configurator for digit2raw executables + */ +namespace bpo = boost::program_options; +struct DigitToRawConfig { + using VariablesMap = typename boost::program_options::variables_map; + using OptionsDescription = typename boost::program_options::options_description; + using MetricStatus_t = typename RawDataMetric::Status_t; + DigitToRawConfig(const VariablesMap& vm) + { + setFromExecOptions(vm); + } + DigitToRawConfig() = default; // careful, data format should be then checked via adjustDataFormat() + void setFromExecOptions(const VariablesMap& vm) + { + mVerbosity = vm["verbosity"].as(); + mInputFile = vm["input-file"].as(); + mOutputDir = vm["output-dir"].as(); + mFileFor = vm["file-for"].as(); + mFlpName = vm["flp-name"].as(); + mConfigKeyValues = vm["configKeyValues"].as(); + + mRdhVersion = vm["rdh-version"].as(); + mNoEmptyHBF = vm["no-empty-hbf"].as(); + mConfDig = vm["hbfutils-config"].as(); + mEnablePadding = vm["enable-padding"].as(); + mCcdbPath = vm["ccdb-path"].as(); + mChannelMapPath = vm["lut-path"].as(); + + mMetricStatusEmu = static_cast(vm["emu-rawdata-metrics"].as()); + mIsRandomMetric = vm["emu-rawdata-metrics-random"].as(); + + adjustDataFormat(); + } + static void configureExecOptions(OptionsDescription& opt_general, const std::string& defaultInputFilename, const std::string& defaultFLP_name) + { + auto add_option = opt_general.add_options(); + // common configs + add_option("verbosity,v", bpo::value()->default_value(0), "verbosity level"); + add_option("input-file,i", bpo::value()->default_value(defaultInputFilename), "input digits file"); + add_option("output-dir,o", bpo::value()->default_value("./"), "output directory for raw data"); + add_option("flp-name", bpo::value()->default_value(defaultFLP_name), "single file per: all,flp,cru,link"); + add_option("file-for,f", bpo::value()->default_value("all"), "single file per: all,flp,cruendpoint,link"); + add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); + // raw data configs + uint32_t defRDH = o2::raw::RDHUtils::getVersion(); + add_option("rdh-version,r", bpo::value()->default_value(defRDH), "RDH version to use"); + add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); + add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); + add_option("ccdb-path", bpo::value()->default_value(""), "CCDB url which contains LookupTable"); + add_option("lut-path", bpo::value()->default_value(""), "LookupTable path"); + // raw data metric emulation mode + add_option("emu-rawdata-metrics", bpo::value()->default_value(0), "Raw data metric emulation, specify 1-byte status word with o2::fit::RawDataMetric::EStatusBits"); + add_option("emu-rawdata-metrics-random", bpo::value()->default_value(false)->implicit_value(true), "Raw data metric emulation, randomly specifies 1-byte status word with o2::fit::RawDataMetric::EStatusBits. If --emu-rawdata-metrics is not specified then all bits will be on and randomly will be taken"); + } + void adjustDataFormat() + { // adjust DataFormat, with or w/o padding + if (mRdhVersion < 7 && !mEnablePadding) { + mEnablePadding = true; + LOG(info) << "padding is always ON for RDH version " << mRdhVersion; + } + mDataFormat = mEnablePadding ? 0 : 2; + } + // common configs + int mVerbosity{0}; + std::string mInputFile{"detdigits.root"}; // put det name before "digits" + std::string mOutputDir{"./"}; + std::string mFlpName{"alio2-cr1-flpnode"}; // put node number after "flp" + std::string mFileFor{"all"}; + std::string mConfigKeyValues{""}; + // raw data configs + uint32_t mRdhVersion{o2::raw::RDHUtils::getVersion()}; + bool mNoEmptyHBF{false}; + std::string mConfDig{std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)}; + bool mEnablePadding{false}; // will be adjusted(if false) wrt to RDH version + std::string mCcdbPath{""}; + std::string mChannelMapPath{""}; + // raw data metric emulation + MetricStatus_t mMetricStatusEmu{}; // status bit for raw data metric emulation + bool mIsRandomMetric{false}; // use random bits for metric emulation + // misc field + uint32_t mDataFormat; +}; + +/* + * Wrapper for digit2raw executables + */ +template +struct DigitToRawDevice { + DigitToRawDevice() = default; + DigitToRawDevice(const DigitToRawConfig& config) { setConfig(config); } + RawWriterFIT_Type mRawWriterFIT{}; + DigitToRawConfig mConfig{}; + void setConfig(const DigitToRawConfig& config) + { + mConfig = config; + configure(mConfig); + } + void configure(const DigitToRawConfig& cfg) + { + // global config + if (!cfg.mConfDig.empty() && cfg.mConfDig != "none") { + o2::conf::ConfigurableParam::updateFromFile(cfg.mConfDig, "HBFUtils"); + } + o2::conf::ConfigurableParam::updateFromString(cfg.mConfigKeyValues); + // configuring FIT raw writer + mRawWriterFIT.setFileFor(cfg.mFileFor); + mRawWriterFIT.setFlpName(cfg.mFlpName); + mRawWriterFIT.setVerbosity(cfg.mVerbosity); + if (cfg.mCcdbPath != "") { + mRawWriterFIT.setCCDBurl(cfg.mCcdbPath); + } + if (cfg.mChannelMapPath != "") { + mRawWriterFIT.setLUTpath(cfg.mChannelMapPath); + } + // raw data metric emulation + mRawWriterFIT.setRandomEmu(cfg.mIsRandomMetric); + mRawWriterFIT.setStatusEmu(cfg.mMetricStatusEmu); + // configuring common raw writer + auto& rawWriter = mRawWriterFIT.getWriter(); + std::string inputGRP = o2::base::NameConf::getGRPFileName(); + const auto grp = o2::parameters::GRPObject::loadFrom(inputGRP); + rawWriter.setContinuousReadout(grp->isDetContinuousReadOut(RawWriterFIT_Type::getDetID())); // must be set explicitly + const int superPageSizeInB = 1024 * 1024; + rawWriter.setSuperPageSize(superPageSizeInB); + rawWriter.useRDHVersion(cfg.mRdhVersion); + rawWriter.setDontFillEmptyHBF(cfg.mNoEmptyHBF); + rawWriter.useRDHDataFormat(cfg.mDataFormat); + if (!cfg.mEnablePadding) { // CRU page alignment padding is used only if no GBT word padding is used + rawWriter.setAlignmentSize(16); // change to constexpr static field from class? + rawWriter.setAlignmentPaddingFiller(0xff); + } + } + void run() + { + run(mConfig); + } + void run(const DigitToRawConfig& cfg) + { + // dst path preparations + o2::raw::assertOutputDirectory(cfg.mOutputDir); + std::string outDirName(cfg.mOutputDir); + if (outDirName.back() != '/') { + outDirName += '/'; + } + // start timestamp preparations + const auto& hbfu = o2::raw::HBFUtils::Instance(); + long startTime = hbfu.startTime > 0 ? hbfu.startTime : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + // convertation digit->raw + mRawWriterFIT.convertDigitsToRaw(outDirName, cfg.mInputFile, startTime); + // config with output info + auto& rawWriter = mRawWriterFIT.getWriter(); + rawWriter.writeConfFile(rawWriter.getOrigin().str, "RAWDATA", o2::utils::Str::concat_string(outDirName, rawWriter.getOrigin().str, "raw.cfg")); + } + + static void digit2raw(const DigitToRawConfig& cfg) + { + TStopwatch swTot; + swTot.Start(); + // configurating + DigitToRawDevice digitToRawDevice(cfg); + // running + digitToRawDevice.run(); + // + swTot.Stop(); + swTot.Print(); + } +}; + } // namespace fit } // namespace o2 diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h index 8147c4ded75d4..868c0f2aa717f 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITDataReaderDPLSpec.h @@ -51,10 +51,12 @@ class FITDataReaderDPLSpec : public Task ConcreteDataMatcher mMatcherChMapCCDB; // matcher for Channel map(LUT) from CCDB bool mIsSampledRawData; bool mUpdateCCDB{true}; + bool mDumpMetrics{false}; void init(InitContext& ic) final { auto ccdbUrl = ic.options().get("ccdb-path"); auto lutPath = ic.options().get("lut-path"); + mDumpMetrics = ic.options().get("dump-raw-data-metric"); if (!ic.options().get("disable-empty-tf-protection")) { mRawReader.enableEmptyTFprotection(); } @@ -123,10 +125,10 @@ class FITDataReaderDPLSpec : public Task const auto rdhDataFormat = o2::raw::RDHUtils::getDataFormat(rdhPtr); if (rdhDataFormat == 0) { // padded cntDF0++; - mRawReader.process(true, payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); + mRawReader.process(true, payload, o2::raw::RDHUtils::getFEEID(rdhPtr), o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); } else if (rdhDataFormat == 2) { // no padding cntDF2++; - mRawReader.process(false, payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); + mRawReader.process(false, payload, o2::raw::RDHUtils::getFEEID(rdhPtr), o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); } else { cntDF_unknown++; continue; // or break? @@ -140,6 +142,9 @@ class FITDataReaderDPLSpec : public Task mRawReader.accumulateDigits(); mRawReader.emptyTFprotection(); mRawReader.makeSnapshot(pc); + if (mDumpMetrics) { + mRawReader.dumpRawDataMetrics(); + } mRawReader.clear(); if ((cntDF0 > 0 && cntDF2 > 0) || cntDF_unknown > 0) { LOG(error) << "Strange RDH::dataFormat in TF. Number of pages: DF=0 - " << cntDF0 << " , DF=2 - " << cntDF2 << " , DF=unknown - " << cntDF_unknown; @@ -170,7 +175,7 @@ framework::DataProcessorSpec getFITDataReaderDPLSpec(const RawReaderType& rawRea inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "SUB_RAWDATA"}, Lifetime::Sporadic}); // in case if one need to use DataSampler askSTFDist = false; } else { - inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "RAWDATA"}, Lifetime::Optional}); + inputSpec.push_back({"STF", ConcreteDataTypeMatcher{rawReader.mDataOrigin, "RAWDATA"}, Lifetime::Timeframe}); } if (askSTFDist) { inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); @@ -194,6 +199,7 @@ framework::DataProcessorSpec getFITDataReaderDPLSpec(const RawReaderType& rawRea o2::framework::ConfigParamSpec{"reserve-vec-chdata", VariantType::Int, 0, {"Reserve memory for ChannelData vector, to DPL channel"}}, o2::framework::ConfigParamSpec{"reserve-vec-buffer", VariantType::Int, 0, {"Reserve memory for DataBlock vector, buffer for each page"}}, o2::framework::ConfigParamSpec{"reserve-map-dig", VariantType::Int, 0, {"Reserve memory for Digit map, mapping in RawReader"}}, + o2::framework::ConfigParamSpec{"dump-raw-data-metric", VariantType::Bool, false, {"Dump raw data metrics, for debugging"}}, o2::framework::ConfigParamSpec{"disable-empty-tf-protection", VariantType::Bool, false, {"Disable empty TF protection. In case of empty payload within TF, only dummy ChannelData object will be sent."}}}}; } diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h index f14256a8bc44d..603c123d337c0 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterSpec.h @@ -35,6 +35,11 @@ namespace ft0 class RecPoints; } +namespace fdd +{ +class RecPoint; +} + namespace fit { @@ -63,6 +68,17 @@ struct DataDescriptionFITCurrents { static constexpr header::DataOrigin getDataOrigin() { return header::gDataOriginFT0; } }; +template <> +struct DataDescriptionFITCurrents { + using DataTStruct = IFDDC; + std::string static getName() { return "fdd"; } + std::string static getCCDBPath() { return "FDD/Calib/IFDDC"; } + static constexpr header::DataDescription getDataDescriptionFITC() { return header::DataDescription{"IFDDC"}; } + static constexpr header::DataDescription getDataDescriptionFITTFId() { return header::DataDescription{"IFDDTFID"}; } + static constexpr header::DataDescription getDataDescriptionCCDB() { return header::DataDescription{"IFDDCCCDB"}; } + static constexpr header::DataOrigin getDataOrigin() { return header::gDataOriginFDD; } +}; + template class FITIntegrateClusters : public Task { @@ -105,7 +121,7 @@ class FITIntegrateClusters : public Task mBufferCurrents.mIAmplA[sliceInTF] += amplA; } - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v || std::is_same_v) { const float nChanC = static_cast(cluster.getTrigger().getNChanC()); const float amplC = static_cast(cluster.getTrigger().getAmplC()); if ((nChanC > mMinNChan) && (amplC > mMinAmpl)) { @@ -164,9 +180,9 @@ o2::framework::DataProcessorSpec getFITIntegrateClusterSpec(const bool disableWr inputs); std::vector outputs; - outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Sporadic); + outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Timeframe); if (!disableWriter) { - outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Sporadic); + outputs.emplace_back(FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h index c5d0afe798740..c53d3e9b6425c 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITIntegrateClusterWriterSpec.h @@ -38,8 +38,8 @@ DataProcessorSpec getFITIntegrateClusterWriterSpec() return MakeRootTreeWriterSpec(fmt::format("{}-currents-writer", FitType::getName()).data(), treeFile.data(), treeName.data(), - BranchDefinition::DataTStruct>{InputSpec{"ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0}, "IFITC", 1}, - BranchDefinition{InputSpec{"tfID", FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0}, "tfID", 1})(); + BranchDefinition::DataTStruct>{InputSpec{"ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Timeframe}, "IFITC", 1}, + BranchDefinition{InputSpec{"tfID", FitType::getDataOrigin(), FitType::getDataDescriptionFITTFId(), 0, Lifetime::Timeframe}, "tfID", 1})(); } } // end namespace fit diff --git a/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h b/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h index 4cc2151b55058..c70fd733cb871 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/FITMergeIntegrateClusterSpec.h @@ -158,7 +158,7 @@ o2::framework::DataProcessorSpec getFITMergeIntegrateClusterSpec() inputs.emplace_back("ifitc", FitType::getDataOrigin(), FitType::getDataDescriptionFITC(), 0, Lifetime::Sporadic); auto ccdbRequest = std::make_shared(true, // orbitResetTime - false, // GRPECS=true for nHBF per TF + true, // GRPECS=true for nHBF per TF false, // GRPLHCIF false, // GRPMagField false, // askMatLUT diff --git a/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h b/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h index 51670f33806e3..cfe41e4bb474f 100644 --- a/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h +++ b/Detectors/FIT/workflow/include/FITWorkflow/RawReaderFIT.h @@ -44,6 +44,7 @@ class RawReaderFIT : public RawReaderType ~RawReaderFIT() = default; typedef RawReaderType RawReader_t; typedef typename RawReader_t::DigitBlockFIT_t DigitBlockFIT_t; + typedef typename RawReader_t::RawDataMetric_t RawDataMetric_t; typedef typename DigitBlockFIT_t::LookupTable_t LookupTable_t; typedef typename DigitBlockFIT_t::Digit_t Digit_t; typedef typename DigitBlockFIT_t::SubDigit_t SubDigitTmp_t; @@ -61,10 +62,17 @@ class RawReaderFIT : public RawReaderType o2::header::DataOrigin mDataOrigin; std::vector mVecDigit; std::vector mVecTrgInput; + std::vector mVecRawDataMetric; SubDigit_t mVecSubDigit; // tuple of vectors SingleSubDigit_t mVecSingleSubDigit; // tuple of vectors bool mEnableEmptyTFprotection{false}; bool mDumpData; + void dumpRawDataMetrics() const + { + for (const auto& entry : mVecRawDataMetric) { + entry.print(); + } + } void reserveVecDPL(std::size_t nDigits, std::size_t nSubDigits) { mVecDigit.reserve(nDigits); @@ -97,6 +105,7 @@ class RawReaderFIT : public RawReaderType }, mVecSingleSubDigit); } + mVecRawDataMetric.clear(); } template auto callGetDigit(std::index_sequence, std::index_sequence) @@ -132,6 +141,7 @@ class RawReaderFIT : public RawReaderType if (mDumpData) { callPrint(IndexesSubDigit{}, IndexesSingleSubDigit{}); } + RawReader_t::getMetrics(mVecRawDataMetric); } void configureOutputSpec(std::vector& outputSpec) const { @@ -151,30 +161,32 @@ class RawReaderFIT : public RawReaderType if constexpr (sUseTrgInput) { outputSpec.emplace_back(mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe); } + outputSpec.emplace_back(mDataOrigin, "RawDataMetric", 0, o2::framework::Lifetime::Timeframe); } void makeSnapshot(o2::framework::ProcessingContext& pc) const { - pc.outputs().snapshot(o2::framework::Output{mDataOrigin, Digit_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}, mVecDigit); + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, Digit_t::sChannelNameDPL, 0}, mVecDigit); if constexpr (sSubDigitExists) { std::apply([&](const auto&... subDigit) { - ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0, o2::framework::Lifetime::Timeframe}, subDigit)), ...); + ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0}, subDigit)), ...); }, mVecSubDigit); } if constexpr (sSingleSubDigitExists) { std::apply([&](const auto&... singleSubDigit) { - ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0, o2::framework::Lifetime::Timeframe}, singleSubDigit)), ...); + ((pc.outputs().snapshot(o2::framework::Output{mDataOrigin, (std::decay::type::value_type::sChannelNameDPL), 0}, singleSubDigit)), ...); }, mVecSingleSubDigit); } if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}, mVecTrgInput); + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, DetTrigInput_t::sChannelNameDPL, 0}, mVecTrgInput); } + pc.outputs().snapshot(o2::framework::Output{mDataOrigin, "RawDataMetric", 0}, mVecRawDataMetric); } template auto& getRefVec(o2::framework::ProcessingContext& pc) { - auto& refVec = pc.outputs().make(o2::framework::Output{mDataOrigin, VecDigitType::value_type::sChannelNameDPL, 0, o2::framework::Lifetime::Timeframe}); + auto& refVec = pc.outputs().make(o2::framework::Output{mDataOrigin, VecDigitType::value_type::sChannelNameDPL, 0}); return refVec; } void enableEmptyTFprotection() @@ -192,4 +204,4 @@ class RawReaderFIT : public RawReaderType } // namespace fit } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/FOCAL/CMakeLists.txt b/Detectors/FOCAL/CMakeLists.txt index 34db833d6d9e9..d19010da9ac76 100644 --- a/Detectors/FOCAL/CMakeLists.txt +++ b/Detectors/FOCAL/CMakeLists.txt @@ -12,5 +12,6 @@ add_subdirectory(base) add_subdirectory(calib) add_subdirectory(calibration) +add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(workflow) \ No newline at end of file diff --git a/Detectors/FOCAL/base/CMakeLists.txt b/Detectors/FOCAL/base/CMakeLists.txt index 6b8bf8e418bd2..9bbf041f6d3c0 100644 --- a/Detectors/FOCAL/base/CMakeLists.txt +++ b/Detectors/FOCAL/base/CMakeLists.txt @@ -10,13 +10,19 @@ # or submit itself to any jurisdiction. o2_add_library(FOCALBase - SOURCES src/EventReader.cxx + SOURCES src/Composition.cxx + src/EventReader.cxx + src/Geometry.cxx + src/Hit.cxx src/TestbeamAnalysis.cxx - PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL ROOT::RIO ROOT::Tree ROOT::TreePlayer + PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL O2::SimulationDataFormat O2::CommonUtils ROOT::RIO ROOT::Tree ROOT::TreePlayer Microsoft.GSL::GSL) o2_target_root_dictionary( FOCALBase - HEADERS include/FOCALBase/EventReader.h + HEADERS include/FOCALBase/Composition.h + include/FOCALBase/EventReader.h + include/FOCALBase/Geometry.h + include/FOCALBase/Hit.h include/FOCALBase/TestbeamAnalysis.h ) \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Composition.h b/Detectors/FOCAL/base/include/FOCALBase/Composition.h new file mode 100644 index 0000000000000..7f8b14386a2b4 --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Composition.h @@ -0,0 +1,77 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_COMPOSITION_H_ +#define ALICEO2_FOCAL_COMPOSITION_H_ + +#include + +namespace o2 +{ + +namespace focal +{ + +class Composition +{ + public: + Composition() = default; + Composition(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz); + Composition(Composition* comp); + Composition(const Composition& comp) = default; + ~Composition() = default; + + void setCompositionParameters(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz) + { + mMaterial = material; + mLayer = layer; + mStack = stack; + mId = id; + mCenterX = cx; + mCenterY = cy; + mCenterZ = cz; + mSizeX = dx; + mSizeY = dy; + mSizeZ = dz; + }; + void setLayerNumber(int layer) { mLayer = layer; } + void setId(int id) { mId = id; } + void setCenterZ(float val) { mCenterZ = val; } + + std::string material() const { return mMaterial; } + int layer() const { return mLayer; } + int stack() const { return mStack; } + int id() const { return mId; } + float centerX() const { return mCenterX; } + float centerY() const { return mCenterY; } + float centerZ() const { return mCenterZ; } + float sizeX() const { return mSizeX; } + float sizeY() const { return mSizeY; } + float sizeZ() const { return mSizeZ; } + float getThickness(void) const { return mSizeZ; } + + private: + std::string mMaterial; + int mLayer = 0; + int mStack = 0; + int mId = 0; + float mCenterX = 0.0; + float mCenterY = 0.0; + float mCenterZ = 0.0; + float mSizeX = 0.0; + float mSizeY = 0.0; + float mSizeZ = 0.0; +}; +} // end namespace focal +} // end namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Geometry.h b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h new file mode 100644 index 0000000000000..3414d84b5298f --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h @@ -0,0 +1,253 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_GEOMETRY_H_ +#define ALICEO2_FOCAL_GEOMETRY_H_ + +#include +#include +#include +#include +#include + +#include "FOCALBase/Composition.h" + +namespace o2 +{ +namespace focal +{ + +class VirtualSegment +{ + + public: + VirtualSegment() + { + } + + VirtualSegment& operator=(const VirtualSegment& segment) + { + if (this != &segment) { + mMinLayer = segment.mMinLayer; + mMaxLayer = segment.mMaxLayer; + mPadSize = segment.mPadSize; + mRelativeSensitiveThickness = segment.mRelativeSensitiveThickness; + mIsPixel = segment.mIsPixel; + mPixelTreshold = segment.mPixelTreshold; + } + return *this; + } + + ~VirtualSegment() {} + + int mMinLayer = -1; + int mMaxLayer = -1; + float mPadSize = -1.0; + float mRelativeSensitiveThickness = -1.0; + int mIsPixel = 0; // 0: pad or strip; 1: pixel; 2: HCAL + float mPixelTreshold = -1.0; +}; // class VirtualSegment + +class Geometry +{ + public: + enum HCALDesgin { + Sandwich = 0, // Sandwich HCAL design + Spaghetti = 1, // Spaghetti design + Sheets = 2 // Sheets design + }; + + Geometry() = default; + Geometry(Geometry* geo); + Geometry(const Geometry& geo) = default; + Geometry& operator=(const Geometry& geo) = default; + ~Geometry() = default; + + static Geometry* getInstance(); + static Geometry* getInstance(const std::string name); + + void init(const std::string geoFile); + void init(); + void buildComposition(); + void setParameters(const std::string geoFile); + void setParameters(); + + // geometry helper functions + // TODO: return tuples instead of using references + std::tuple getGeoPadCenter(int tower, int layer, int stack, int row, int col) const; + std::tuple getGeoPadCenterLocal(int towerX, int towerY, int row, int col) const; + std::tuple getGeoPixCenterLocal(int row, int col) const; + std::tuple getGeoPixelCenter(int pixel_id, int tower, int layer, int stack, int row, int col) const; + std::tuple getGeoCompositionCenter(int tower, int layer, int stack) const; + std::tuple getPadPositionId2RowColStackLayer(int id) const; + int getPixelNumber(int vol0, int vol1, int vol2, double x, double y) const; + std::tuple getGeoTowerCenter(int tower, int segment = -1) const; + bool disabledTower(int tower); + std::tuple getVirtualInfo(double x, double y, double z) const; + std::tuple getXYZFromColRowSeg(int col, int row, int segment) const; + std::tuple getVirtualNColRow(int segment) const; + std::tuple getVirtualLayerSegment(float z) const; + std::tuple getVirtualSegmentFromLayer(int layer) const; + int getVirtualSegment(float z) const; + + // getters + int getNumberOfPads() const { return mGlobal_PAD_NX * mGlobal_PAD_NY; } + int getNumberOfPADsInX() const { return mGlobal_PAD_NX_Tower; } + int getNumberOfPADsInY() const { return mGlobal_PAD_NY_Tower; } + int getNumberOfPIXsInX() const { return mGlobal_PIX_NX_Tower; } + int getNumberOfPIXsInY() const { return mGlobal_PIX_NY_Tower; } + float getHCALTowerSize() const { return mGlobal_HCAL_Tower_Size; } + int getHCALTowersInX() const { return mGlobal_HCAL_Tower_NX; } + int getHCALTowersInY() const { return mGlobal_HCAL_Tower_NY; } + int getNumberOfSegments() const { return mNumberOfSegments; } // NOTE: These are not the virtual segments, but just total number of layers as read from the geometry file. Need to disambiguate + int getNumberOfPadLayers() const { return mNPadLayers; } + int getNumberOfPixelLayers() const { return mNPixelLayers; } + int getNumberOfHCalLayers() const { return mNHCalLayers; } + int getNumberOfLayers() const { return mNPadLayers + mNPixelLayers + mNHCalLayers; } + int getNumberOfLayerSeg() const { return mLayerSeg; } + + double getFOCALSizeX() const; + double getFOCALSizeY() const; + double getTowerSize() const { return mTowerSizeX; } + double getTowerSizeX() const; + double getTowerSizeY() const; + double getFOCALSizeZ() const; + double getECALSizeZ() const; + double getECALCenterZ() const; + double getHCALSizeZ() const; + double getHCALCenterZ() const; + double getFOCALSegmentZ(int seg) const; + double getFOCALZ0() const { return mGlobal_FOCAL_Z0; } + int getNumberOfTowersInX() const { return mGlobal_Tower_NX; } + int getNumberOfTowersInY() const { return mGlobal_Tower_NY; } + double getTowerGapSize() const { return mGlobal_TOWER_TOLX; } + double getTowerGapSizeX() const { return mGlobal_TOWER_TOLX; } + double getTowerGapSizeY() const { return mGlobal_TOWER_TOLY; } + double getGlobalPixelSize() const { return mGlobal_Pixel_Size; } + double getGlobalPixelWaferSizeX() const { return mGlobal_PIX_SizeX; } + double getGlobalPixelWaferSizeY() const { return mGlobal_PIX_SizeY; } + double getGlobalPixelSkin() const { return mGlobal_PIX_SKIN; } + double getGlobalPixelOffsetX() const { return mGlobal_PIX_OffsetX; } + double getGlobalPadSize() const { return mGlobal_Pad_Size; } + float getMiddleTowerOffset() const { return mGlobal_Middle_Tower_Offset; } + bool getInsertFrontPadLayers() const { return mInsertFrontPadLayers; } + bool getInsertHCalReadoutMaterial() const { return mInsertFrontHCalReadoutMaterial; } + float getHCALPitchSize() const { return mGlobal_HCAL_Pitch_Size; } + float getHCALBeamPipeHoleSize() const { return mGlobal_HCAL_BeamPipeHole_Size; } + + float getDetectorOpeningRight() const { return mGlobal_DetectorOpening_Right; } + float getDetectorOpeningLeft() const { return mGlobal_DetectorOpening_Left; } + + std::vector getFOCALMicroModule(int layer) const; + const Composition* getComposition(int layer, int stack) const; + std::string_view getTowerGapMaterial() const { return mGlobal_Gap_Material; } + + int getVirtualNSegments() const; + + float getVirtualPadSize(int segment) const; + float getVirtualRelativeSensitiveThickness(int segment) const; + float getVirtualPixelTreshold(int segment) const; + float getVirtualSegmentSizeZ(int segment) const; + float getVirtualSegmentZ(int segment) const; + bool getVirtualIsPixel(int segment) const; + bool getVirtualIsHCal(int segment) const; + int getVirtualNLayersInSegment(int segment) const; + int getVirtualMinLayerInSegment(int segment) const; + int getVirtualMaxLayerInSegment(int segment) const; + + void setUpLayerSegmentMap(); + void setUpTowerWaferSize(); + + HCALDesgin getHCALDesign() const { return mHCALDesign; } + + protected: + std::vector mGeometryComposition; + std::vector mFrontMatterCompositionBase; + std::vector mPadCompositionBase; + std::vector mPixelCompositionBase; + std::vector mHCalCompositionBase; + + // PAD setup + float mGlobal_Pad_Size = 0.0; // pad size + int mGlobal_PAD_NX = 0; // number of X pads in wafer + int mGlobal_PAD_NY = 0; // number of Y pads in wafer + int mGlobal_PAD_NX_Tower = 0; // number of X wafers in tower + int mGlobal_PAD_NY_Tower = 0; // number of Y wafers in tower + float mGlobal_PPTOL = 0.0; // tolerance between the wafers + float mGlobal_PAD_SKIN = 0.0; // dead area (guard ring) on the wafer + float mWaferSizeX = 0.0; // Wafer X size + float mWaferSizeY = 0.0; // Wafer Y size + + float mGlobal_DetectorOpening_Right = 0.0; // detector opening in X + float mGlobal_DetectorOpening_Left = 0.0; // detector opening in Y + + // PIX setup + float mGlobal_Pixel_Size = 0.0; // pixel size + float mGlobal_PIX_SizeX = 0.0; // sensor size X + float mGlobal_PIX_SizeY = 0.0; // sensor size Y + float mGlobal_PIX_OffsetX = 0.0; // offset for pixel layers in X + float mGlobal_PIX_OffsetY = 0.0; // offset for pixel layers in Y + float mGlobal_PIX_SKIN = 0.0; + int mGlobal_PIX_NX_Tower = 0; // number of sensors in X + int mGlobal_PIX_NY_Tower = 0; // number of sensors in Y + bool mGlobal_Pixel_Readout = false; // readout on + + // Tower setup + int mNPadLayers = 0; // total number of pad layers + int mNPixelLayers = 0; // number of pixel layers + std::array mPixelLayerLocations; // location of the pixel layers + int mGlobal_Tower_NX = 0; // How many towers in X + int mGlobal_Tower_NY = 0; // How many towers in Y + float mTowerSizeX = 0.0; // X size of tower + float mTowerSizeY = 0.0; // Y size of tower + float mGlobal_TOWER_TOLX = 0.0; // X - tolarance around tower + float mGlobal_TOWER_TOLY = 0.0; // Y - tolarance around tower + float mGlobal_Middle_Tower_Offset = 0.0; // if odd layers, the middle tower is offset due to the beampipe + std::string mGlobal_Gap_Material; // gap filling material NOTE: currently not used + + float mGlobal_HCAL_Tower_Size = 0.0; + int mGlobal_HCAL_Tower_NX = 0; // Number of HCAL towers on X + int mGlobal_HCAL_Tower_NY = 0; // Number of HCAL towers on Y + float mGlobal_HCAL_Pitch_Size = 0.0; // Distance between two fibers + float mGlobal_HCAL_BeamPipeHole_Size = 0.0; // beam pipe hole size in HCAL + HCALDesgin mHCALDesign = Sandwich; // HCAL design type + + float mGlobal_FOCAL_Z0 = 0.0; + + bool mInsertFrontPadLayers = false; // Have 2 pad layers in front of ECAL for charged particle veto + bool mInsertFrontHCalReadoutMaterial = false; // if true, insert an 1cm thick aluminium layer at 2cm behind HCal to simulate the material introduced by the readout + + int mLayerSeg = 0; + int mNHCalLayers = 0; // number of HCalLayers + std::array mSegments; // which layer belongs to which segment + std::array mNumberOfLayersInSegments; // nymber of layers in each segment + int mNumberOfSegments = 0; // number of long. segements + int mNFrontMatterCompositionBase = 0; + std::array mLocalLayerZ; //// layer location in z + std::array mLocalSegmentsZ; /// segment location in z + float mFrontMatterLayerThickness = 0.0; + float mPadLayerThickness = 0.0; + float mPixelLayerThickness = 0.0; + float mHCalLayerThickness = 0.0; + std::array mLayerThickness; // thickenss of the layers + std::list mDisableTowers; + + int mVirtualNSegments = 0; + std::vector mVirtualSegmentComposition; + + private: + static Geometry* sGeom; + static bool sInit; +}; // Geometry + +} // namespace focal +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/include/FOCALBase/Hit.h b/Detectors/FOCAL/base/include/FOCALBase/Hit.h new file mode 100644 index 0000000000000..e24c48ac60284 --- /dev/null +++ b/Detectors/FOCAL/base/include/FOCALBase/Hit.h @@ -0,0 +1,170 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_HIT_H +#define ALICEO2_FOCAL_HIT_H + +#include +#include "SimulationDataFormat/BaseHits.h" +#include "CommonUtils/ShmAllocator.h" +#include + +namespace o2::focal +{ + +/// \class Hit +/// \brief Common FOCAL hit class for the detector simulation +/// \ingroup FOCALbase +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since May 29, 2024 +class Hit : public o2::BasicXYZEHit +{ + public: + /// \enum Subsystem_t + /// \brief Subsystem index of the Hit + enum class Subsystem_t { + EPADS, ///< ECAL pads + EPIXELS, ///< ECAL pixels + HCAL, ///< HCAL + UNKNOWN ///< Undefined + }; + + /// \struct HitID + /// \brief Mapped information of a channel + struct HitID { + int mParentID; ///< parentID of the track creating this hit + uint8_t mRow; ///< Row of the hit in the calorimeter + uint8_t mColumn; ///< Column of the hit in the calorimeter + uint8_t mLayer; ///< Layer the was hit + + bool operator==(const HitID& other) const + { + return mParentID == other.mParentID && mRow == other.mRow && mColumn == other.mColumn && mLayer == other.mLayer; + } + friend std::ostream& operator<<(std::ostream& stream, const Hit::HitID& channel); + }; + + /// \struct HitIDHasher + /// \brief Hash functor for hit ID + struct HitIDHasher { + + /// \brief Functor implementation + /// \param s Hit for which to determine a hash value + /// \return hash value for channel ID + size_t operator()(const HitID& s) const + { + std::size_t seed = 0; + boost::hash_combine(seed, s.mParentID); + boost::hash_combine(seed, s.mRow); + boost::hash_combine(seed, s.mColumn); + boost::hash_combine(seed, s.mLayer); + return seed; + } + }; + + /// \brief Dummy constructor + Hit() = default; + + /// \brief Construction of the FOCAL hit with full information + /// \param primary Index of the incoming primary particle + /// \param trackID Index of the MC particle (also shower particle) responsible for the energy loss + /// \param detID Module index inside the subsystem + /// \param subsystem FOCAL Subdetector (E-Pads, E-Pixels, HCAL) + /// \param pos Geometric hit position (global coordintate system) + /// \param tof Time-of-flight of the particle to the FOCAL + /// \param eLoss Energy loss + Hit(int primary, int trackID, int detID, Subsystem_t subsystem, double initialEnergy, const math_utils::Point3D& pos, + double tof, double eLoss); + + /// \brief Destructor + ~Hit() = default; + + /// \brief Comparison operator for equalness + /// \param other Hit to compare to + /// \return True if subsytem, module and MC particle ID match, false otherwise + bool operator==(const Hit& other) const; + + /// \brief Comparison operator for smaller + /// \param other Hit to compare to + /// \return True if other hit is smaller (first track ID, then, subsystem ID, then module ID), false otherwise + bool operator<(const Hit& other) const; + + /// \brief Operator for incremental sum, adding energy loss of the other hit to this energy loss + /// \param other Hit to add to this one + /// \return This hit containing the sum of the two energy losses + Hit& operator+=(const Hit& other); + + /// \brief Get the type of the subsystem for which the hit was created + /// \return Subsystem type + Subsystem_t getSubsystem() const { return mSubSystem; } + + /// \brief Check if the hit is a FOCAL-E pixel hit + /// \return True if the hit is a FOCAL-E pixel hit, false otherwise + bool isPixelHit() const { return mSubSystem == Subsystem_t::EPIXELS; } + + /// \brief Check if the hit is a FOCAL-E pad hit + /// \return True if the hit is a FOCAL-E pad hit, false otherwise + bool isPadHit() const { return mSubSystem == Subsystem_t::EPADS; } + + /// \brief Check if the hit is a FOCAL-H hit + /// \return True if the hit is a FOCAL-H, false otherwise + bool isHCALHit() const { return mSubSystem == Subsystem_t::HCAL; } + + /// \brief Get index of the incomimg primary particle associated with the hit + /// \return Associated primary particle + int getPrimary() const { return mPrimary; } + + /// \brief Get energy of the incoming primary particle at the entrance of FOCAL + /// \return Initial energy + double getInitialEnergy() const { return mInitialEnergy; } + + /// \brief Set energy of the incoming primary particle at the entrance of FOCAL + /// \param energy Initial energy + void setInitialEnergy(double energy) { mInitialEnergy = energy; } + + /// \brief Set index of the incomimg primary particle associated with the hit + /// \param primary Associated primary particle + void setPrimary(int primary) { mPrimary = primary; } + + /// \brief Print information of this hit on the output stream + /// \param stream Stream to print on + void printStream(std::ostream& stream) const; + + private: + Subsystem_t mSubSystem = Subsystem_t::UNKNOWN; ///< FOCAL subdetector + int mPrimary = -1; ///< Primary particles at the origin of the hit + double mInitialEnergy = 0.; ///< Energy of the parent particle that entered the EMCAL + + ClassDefNV(Hit, 1); +}; + +/// @brief Sum operator, creating a new hit with the sum of the two energy losses +/// @param lhs Left-hand side of the sum +/// @param rhs Right-hand side of the sum +/// @return New hit with the properties of the lhs hit and the summed energy loss of the two hits +Hit operator+(const Hit& lhs, const Hit& rhs); + +/// \brief Output stream operator for FOCAL hits +/// \param stream Stream to write on +/// \param point Hit to be printed +/// \return Stream after printing +std::ostream& operator<<(std::ostream& stream, const Hit& point); +} // namespace o2::focal + +#ifdef USESHM +namespace std +{ +template <> +class allocator : public o2::utils::ShmAllocator +{ +}; +} // namespace std +#endif +#endif \ No newline at end of file diff --git a/Detectors/FOCAL/base/src/Composition.cxx b/Detectors/FOCAL/base/src/Composition.cxx new file mode 100644 index 0000000000000..aefbbee3cd6ca --- /dev/null +++ b/Detectors/FOCAL/base/src/Composition.cxx @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FOCALBase/Geometry.h" + +using namespace o2::focal; + +Composition::Composition(std::string material, int layer, int stack, int id, + float cx, float cy, float cz, float dx, float dy, float dz) : mMaterial(material), + mLayer(layer), + mStack(stack), + mId(id), + mCenterX(cx), + mCenterY(cy), + mCenterZ(cz), + mSizeX(dx), + mSizeY(dy), + mSizeZ(dz) +{ + // Default constructor +} + +Composition::Composition(Composition* comp) : mMaterial(nullptr), + mLayer(0), + mStack(0), + mId(0), + mCenterX(0), + mCenterY(0), + mCenterZ(0), + mSizeX(0), + mSizeY(0), + mSizeZ(0) +{ + *this = comp; +} \ No newline at end of file diff --git a/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h b/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h index cbb99ddb51abb..b39e551e8ed27 100644 --- a/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h +++ b/Detectors/FOCAL/base/src/FOCALBaseLinkDef.h @@ -17,4 +17,7 @@ #pragma link C++ class o2::focal::EventReader + ; #pragma link C++ class o2::focal::TestbeamAnalysis + ; +#pragma link C++ class o2::focal::Geometry + ; +#pragma link C++ class o2::focal::Composition + ; +#pragma link C++ class o2::focal::Hit + ; #endif diff --git a/Detectors/FOCAL/base/src/Geometry.cxx b/Detectors/FOCAL/base/src/Geometry.cxx new file mode 100644 index 0000000000000..2699ab5c7d602 --- /dev/null +++ b/Detectors/FOCAL/base/src/Geometry.cxx @@ -0,0 +1,1491 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// --- Standard library --- +#include +#include + +// --- ROOT system --- + +#include + +#include "FOCALBase/Geometry.h" + +using namespace o2::focal; + +bool Geometry::sInit = false; +Geometry* Geometry::sGeom = nullptr; + +//_________________________________________________________________________ +Geometry::Geometry(Geometry* geo) +{ + std::fill_n(mPixelLayerLocations.begin(), 20, -1); + std::fill_n(mSegments.begin(), 100, -100); + std::fill_n(mNumberOfLayersInSegments.begin(), 100, -1); + std::fill_n(mLocalLayerZ.begin(), 100, 0.0); + std::fill_n(mLocalSegmentsZ.begin(), 100, 0.0); + std::fill_n(mLayerThickness.begin(), 100, 0.0); + + *this = geo; +} + +//_________________________________________________________________________ +Geometry* Geometry::getInstance() +{ + + if (sGeom == nullptr) { + sGeom = new Geometry(); + sGeom->init(); + } else { + if (sInit == false) { + sGeom = new Geometry(); + sGeom->init(); + } + } + + return sGeom; +} + +//_________________________________________________________________________ +Geometry* Geometry::getInstance(std::string filename) +{ + + if (sGeom == nullptr) { + sGeom = new Geometry(); + sGeom->init(filename); + } else { + if (sInit == false) { + sGeom = new Geometry(); + sGeom->init(filename); + } + } + return sGeom; +} + +//_________________________________________________________________________ +void Geometry::init(std::string filename) +{ + if (filename != "default") { + setParameters(filename); + } else { + setParameters(); + } + + buildComposition(); + sInit = true; +} + +//_________________________________________________________________________ +void Geometry::init() +{ + setParameters(); + buildComposition(); + sInit = true; +} + +//_________________________________________________________________________ +void Geometry::buildComposition() +{ + mGeometryComposition.reserve(1000); + + int nlayers = mNPadLayers + mNPixelLayers + mNHCalLayers; + + ////// Si pad micro module for the first Pad layer + for (int i = 0; i < nlayers; i++) { + if (i < mNPadLayers + mNPixelLayers) { + // Check whether it is a pixel layer + int isPixel = 0; + for (int iPix = 0; iPix < mNPixelLayers && !isPixel; iPix++) { + LOG(debug) << "Check pixel layer idx " << iPix << " loc " << mPixelLayerLocations[iPix] << " i= " << i; + if (i == mPixelLayerLocations[iPix]) { + isPixel = 1; + } + } + + if (isPixel) { + // create pixel compositions + LOG(debug) << "Adding pixel layer at layer " << i; + for (auto& icomp : mPixelCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mPixelLayerThickness; + } else { + // create pad compositions + for (auto& icomp : mPadCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mPadLayerThickness; + } + } else { + // create hcal compositions + for (auto& icomp : mHCalCompositionBase) { + icomp.setLayerNumber(i); + mGeometryComposition.push_back(icomp); + } + mLayerThickness[i] = mHCalLayerThickness; + } + } // end loop over nlayers + + for (int i = 0; i < nlayers; i++) { + mLocalLayerZ[i] = 0; + for (int j = 0; j < i; j++) { + mLocalLayerZ[i] += mLayerThickness[j]; + } + } + for (int i = 0; i < nlayers; i++) { + mLocalSegmentsZ[mSegments[i]] += mLayerThickness[i]; + } + + mLocalLayerZ[nlayers] = -1; + + ////// add the front matter to the tower + for (auto& icomp : mFrontMatterCompositionBase) { + icomp.setLayerNumber(-1); + mGeometryComposition.push_back(icomp); + } + + //// re-iterate to set the longitudinal positions + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() >= 0) { + icomp.setCenterZ(mFrontMatterLayerThickness + mLocalLayerZ[icomp.layer()] + icomp.centerZ() + icomp.sizeZ() / 2); /// this is pad/strip layer + } else { + icomp.setCenterZ(icomp.centerZ() + icomp.sizeZ() / 2); /// this is frontmatter + } + } +} + +//_________________________________________________________________________ +void Geometry::setParameters() +{ + + LOG(warning) << " Default parameters are not used "; + /////// this is default setting for the global parameters + mGlobal_FOCAL_Z0 = 500; +} + +//_________________________________________________________________________ +void Geometry::setParameters(std::string geometryfile) +{ + /////// this is default setting for the global parameters + mGlobal_FOCAL_Z0 = 700.0; + mInsertFrontPadLayers = false; + // PAD setup + mGlobal_Pad_Size = 1.0; // pad size + mGlobal_PAD_NX = 9; // number of X pads in wafer + mGlobal_PAD_NY = 8; // number of Y pads in wafer + mGlobal_PAD_NX_Tower = 5; // number of X wafers in tower + mGlobal_PAD_NY_Tower = 1; // number of Y wafers in tower + mGlobal_PPTOL = 0.0; // tolerance between the wafers + mGlobal_PAD_SKIN = 0.2; // dead area (guard ring) on the wafer + mGlobal_PIX_SKIN = 0.004; + // PIX setup + mGlobal_Pixel_Readout = false; + mGlobal_Pixel_Size = 0.005; // pixel size + mGlobal_PIX_SizeX = 3.0; // sensor size X + mGlobal_PIX_SizeY = 2.74; // sensor size Y + mGlobal_PIX_OffsetX = 1.0; + mGlobal_PIX_OffsetY = 0.09; + mGlobal_PIX_NX_Tower = 15; // number of sensors in X + mGlobal_PIX_NY_Tower = 3; // number of sensors in Y + + mGlobal_Tower_NX = 2; + mGlobal_Tower_NY = 11; + + mNPixelLayers = 4; + mPixelLayerLocations[0] = 2; + mPixelLayerLocations[1] = 4; + mPixelLayerLocations[2] = 6; + mPixelLayerLocations[3] = 8; + + mTowerSizeX = 0; + mTowerSizeY = 0; + mWaferSizeX = 0; + mWaferSizeY = 0; + + std::ifstream fin(geometryfile); + if (fin.fail()) { + LOG(error) << "No geometry file for FoCAL. Use default ones"; + setParameters(); + return; + } else { + LOG(info) << "Using geometry file " << geometryfile; + } + + std::vector padCompDummy(10); + std::vector hCalCompDummy(10); + std::vector pixelCompDummy(10); + std::vector frontMatterCompDummy(10); + int nPad = 0; + int hHCal = 0; + int nPixel = 0; + int nFrontMatter = 0; + + int npadlayers = 0; + int npixlayers = 0; + int pixl[10]; + + std::string input; + + LOG(info) << "Loading FOCAL geometry file "; + while (getline(fin, input)) { + LOG(debug) << "Read string :: " << input.c_str(); + const char* p = std::strpbrk("#", input.c_str()); + if (p != nullptr) { + LOG(debug) << "Skipping comment"; + continue; + } + + std::vector tokens; + std::stringstream str(input); + std::string tmpStr; + while (getline(str, tmpStr, ' ')) { + if (tmpStr.empty()) { + continue; + } + tokens.push_back(tmpStr); + } + + std::string command = tokens[0]; + LOG(debug) << "command: " << command; + + if (command.find("COMPOSITION") != std::string::npos) /// definition of the composition + { + std::string material = tokens[1]; + float cx = std::stof(tokens[2]); + float cy = std::stof(tokens[3]); + float dx = std::stof(tokens[4]); + float dy = std::stof(tokens[5]); + float dz = std::stof(tokens[6]); + float cz = 0; + + LOG(debug) << "Material :: " << material; + LOG(debug) << "cx/cy/dx/dy/dz :: " << cx << " / " << cy << " / " << dx << " / " << dy << " / " << dz; + + int stack; + if (command.find("PAD") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_PAD_S%d", &stack); + padCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + nPad++; + } + + if (command.find("HCAL") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_HCAL_S%d", &stack); + hCalCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + hHCal++; + } + + if (command.find("PIX") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_PIX_S%d", &stack); + pixelCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + mGlobal_PIX_SizeX = dx; + mGlobal_PIX_SizeY = dy; + nPixel++; + } + + if (command.find("FM") != std::string::npos) { + sscanf(command.c_str(), "COMPOSITION_FM_S%d", &stack); + frontMatterCompDummy.emplace_back(material, stack, stack, 0, cx, cy, cz, dx, dy, dz); + nFrontMatter++; + } + } // end if COMPOSITION + + if (command.find("GLOBAL") != std::string::npos) { + + if (command.find("PAD_SIZE") != std::string::npos) { + mGlobal_Pad_Size = std::stof(tokens[1]); + LOG(debug) << "Pad sensor size is set to : " << mGlobal_Pad_Size; + } + + if (command.find("PAD_NX") != std::string::npos) { + mGlobal_PAD_NX = std::stoi(tokens[1]); + LOG(debug) << "No. sensors per pad wafer in X direction : " << mGlobal_PAD_NX; + } + + if (command.find("PAD_NY") != std::string::npos) { + mGlobal_PAD_NY = std::stoi(tokens[1]); + LOG(debug) << "No. sensors per pad wafer in Y direction : " << mGlobal_PAD_NY; + } + + if (command.find("PAD_SUPERMODULE_X") != std::string::npos) { + mGlobal_PAD_NX_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pad wafers per module in X direction : " << mGlobal_PAD_NX_Tower; + } + + if (command.find("PAD_SUPERMODULE_Y") != std::string::npos) { + mGlobal_PAD_NY_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pad wafers per module in Y direction : " << mGlobal_PAD_NY_Tower; + } + + if (command.find("PIX_NX") != std::string::npos) { + mGlobal_PIX_NX_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pixels sensors per module in X direction : " << mGlobal_PIX_NX_Tower; + } + + if (command.find("PIX_NY") != std::string::npos) { + mGlobal_PIX_NY_Tower = std::stoi(tokens[1]); + LOG(debug) << "Number of pixels sensors per module in Y direction : " << mGlobal_PIX_NY_Tower; + } + + if (command.find("PAD_PPTOL") != std::string::npos) { + mGlobal_PPTOL = std::stof(tokens[1]); + LOG(debug) << "Distance between pad sensors : " << mGlobal_PPTOL; + } + + if (command.find("PAD_SKIN") != std::string::npos) { + mGlobal_PAD_SKIN = std::stof(tokens[1]); + LOG(debug) << "Pad wafer skin : " << mGlobal_PAD_SKIN; + } + + if (command.find("FOCAL_Z") != std::string::npos) { + mGlobal_FOCAL_Z0 = std::stof(tokens[1]); + LOG(debug) << "Z-Location of the FoCAL is set to : " << mGlobal_FOCAL_Z0; + } + + if (command.find("DetectorOpen_Right") != std::string::npos) { + mGlobal_DetectorOpening_Right = std::stof(tokens[1]); + LOG(debug) << "Detector opening on the right : " << mGlobal_DetectorOpening_Right; + } + + if (command.find("DetectorOpen_Left") != std::string::npos) { + mGlobal_DetectorOpening_Left = std::stof(tokens[1]); + LOG(debug) << "Detector opening on the left : " << mGlobal_DetectorOpening_Left; + } + + if (command.find("HCAL_TOWER_SIZE") != std::string::npos) { + mGlobal_HCAL_Tower_Size = std::stof(tokens[1]); + LOG(debug) << "The size of the HCAL readout tower will be : " << mGlobal_HCAL_Tower_Size; + } + + if (command.find("HCAL_PITCH_SIZE") != std::string::npos) { + mGlobal_HCAL_Pitch_Size = std::stof(tokens[1]); + LOG(debug) << "The distance between fibers is : " << mGlobal_HCAL_Pitch_Size; + } + + if (command.find("HCAL_TOWER_NX") != std::string::npos) { + mGlobal_HCAL_Tower_NX = std::stoi(tokens[1]); + LOG(debug) << "The number of the HCAL readout towers in X will be : " << mGlobal_HCAL_Tower_NX; + } + + if (command.find("HCAL_TOWER_NY") != std::string::npos) { + mGlobal_HCAL_Tower_NY = std::stoi(tokens[1]); + LOG(debug) << "The number of the HCAL readout towers in Y will be : " << mGlobal_HCAL_Tower_NY; + } + + if (command.find("HCAL_BEAMPIPE") != std::string::npos) { + mGlobal_HCAL_BeamPipeHole_Size = std::stof(tokens[1]); + LOG(debug) << "The HCAL beam pipe openning : " << mGlobal_HCAL_BeamPipeHole_Size; + } + + if (command.find("PIX_OffsetX") != std::string::npos) { + mGlobal_PIX_OffsetX = std::stof(tokens[1]); + LOG(debug) << "Pixel offset from the beam pipe will be: " << mGlobal_PIX_OffsetX; + } + + if (command.find("PIX_OffsetY") != std::string::npos) { + mGlobal_PIX_OffsetY = std::stof(tokens[1]); + LOG(debug) << "Pixel offset from the top of module will be: " << mGlobal_PIX_OffsetY; + } + + if (command.find("PIX_SKIN") != std::string::npos) { + mGlobal_PIX_SKIN = std::stof(tokens[1]); + LOG(debug) << "Pixel sensor skin : " << mGlobal_PIX_SKIN; + } + + if (command.find("TOWER_TOLX") != std::string::npos) { + mGlobal_TOWER_TOLX = std::stof(tokens[1]); + mGlobal_Gap_Material = tokens[2]; + LOG(debug) << "Distance between modules in X direction : " << mGlobal_TOWER_TOLX << ", Material : " << mGlobal_Gap_Material; + } + + if (command.find("TOWER_TOLY") != std::string::npos) { + mGlobal_TOWER_TOLY = std::stof(tokens[1]); + mGlobal_Gap_Material = tokens[2]; + LOG(debug) << "Distance between modules in Y direction : " << mGlobal_TOWER_TOLY << " Material : " << mGlobal_Gap_Material; + } + + if (command.find("MIDDLE_TOWER_OFFSET") != std::string::npos) { + mGlobal_Middle_Tower_Offset = std::stof(tokens[1]); + LOG(debug) << "Middle tower offset will be: " << mGlobal_Middle_Tower_Offset; + } + + if (command.find("Tower_NX") != std::string::npos) { + mGlobal_Tower_NX = std::stof(tokens[1]); + LOG(debug) << "Number of FOCAL modules in x direction is set to : " << mGlobal_Tower_NX; + } + + if (command.find("Tower_NY") != std::string::npos) { + mGlobal_Tower_NY = std::stof(tokens[1]); + LOG(debug) << "Number of FOCAL modules in y direction is set to : " << mGlobal_Tower_NY; + } + } // end if GLOBAL + + if (command.find("COMMAND") != std::string::npos) { + + if (command.find("NUMBER_OF_PAD_LAYERS") != std::string::npos) { + npadlayers = std::stoi(tokens[1]); + LOG(debug) << "Number of pad layers " << npadlayers; + } + + if (command.find("NUMBER_OF_HCAL_LAYERS") != std::string::npos) { + mNHCalLayers = std::stoi(tokens[1]); + LOG(debug) << "Number of HCAL layers " << mNHCalLayers; + } + + if (command.find("NUMBER_OF_SEGMENTS") != std::string::npos) { + mNumberOfSegments = std::stoi(tokens[1]); + LOG(debug) << "Number of Segments " << mNumberOfSegments; + } + + if (command.find("INSERT_PIX") != std::string::npos) { + sscanf(command.c_str(), "COMMAND_INSERT_PIX_AT_L%d", &pixl[npixlayers]); + LOG(debug) << "Number of pixel layer " << npixlayers << " : location " << pixl[npixlayers]; + npixlayers++; + } + + if (command.find("COMMAND_PIXEL_READOUT_ON") != std::string::npos) { + mGlobal_Pixel_Readout = true; + mGlobal_Pixel_Size = std::stof(tokens[1]); + LOG(debug) << "Pixel readout on (for MASPS): pixel size is set to : " << mGlobal_Pixel_Size; + } + + if (command.find("COMMAND_INSERT_FRONT_PAD_LAYERS") != std::string::npos) { + mInsertFrontPadLayers = true; + LOG(debug) << "Insert two pad layers in front of ECAL for charged particle veto!"; + } + + if (command.find("COMMAND_INSERT_HCAL_READOUT") != std::string::npos) { + mInsertFrontHCalReadoutMaterial = true; + LOG(debug) << "Insert Aluminium 1cm thick layer behind HCAL to simulate readout SiPM material !"; + } + } // end if COMMAND + + if (command.find("VIRTUAL") != std::string::npos) { + + int segment, minlayer, maxLayer, isPixel; + float padSize, sensitiveThickness, pixelTreshold; + + if (command.find("N_SEGMENTS") != std::string::npos) { + mVirtualNSegments = std::stoi(tokens[1]); + LOG(debug) << "Number of Virtual Segments is set to : " << mVirtualNSegments; + } + + if (command.find("SEGMENT_LAYOUT") != std::string::npos) { + minlayer = std::stoi(tokens[1]); + maxLayer = std::stoi(tokens[2]); + padSize = std::stof(tokens[3]); + sensitiveThickness = std::stof(tokens[4]); + isPixel = std::stoi(tokens[5]); + pixelTreshold = std::stof(tokens[6]); + + if (mVirtualSegmentComposition.size() == 0) { + if (mVirtualNSegments <= 0) { + LOG(debug) << "Making 20 segments"; + for (int seg = 0; seg < 20; seg++) { + mVirtualSegmentComposition.emplace_back(); + } + mVirtualNSegments = 20; + } else { + LOG(debug) << "Making " << mVirtualNSegments << " segments"; + for (int seg = 0; seg < mVirtualNSegments; seg++) { + mVirtualSegmentComposition.emplace_back(); + } + } + } + + sscanf(command.c_str(), "VIRTUAL_SEGMENT_LAYOUT_N%d", &segment); + if (segment > mVirtualNSegments) { + continue; + } + mVirtualSegmentComposition[segment].mMinLayer = minlayer; + mVirtualSegmentComposition[segment].mMaxLayer = maxLayer; + mVirtualSegmentComposition[segment].mPadSize = padSize; + mVirtualSegmentComposition[segment].mRelativeSensitiveThickness = sensitiveThickness; + mVirtualSegmentComposition[segment].mPixelTreshold = pixelTreshold; + mVirtualSegmentComposition[segment].mIsPixel = isPixel; + + LOG(debug) << "Segment number " << segment << " defined with (minLayer, maxLayer, padSize, isPixel): (" + << minlayer << ", " << maxLayer << ", " << padSize << ", " << isPixel << ")"; + } // end if SEGMENT_LAYOUT + + } // end if VIRTUAL + + } // end while + + setUpTowerWaferSize(); + /////// re-arrange the longitudinal components + mNPixelLayers = npixlayers; + for (int i = 0; i < npixlayers; i++) { + mPixelLayerLocations[i] = pixl[i]; + } + + mNPadLayers = npadlayers; + LOG(debug) << "mNPadLayers, mNPixelLayers, mNHCalLayers, mNumberOfSegments :: " << mNPadLayers << " / " << mNPixelLayers << " / " << mNHCalLayers << " / " << mNumberOfSegments; + + mLayerSeg = (mNPadLayers + mNPixelLayers + mNHCalLayers) / mNumberOfSegments; + + if (mNumberOfSegments >= 100) { + LOG(warning) << "You reached the segments limits! Setting Number of segments to: 100"; + mNumberOfSegments = 99; + LOG(warning) << "New number of segments: " << mNumberOfSegments; + mLayerSeg = (mNPadLayers + mNPixelLayers + mNHCalLayers) / mNumberOfSegments; + } + if ((mNPadLayers + mNPixelLayers + mNHCalLayers) % mNumberOfSegments) { + mNumberOfSegments++; + for (int i = 0; i < mNumberOfSegments; i++) { + mNumberOfLayersInSegments[i] = mLayerSeg; + } + LOG(debug) << "Number of segments: " << mNumberOfSegments; + } else { + for (int i = 0; i < mNumberOfSegments; i++) { + mNumberOfLayersInSegments[i] = mLayerSeg; + } + } + + setUpLayerSegmentMap(); + + float center_z = 0; + + mPadCompositionBase.reserve(200); + mHCalCompositionBase.reserve(200); + mPixelCompositionBase.reserve(200); + mFrontMatterCompositionBase.reserve(200); + + for (auto& tmpComp : padCompDummy) { + LOG(debug) << "Material(pad layer) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + if (tmpComp.material().compare("SiPad")) { // materials other than SiPad + mPadCompositionBase.emplace_back(tmpComp.material(), tmpComp.layer(), tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), center_z, + mTowerSizeX, mTowerSizeY, tmpComp.sizeZ()); + if (mTowerSizeX < tmpComp.sizeX()) { + mTowerSizeX = tmpComp.sizeX(); + } + if (mTowerSizeY < tmpComp.sizeY()) { + mTowerSizeY = tmpComp.sizeY(); + } + } else { + for (int itowerX = 0; itowerX < mGlobal_PAD_NX_Tower; itowerX++) { + for (int itowerY = 0; itowerY < mGlobal_PAD_NY_Tower; itowerY++) { + for (int ix = 0; ix < mGlobal_PAD_NX; ix++) { + for (int iy = 0; iy < mGlobal_PAD_NY; iy++) { + auto [x, y] = getGeoPadCenterLocal(itowerX, itowerY, iy, ix); + mPadCompositionBase.emplace_back("SiPad", tmpComp.layer(), tmpComp.stack(), + ix + iy * mGlobal_PAD_NX + itowerX * mGlobal_PAD_NX * mGlobal_PAD_NY + itowerY * mGlobal_PAD_NX_Tower * mGlobal_PAD_NX * mGlobal_PAD_NY, + x, y, center_z, + mGlobal_Pad_Size, mGlobal_Pad_Size, tmpComp.sizeZ()); + if (mTowerSizeX < mGlobal_Pad_Size) { + mTowerSizeX = mGlobal_Pad_Size; + } + if (mTowerSizeY < mGlobal_Pad_Size) { + mTowerSizeY = mGlobal_Pad_Size; + } + } + } + } // end for itowerY + } // end for itowerX + } // end else + center_z += tmpComp.getThickness(); + } // end loop over pad layer compositions + LOG(debug) << "============ Created all pad layer compositions (" << mPadCompositionBase.size() << " volumes)"; + + mPadLayerThickness = center_z; + + mGlobal_PIX_OffsetY = (getTowerSizeY() - mGlobal_PIX_NY_Tower * mGlobal_PIX_SizeY) / 2 - 2.0 * mGlobal_PIX_SKIN; + + center_z = 0; + for (auto& tmpComp : pixelCompDummy) { + LOG(debug) << "Material (pixel layer) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + if (tmpComp.material().compare("SiPix")) { + mPixelCompositionBase.emplace_back(tmpComp.material(), mPixelLayerLocations[0], tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), center_z, mTowerSizeX, mTowerSizeY, tmpComp.sizeZ()); + } else { + for (int ix = 0; ix < mGlobal_PIX_NX_Tower; ix++) { + for (int iy = 0; iy < mGlobal_PIX_NY_Tower; iy++) { + auto [pixX, pixY] = getGeoPixCenterLocal(iy, ix); + mPixelCompositionBase.emplace_back("SiPix", tmpComp.layer(), tmpComp.stack(), ix + iy * mGlobal_PIX_NX_Tower, + pixX, pixY, center_z, + mGlobal_PIX_SizeX, mGlobal_PIX_SizeY, tmpComp.sizeZ()); + } + } + } + center_z += tmpComp.getThickness(); + } + LOG(debug) << "============ Created all pixel layer compositions (" << mPixelCompositionBase.size() << " volumes)"; + mPixelLayerThickness = center_z; + + // Add HCal Layers + center_z = 0; + for (auto& tmpComp : hCalCompDummy) { + LOG(debug) << "Material (hcal) " << tmpComp.material(); + LOG(debug) << "layer / stack / id :: " << tmpComp.layer() << " / " << tmpComp.stack() << " / " << tmpComp.id(); + LOG(debug) << "center x,y,dz :: " << tmpComp.sizeX() << " / " << tmpComp.sizeY() << " / " << tmpComp.sizeZ(); + mHCalCompositionBase.emplace_back(tmpComp.material(), tmpComp.layer(), tmpComp.stack(), tmpComp.id(), + tmpComp.centerX(), tmpComp.centerY(), mNHCalLayers == 1 ? 0. : center_z, // if we decided to use the spagetti HCAL it will be only one layer with two compositions + tmpComp.sizeX(), tmpComp.sizeY(), tmpComp.sizeZ()); + if (mNHCalLayers == 1) { + center_z = tmpComp.getThickness(); + } else { + center_z += tmpComp.getThickness(); + } + } + LOG(debug) << "============ Created all hcal compositions (" << mHCalCompositionBase.size() << " volumes)"; + mHCalLayerThickness = center_z; + center_z = 0; + + if (mNHCalLayers == 1 && hHCal > 2) { + mHCALDesign = Geometry::HCALDesgin::Sheets; + } else if (mNHCalLayers == 1 && hHCal == 2) { + mHCALDesign = Geometry::HCALDesgin::Spaghetti; + } else { + mHCALDesign = Geometry::HCALDesgin::Sandwich; + } + + mFrontMatterLayerThickness = center_z; + LOG(debug) << " end of SetParameters "; +} + +//_________________________________________________________________________ +const Composition* Geometry::getComposition(int layer, int stack) const +{ + + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() == layer && icomp.stack() == stack) { + return &icomp; + } + } + return nullptr; +} + +//_________________________________________________________________________ +std::vector Geometry::getFOCALMicroModule(int layer) const +{ + + std::vector layerComposition; + + if (layer == -1) { + for (auto& icomp : mGeometryComposition) { + layerComposition.push_back(&icomp); + } + } else { + for (auto& icomp : mGeometryComposition) { + if (icomp.layer() == layer) { + layerComposition.push_back(&icomp); + } + } + } + return layerComposition; +} + +//_________________________________________________________________________ +/// this gives global position of the center of tower +std::tuple Geometry::getGeoTowerCenter(int tower, int segment) const +{ + int id = tower; + int itowerx = id % getNumberOfTowersInX(); + int itowery = id / getNumberOfTowersInX(); + + float dwx = getTowerSizeX() + getTowerGapSizeX(); + float dwy = getTowerSizeY() + getTowerGapSizeY(); + + double x = itowerx * dwx + 0.5 * dwx - 0.5 * getFOCALSizeX(); + double y = itowery * dwy + 0.5 * dwy - 0.5 * getFOCALSizeY(); + if (itowerx == 0 && itowery == 5) { + x -= mGlobal_Middle_Tower_Offset; + } + if (itowerx == 1 && itowery == 5) { + x += mGlobal_Middle_Tower_Offset; + } + + // From here is HCal stuff + if (getVirtualIsHCal(segment)) { + auto [status, nCols, nRows] = getVirtualNColRow(segment); + int ix = id % nCols; + int iy = id / nRows; + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + float padSize = mVirtualSegmentComposition[segment].mPadSize; + double hCALsizeX = nCols * padSize; + double hCALsizeY = nRows * padSize; + + x = ix * padSize + 0.5 * padSize - 0.5 * hCALsizeX; + y = iy * padSize + 0.5 * padSize - 0.5 * hCALsizeY; + break; + } + case HCALDesgin::Spaghetti: { + float towerSize = getHCALTowerSize() / 7; // To be set from outside (number of channels on x & y) + y = iy * towerSize + 0.5 * towerSize - 0.5 * towerSize * nRows; + x = ix * towerSize + 0.5 * towerSize - 0.5 * towerSize * nCols; + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + x = ix * hCALsizeX / getHCALTowersInX() + 0.5 * hCALsizeX / getHCALTowersInX() - 0.5 * hCALsizeX; + y = iy * hCALsizeY / getHCALTowersInY() + 0.5 * hCALsizeY / getHCALTowersInY() - 0.5 * hCALsizeY; + break; + } + default: + break; + } + } + + /* + //// remove beam pipe area + // define beam pipe radius, calculate half of the tower diagonal in XY + // and remove every tower which center is closer than the sum of the two... + double beamPipeRadius = 3.6; // in cm TODO: check if this is OK + double towerHalfDiag = std::sqrt(2)*0.5*getTowerSizeX(); // tower half diagonal + double minRadius = beamPipeRadius+towerHalfDiag; + // + if((x*x+y*y) < (minRadius*minRadius)){ // comparing the tower center position with the minimum distance in second powers. + //mDisableTowers.push_back(Tower+1); + //return false; + } + */ + + return {x, y, getFOCALZ0()}; +} + +//_________________________________________________________________________ +std::tuple Geometry::getGeoCompositionCenter(int tower, int layer, int stack) const +{ + auto [status, segment] = getVirtualSegmentFromLayer(layer); + auto [towX, towY, towZ] = getGeoTowerCenter(tower, segment); + double z = towZ; + + Composition* comp1 = (Composition*)getComposition(layer, stack); + if (comp1 == nullptr) { + z = z + mLocalLayerZ[layer] - getFOCALSizeZ() / 2; + } else { + z = comp1->centerZ() - getFOCALSizeZ() / 2 + getFOCALZ0(); + } + return {towX, towY, z}; +} + +//_________________________________________________________________________ +/// this gives global position of the pad +std::tuple Geometry::getGeoPadCenter(int tower, int layer, int stack, int row, int col) const +{ + auto [x, y, z] = getGeoCompositionCenter(tower, layer, stack); + int itowerx = tower % mGlobal_PAD_NX_Tower; + int itowery = tower / mGlobal_PAD_NX_Tower; + auto [padX, padY] = getGeoPadCenterLocal(itowerx, itowery, row, col); + + return {x + padX, y + padY, z}; +} + +//_________________________________________________________________________ +/// this gives local position of the pad with respect to the wafer +std::tuple Geometry::getGeoPadCenterLocal(int towerX, int towerY, int row, int col) const +{ + /// startting to count from upper-left + /* + (0,0) + ___________________ + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | + */ + double x = +towerX * mWaferSizeX + mGlobal_PAD_SKIN + col * (mGlobal_Pad_Size + mGlobal_PPTOL) + 0.5 * mGlobal_Pad_Size; + double y = -towerY * mWaferSizeY - mGlobal_PAD_SKIN - row * (mGlobal_Pad_Size + mGlobal_PPTOL) - 0.5 * mGlobal_Pad_Size; + x = x - 0.5 * getTowerSizeX(); + y = y + 0.5 * mTowerSizeY; + return {x, y}; +} + +/// this gives local position of the pad with respect to the wafer +std::tuple Geometry::getGeoPixCenterLocal(int row, int col) const +{ + /// startting to count from upper-left + /* + (0,0) + ___________________ + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | __ __ + | |__| |__| + | + */ + double x = +col * (mGlobal_PIX_SizeX + 2.0 * mGlobal_PIX_SKIN) + 0.5 * mGlobal_PIX_SizeX; + double y = -row * (mGlobal_PIX_SizeY + 2.0 * mGlobal_PIX_SKIN) - 0.5 * mGlobal_PIX_SizeY; + x = x - 0.5 * mTowerSizeX; + y = y + 0.5 * mTowerSizeY - mGlobal_PIX_OffsetY; + return {x, y}; +} + +//_________________________________________________________________________ +double Geometry::getTowerSizeX() const +{ + return mTowerSizeX; + // return mGlobal_NX_NY_Pads*(mGlobal_Pad_Size+mGlobal_PPTOL)-mGlobal_PPTOL+2*mGlobal_PAD_SKIN; +} + +//_________________________________________________________________________ +double Geometry::getTowerSizeY() const +{ + return mTowerSizeY; + // return mGlobal_NX_NY_Pads*(mGlobal_Pad_Size+mGlobal_PPTOL)-mGlobal_PPTOL+2*mGlobal_PAD_SKIN; +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeX() const +{ + return mGlobal_Tower_NX * (getTowerSizeX() + mGlobal_TOWER_TOLX); +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeY() const +{ + return mGlobal_Tower_NY * (getTowerSizeY() + mGlobal_TOWER_TOLY); +} + +//_________________________________________________________________________ +double Geometry::getFOCALSizeZ() const +{ + + double ret = 0; + for (int i = 0; i < mNPadLayers + mNPixelLayers + mNHCalLayers; i++) { + ret += mLayerThickness[i]; + } + ret = ret + mFrontMatterLayerThickness; + return ret; +} + +//_________________________________________________________________________ +double Geometry::getECALSizeZ() const +{ + + double ret = 0; + for (int i = 0; i < mNPadLayers + mNPixelLayers; i++) { + ret += mLayerThickness[i]; + } + ret = ret + mFrontMatterLayerThickness; + return ret; +} + +//_________________________________________________________________________ +double Geometry::getECALCenterZ() const +{ + + // Determines the ECAL z center of mass with respect to the FOCAL + double centerZ = mFrontMatterLayerThickness + mLocalLayerZ[0] + getECALSizeZ() / 2; + return centerZ; +} + +//_________________________________________________________________________ +double Geometry::getHCALSizeZ() const +{ + + double ret = 0; + for (int i = mNPadLayers + mNPixelLayers; i < mNPadLayers + mNPixelLayers + mNHCalLayers; i++) { + ret += mLayerThickness[i]; + } + return ret; +} + +//_________________________________________________________________________ +double Geometry::getHCALCenterZ() const +{ + + double centerZ = mFrontMatterLayerThickness + mLocalLayerZ[mNPadLayers + mNPixelLayers] + getHCALSizeZ() / 2; + return centerZ; +} + +//_________________________________________________________________________ +// this returns the following quantities for the pad position location +// layer depth +// pad row and col in the wafer +// wafer id in the brick, where the pad belongs to +std::tuple Geometry::getPadPositionId2RowColStackLayer(int id) const +{ + + //// id contains loction of pads in the tower, pad stack, pad layer + ///// (fComp->id()) + (fComp->stack() << 12) + (fComp->layer() << 16) +1 ; + ///// + int number = id - 1; + int padid = (number & 0xfff); + int stack = (number >> 12) & 0x000f; + // lay = (number >> 16) & 0x00ff; + int lay = (number >> 16) & 0x000f; + + // seg = fSegments[lay]; + auto [status, seg] = getVirtualSegmentFromLayer(lay); // NOTE: to be checked since this overrides the initialization above + /*col = padid%mGlobal_PAD_NX; + row = padid/mGlobal_PAD_NX;*/ + int waferx = 0; + int wafery = 0; + int col = 0; + int row = 0; + + // This gives the (col,row) of the pixel sensor + if (getVirtualIsPixel(seg)) { + col = padid % mGlobal_PIX_NX_Tower; + row = padid / mGlobal_PIX_NX_Tower; + } else { + col = padid % mGlobal_PAD_NX; + int remainder = (padid - col) / mGlobal_PAD_NX; + row = remainder % mGlobal_PAD_NY; + remainder = (remainder - row) / mGlobal_PAD_NY; + waferx = remainder % mGlobal_PAD_NX_Tower; + wafery = remainder / mGlobal_PAD_NX_Tower; + } + /*cout << "FROM GEOMETRY stack/lay/seg/waferx/wafery/col/row :: " << stack << " / " << lay << " / " << seg << " / " + << waferx << " / " << wafery << " / " << col << " / " << row << endl;*/ + if (getVirtualIsHCal(seg)) { + auto [status, nCols, nRows] = getVirtualNColRow(seg); + col = id % nCols; + row = id / nRows; + } + + return {row, col, stack, lay, seg, waferx, wafery}; +} + +//_________________________________________________________________________ +//// this gives longitudinal position of the segment +double Geometry::getFOCALSegmentZ(int seg) const +{ + + double ret = 0; + if (seg < 0 || seg > mNumberOfSegments) { + ret = getFOCALZ0(); + } else { + for (int i = 0; i < seg; i++) { + ret += mLocalSegmentsZ[i]; + } + } + ret = ret + mLocalSegmentsZ[seg] / 2 + getFOCALZ0() - getFOCALSizeZ() / 2; + return ret; +} + +//_________________________________________________________________________ +/// this function defines: +/// layer is pixel or pad +/// which segment this layer belongs to? +void Geometry::setUpLayerSegmentMap() +{ + ///// define the longitudinal elements + //// mSegments = -1 -> strip layer + //// mSegments = 0 --> pad 0th segement + //// mSegments = 1 --> pad 1th segement + //// mSegments = 2 --> pad 2th segement + + std::vector layerType; + for (int j = 0; j < mNPixelLayers + mNPadLayers + mNHCalLayers; j++) { + layerType.push_back(0); + } + for (int i = 0; i < mNPixelLayers; i++) { + layerType[mPixelLayerLocations[i]] = -1; + } + + int low = 0; + int start = 0; + int high = 0; + for (int i = 0; i < mNumberOfSegments; i++) { + high += mNumberOfLayersInSegments[i]; + for (int j = start; j < mNPixelLayers + mNPadLayers + mNHCalLayers; j++) { + if (layerType[j] == -1) { + mSegments[j] = i; + start++; + } else { + mSegments[j] = i; + low++; + start++; + } + if (low >= high) { + break; + } + } + } +} + +//_________________________________________________________________________ +/// this is the pixel number to be stored in the Hits.root file +/// this is used for the study with fine pixel readout +/// the pad is divided into the pixels with the size of mGlobal_ Pixel_Size +int Geometry::getPixelNumber(int vol0, int vol1, int /*vol2*/, double x, double y) const +{ + int ret = 0; + if (mGlobal_Pixel_Readout == false) { + ret = -1; + return ret; + } + int id = vol0; + // int tower = vol1; + // int brick = vol2; /// meaning 0 in the current design + + auto [row, col, stack, layer, segment, waferX, waferY] = getPadPositionId2RowColStackLayer(id); + auto [pixX, pixY] = getGeoPixCenterLocal(row, col); + + double x_loc = x - pixX; + double y_loc = y - pixY; + double pixel_nbr_x = ((x_loc + 0.5 * getGlobalPixelWaferSizeX()) / (mGlobal_Pixel_Size)); + double pixel_nbr_y = ((y_loc + 0.5 * getGlobalPixelWaferSizeY()) / (mGlobal_Pixel_Size)); + + int pixel_number_x; + pixel_number_x = static_cast(pixel_nbr_x); + // if(pixel_number_x-pixel_nbr_x>0.5){ + // pixel_number_x = pixel_number_x+1; + // } + + int pixel_number_y; + pixel_number_y = static_cast(pixel_nbr_y); + // if(pixel_number_y-pixel_nbr_y>0.5){ + // pixel_number_y = pixel_number_y+1; + // } + ret = (pixel_number_x << 16) | pixel_number_y; + // cout< Geometry::getGeoPixelCenter(int pixel, int tower, int layer, int stack, int row, int col) const +{ + auto [x0, y0, z0] = getGeoPadCenter(tower, layer, stack, row, col); + + int pixel_y = pixel & 0xff; + int pixel_x = (pixel >> 8) & 0xff; + + double x1, y1; + x1 = pixel_x * mGlobal_Pixel_Size + 0.5 * mGlobal_Pixel_Size - 0.5 * mGlobal_Pad_Size; + y1 = pixel_y * mGlobal_Pixel_Size + 0.5 * mGlobal_Pixel_Size - 0.5 * mGlobal_Pad_Size; + + return {x1 + x0, y1 + y0, z0}; +} + +std::tuple Geometry::getVirtualInfo(double x, double y, double z) const +{ + // + // Calculate col, row, layer, (virtual) segment from x,y,z + // returns false if outside volume + // + int col = -1, row = -1; + auto [status, layer, segment] = getVirtualLayerSegment(z); + + if (!status) { + return {false, col, row, layer, segment}; + } + if (segment == -1) { + return {false, col, row, layer, segment}; + } + if (std::abs(x) > (getFOCALSizeX() + 2 * getMiddleTowerOffset()) / 2) { + return {false, col, row, layer, segment}; + } + if (std::abs(y) > getFOCALSizeY() / 2) { + return {false, col, row, layer, segment}; + } + + if (getVirtualIsHCal(segment)) { + float towerSize = getHCALTowerSize(); + double beamPipeRadius = 3.0; // in cm TODO check the number is OK (different hardcoded values are used elsewhere) + double minRadius = beamPipeRadius + towerSize / 2.; + + double hCALsizeX = getHCALTowersInX() * towerSize; + double hCALsizeY = getHCALTowersInY() * towerSize; + + if (x < minRadius && x > -minRadius && y < minRadius && y > -minRadius) { + x = x < 0 ? x - 0.001 : x + 0.001; + y = y < 0 ? y - 0.001 : y + 0.001; + } + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + row = (int)((y + hCALsizeY / 2) / (towerSize)); + col = (int)((x + hCALsizeX / 2) / (towerSize)); + break; + } + case HCALDesgin::Spaghetti: { + row = (int)((y + hCALsizeY / 2) / (towerSize / 7)); + col = (int)((x + hCALsizeX / 2) / (towerSize / 7)); + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + if (y < getHCALBeamPipeHoleSize() / 2 && y > -getHCALBeamPipeHoleSize() / 2) { + if (x < 0) { + x += 1.0; // remove the offset around the beam pipe + } else { + x -= 1.0; // remove the offset around the beam pipe + } + } + + row = (int)((y + hCALsizeY / 2) / (hCALsizeY / getHCALTowersInY())); + if (x > 0) { + x = x - 0.15 - 0.06; + } + col = (int)((x - 0.15 + hCALsizeX / 2) / ((hCALsizeX - 0.15 * 2 - 0.06 * 2) / getHCALTowersInX())); + break; + } + default: + break; + } + } else { + row = (int)((y + getFOCALSizeY() / 2) / mVirtualSegmentComposition[segment].mPadSize); + // if it is the towers right and left of the beam pipe, adjust x so the offset is removed + // if(y < 4.2 && y > - 4.2) { // TO BE set from outside or somewhere else -4,4 is the y position of the middle towers + // x = x < 0 ? x + GetMiddleTowerOffset() : x - GetMiddleTowerOffset(); + // } + col = (int)((x + getFOCALSizeX() / 2) / mVirtualSegmentComposition[segment].mPadSize); + } + return {true, col, row, layer, segment}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getXYZFromColRowSeg(int col, int row, int segment) const +{ + + double x = 0.0, y = 0.0, z = 0.0; + if (segment > mVirtualNSegments) { + return {false, x, y, z}; + } + + if (getVirtualIsHCal(segment)) { + float towerSize = getHCALTowerSize(); + double hCALsizeX = getHCALTowersInX() * towerSize; + double hCALsizeY = getHCALTowersInY() * towerSize; + + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (towerSize); + break; + } + case HCALDesgin::Spaghetti: { + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize / 7); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (towerSize / 7); + break; + } + case HCALDesgin::Sheets: { + Composition comp1 = mHCalCompositionBase[0]; + Composition comp2 = mHCalCompositionBase[2]; + double hCALsizeX = comp1.sizeX() * 2; // Size of two sheet in X + double hCALsizeY = getHCALTowersInY() * (comp1.sizeY() + comp2.sizeY()) * 2; // To be set in a better way + + y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (hCALsizeY / getHCALTowersInY()); + x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (hCALsizeX / getHCALTowersInX()); + break; + } + default: + break; + } + } else { + y = -1 * getFOCALSizeY() / 2 + ((float)row + 0.5) * mVirtualSegmentComposition[segment].mPadSize; + x = -1 * getFOCALSizeX() / 2 + ((float)col + 0.5) * mVirtualSegmentComposition[segment].mPadSize; + // Middle towers offset + // if(y < 4.2 && y > - 4.2) { // TO BE set from outside or somewhere else -4,4 is the y position of the middle towers + // x = x < 0 ? x - GetMiddleTowerOffset() : x + GetMiddleTowerOffset(); + // } + } + + if (std::abs(x) > (getFOCALSizeX() + 2 * getMiddleTowerOffset()) / 2) { + return {false, x, y, z}; + } + if (std::abs(y) > getFOCALSizeY() / 2) { + return {false, x, y, z}; + } + z = getVirtualSegmentZ(segment); + return {true, x, y, z}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualNColRow(int segment) const +{ + + // ix + iy*mGlobal_PAD_NX + itowerX*mGlobal_PAD_NX*mGlobal_PAD_NY + itowerY*mGlobal_PAD_NX_Tower*mGlobal_PAD_NX*mGlobal_PAD_NY + int nCol = -1, nRow = -1; + if (mVirtualSegmentComposition.size() == 0) { + return {false, nCol, nRow}; + } + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return {false, nCol, nRow}; + } + nCol = (int)(getFOCALSizeX() / mVirtualSegmentComposition[segment].mPadSize + 0.001); + nRow = (int)(getFOCALSizeY() / mVirtualSegmentComposition[segment].mPadSize + 0.001); + if (getVirtualIsHCal(segment)) { + switch (mHCALDesign) { + case HCALDesgin::Sandwich: { + nCol = getHCALTowersInX(); + nRow = getHCALTowersInY(); + break; + } + case HCALDesgin::Spaghetti: { + nCol = getHCALTowersInX() * 7; // To be set from outside (number of channels in each tower on x) + nRow = getHCALTowersInY() * 7; // To be set from outside (number of channels in each tower on y) + break; + } + case HCALDesgin::Sheets: { + nCol = getHCALTowersInX(); + nRow = getHCALTowersInY(); + break; + } + default: + break; + } + } + return {true, nCol, nRow}; +} + +//_______________________________________________________________________ +int Geometry::getVirtualNSegments() const +{ + + return mVirtualNSegments; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualLayerSegment(float z) const +{ + + int layer = -1; + int segment = -1; + + z = z - getFOCALZ0() + getFOCALSizeZ() / 2; // z from front face (excluding fron matter) + float emLayersZ = mNPadLayers * mPadLayerThickness + mNPixelLayers * mPixelLayerThickness; // Pixel layers replace pad layers + if (z < emLayersZ) { + layer = mNPadLayers + mNPixelLayers - 1; + while (layer >= 0 && z < mLocalLayerZ[layer]) { + layer--; + } + } else { + z = z - emLayersZ; + layer = int(z / mHCalLayerThickness) + mNPadLayers + mNPixelLayers; + } + + if ((layer < 0) || (layer >= (mNPadLayers + mNPixelLayers + mNHCalLayers))) { + return {false, layer, segment}; + } + + segment = -1; + for (int nSeg = 0; nSeg < mVirtualNSegments; nSeg++) { + if ((layer >= mVirtualSegmentComposition[nSeg].mMinLayer) && (layer <= mVirtualSegmentComposition[nSeg].mMaxLayer)) { + segment = nSeg; + break; + } + } + + if (segment == mVirtualNSegments) { + return {false, layer, segment}; + } + return {true, layer, segment}; +} + +//_______________________________________________________________________ +std::tuple Geometry::getVirtualSegmentFromLayer(int layer) const +{ + + int segment = -1; + for (int nSeg = 0; nSeg < mVirtualNSegments; nSeg++) { + // cout << "Segment boundaries " << nSeg << " : " << mVirtualSegmentComposition[nSeg].fMinLayer << " " << mVirtualSegmentComposition[nSeg].fMaxLayer << endl; + if ((layer >= mVirtualSegmentComposition[nSeg].mMinLayer) && (layer <= mVirtualSegmentComposition[nSeg].mMaxLayer)) { + segment = nSeg; + break; + } + } + if (segment == mVirtualNSegments) { + return {false, segment}; + } + return {true, segment}; +} + +//_______________________________________________________________________ +int Geometry::getVirtualSegment(float z) const +{ + auto [status, layer, segment] = getVirtualLayerSegment(z); + return segment; +} + +//_______________________________________________________________________ +float Geometry::getVirtualPadSize(int segment) const +{ + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mPadSize; +} + +//_______________________________________________________________________ +float Geometry::getVirtualRelativeSensitiveThickness(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mRelativeSensitiveThickness; +} + +//_______________________________________________________________________ +float Geometry::getVirtualPixelTreshold(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + return mVirtualSegmentComposition[segment].mPixelTreshold; +} + +//________________________________________________________________________ +float Geometry::getVirtualSegmentSizeZ(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + float size = 0; + for (int nLay = mVirtualSegmentComposition[segment].mMinLayer; nLay <= mVirtualSegmentComposition[segment].mMaxLayer; nLay++) { + size += mLayerThickness[nLay]; + } + return size; +} + +//________________________________________________________________________ +float Geometry::getVirtualSegmentZ(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + float before = 0; + float thickness = 0; + + for (int nLay = 0; nLay < mVirtualSegmentComposition[segment].mMinLayer; nLay++) { + before += mLayerThickness[nLay]; + } + for (int nLay = mVirtualSegmentComposition[segment].mMinLayer; nLay <= mVirtualSegmentComposition[segment].mMaxLayer; nLay++) { + thickness += mLayerThickness[nLay]; + } + return getFOCALZ0() - getFOCALSizeZ() / 2 + before + thickness / 2; +} + +//________________________________________________________________________ +bool Geometry::getVirtualIsPixel(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return false; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return false; + } + + return (mVirtualSegmentComposition[segment].mIsPixel == 1); +} + +//________________________________________________________________________ +bool Geometry::getVirtualIsHCal(int segment) const +{ + + if (mVirtualSegmentComposition.size() == 0) { + return false; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return false; + } + return (mVirtualSegmentComposition[segment].mIsPixel == 2); +} + +//________________________________________________________________________ +int Geometry::getVirtualNLayersInSegment(int segment) const +{ + // + // Get the number of layers in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return (mVirtualSegmentComposition[segment].mMaxLayer - mVirtualSegmentComposition[segment].mMinLayer + 1); +} + +//_______________________________________________________________________ +int Geometry::getVirtualMinLayerInSegment(int segment) const +{ + // + // Get the number of first layer in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return mVirtualSegmentComposition[segment].mMinLayer; +} + +//_______________________________________________________________________ +int Geometry::getVirtualMaxLayerInSegment(int segment) const +{ + // + // Get the number of first layer in a given segment + // + if (mVirtualSegmentComposition.size() == 0) { + return -1; + } + + if ((segment < 0) || (segment >= mVirtualNSegments)) { + return -1; + } + return mVirtualSegmentComposition[segment].mMaxLayer; +} diff --git a/Detectors/FOCAL/base/src/Hit.cxx b/Detectors/FOCAL/base/src/Hit.cxx new file mode 100644 index 0000000000000..4155cdc3988a7 --- /dev/null +++ b/Detectors/FOCAL/base/src/Hit.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include "FOCALBase/Hit.h" + +using namespace o2::focal; + +Hit::Hit(int primary, int trackID, int detID, Subsystem_t subsystem, double initialEnergy, const math_utils::Point3D& pos, + double tof, double eLoss) : o2::BasicXYZEHit(pos.X(), pos.Y(), pos.Z(), tof, eLoss, trackID, detID), + mSubSystem(subsystem), + mInitialEnergy(initialEnergy) + +{ +} + +bool Hit::operator==(const Hit& other) const +{ + return (GetDetectorID() == other.GetDetectorID()) && (GetTrackID() == other.GetTrackID() && mSubSystem == other.mSubSystem); +} + +bool Hit::operator<(const Hit& other) const +{ + if (GetTrackID() != other.GetTrackID()) { + return GetTrackID() < other.GetTrackID(); + } + if (mSubSystem != other.mSubSystem) { + return mSubSystem < other.mSubSystem; + } + return GetDetectorID() < other.GetDetectorID(); +} + +Hit& Hit::operator+=(const Hit& other) +{ + SetEnergyLoss(GetEnergyLoss() + other.GetEnergyLoss()); + return *this; +} + +void Hit::printStream(std::ostream& stream) const +{ + stream << "FOCAL point: Track " << GetTrackID() << " in detector segment " << GetDetectorID() + << " at position (" << GetX() << "|" << GetY() << "|" << GetZ() << "), energy loss " << GetEnergyLoss() + << ", initial (parent) energy " << mInitialEnergy << " from primary " << mPrimary; +} + +Hit o2::focal::operator+(const Hit& lhs, const Hit& rhs) +{ + Hit summed(lhs); + summed += rhs; + return summed; +} + +std::ostream& o2::focal::operator<<(std::ostream& stream, const o2::focal::Hit& point) +{ + point.printStream(stream); + return stream; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calib/CMakeLists.txt b/Detectors/FOCAL/calib/CMakeLists.txt index 01288c40320e9..4ff37d91d89bc 100644 --- a/Detectors/FOCAL/calib/CMakeLists.txt +++ b/Detectors/FOCAL/calib/CMakeLists.txt @@ -11,10 +11,12 @@ o2_add_library(FOCALCalib SOURCES src/PadPedestal.cxx + src/PadBadChannelMap.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL Boost::serialization ) o2_target_root_dictionary( FOCALCalib HEADERS include/FOCALCalib/PadPedestal.h + include/FOCALCalib/PadBadChannelMap.h ) \ No newline at end of file diff --git a/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h b/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h new file mode 100644 index 0000000000000..198c98a44c90d --- /dev/null +++ b/Detectors/FOCAL/calib/include/FOCALCalib/PadBadChannelMap.h @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_FOCAL_PADBADCHANNELMAP_H +#define ALICEO2_FOCAL_PADBADCHANNELMAP_H + +#include +#include +#include +#include +#include "Rtypes.h" + +#include "DataFormatsFOCAL/Constants.h" + +namespace o2::focal +{ + +class PadBadChannelMap +{ + public: + enum MaskType_t { + GOOD_CHANNEL = 0, + WARM_CHANNEL = 1, + BAD_CHANNEL = 2, + DEAD_CHANNEL = 3 + }; + + class ChannelIndexException final : public std::exception + { + public: + ChannelIndexException(std::size_t layer, std::size_t channel) : mLayer(layer), mChannel(channel) + { + mMessage = "Access to invalid channel: Layer " + std::to_string(mLayer) + ", channel " + std::to_string(mChannel); + } + ~ChannelIndexException() noexcept final = default; + + const char* what() const noexcept final { return mMessage.data(); } + + std::size_t getLayer() const noexcept { return mLayer; } + std::size_t getChannel() const noexcept { return mChannel; } + + private: + std::size_t mLayer; + std::size_t mChannel; + std::string mMessage; + }; + + PadBadChannelMap(); + ~PadBadChannelMap() = default; + + void reset(); + + void setChannelStatus(std::size_t layer, std::size_t channel, MaskType_t channeltype); + void setGoodChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::WARM_CHANNEL); } + void setBadChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::BAD_CHANNEL); } + void setDeadChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::DEAD_CHANNEL); } + void setWarmChannel(std::size_t layer, std::size_t channel) { setChannelStatus(layer, channel, MaskType_t::WARM_CHANNEL); } + + MaskType_t getChannelStatus(std::size_t layer, std::size_t channel) const; + bool isGoodChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isBadChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isDeadChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + bool isWarmChannel(std::size_t layer, std::size_t channel) const { return getChannelStatus(layer, channel) == MaskType_t::GOOD_CHANNEL; } + + private: + void init(); + std::size_t getChannelIndex(std::size_t layer, std::size_t channel) const; + std::array mChannelStatus; + + ClassDefNV(PadBadChannelMap, 1) +}; + +} // namespace o2::focal +#endif // ALICEO2_FOCAL_PADBADCHANNELMAP_H \ No newline at end of file diff --git a/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h b/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h index 737d6eed7e33a..c074756d147cc 100644 --- a/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h +++ b/Detectors/FOCAL/calib/include/FOCALCalib/PadPedestal.h @@ -51,7 +51,7 @@ class PadPedestal } }; - class InvalidChannelException : public std::exception + class InvalidChannelException final : public std::exception { public: InvalidChannelException(std::size_t layer, std::size_t channel) : mLayer(layer), mChannel(channel) @@ -71,7 +71,7 @@ class PadPedestal std::string mMessage; }; - class InvalidLayerException : public std::exception + class InvalidLayerException final : public std::exception { public: InvalidLayerException(std::size_t layer) : mLayer(layer) @@ -91,12 +91,15 @@ class PadPedestal PadPedestal() = default; ~PadPedestal() = default; + bool operator==(const PadPedestal& rhs) const; + void clear(); void setPedestal(std::size_t layer, std::size_t channel, double pedestal); double getPedestal(std::size_t layer, std::size_t channel) const; TH1* getHistogramRepresentation(int layer) const; std::array getLayerHistogramRepresentations() const; + int getNumberOfChannels() const { return mPedestalValues.size(); } private: std::unordered_map mPedestalValues; diff --git a/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h b/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h index a7fc9b4f3c620..8b7910a56aa76 100644 --- a/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h +++ b/Detectors/FOCAL/calib/src/FOCALCalibLinkDef.h @@ -16,4 +16,6 @@ #pragma link off all functions; #pragma link C++ class o2::focal::PadPedestal + ; +#pragma link C++ class o2::focal::PadPedestal::ChannelID + ; +#pragma link C++ class o2::focal::PadBadChannelMap + ; #endif diff --git a/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx b/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx new file mode 100644 index 0000000000000..e5cf8de042f70 --- /dev/null +++ b/Detectors/FOCAL/calib/src/PadBadChannelMap.cxx @@ -0,0 +1,50 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "FOCALCalib/PadBadChannelMap.h" + +using namespace o2::focal; + +PadBadChannelMap::PadBadChannelMap() +{ + init(); +} + +void PadBadChannelMap::init() +{ + // Mark all channels as good channels + for (std::size_t index = 0; index < mChannelStatus.size(); index++) { + mChannelStatus[index] = static_cast(MaskType_t::GOOD_CHANNEL); + } +} + +void PadBadChannelMap::reset() +{ + init(); +} + +void PadBadChannelMap::setChannelStatus(std::size_t layer, std::size_t channel, MaskType_t masktype) +{ + mChannelStatus[getChannelIndex(layer, channel)] = static_cast(masktype); +} + +PadBadChannelMap::MaskType_t PadBadChannelMap::getChannelStatus(std::size_t layer, std::size_t channel) const +{ + return static_cast(mChannelStatus[getChannelIndex(layer, channel)]); +} + +std::size_t PadBadChannelMap::getChannelIndex(std::size_t layer, std::size_t channel) const +{ + if (layer >= constants::PADS_NLAYERS || channel >= constants::PADLAYER_MODULE_NCHANNELS) { + throw ChannelIndexException(layer, channel); + } + return channel + constants::PADLAYER_MODULE_NCHANNELS * layer; +} \ No newline at end of file diff --git a/Detectors/FOCAL/calib/src/PadPedestal.cxx b/Detectors/FOCAL/calib/src/PadPedestal.cxx index d187af4622b5d..e61d74a45817b 100644 --- a/Detectors/FOCAL/calib/src/PadPedestal.cxx +++ b/Detectors/FOCAL/calib/src/PadPedestal.cxx @@ -8,7 +8,7 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. - +#include #include #include @@ -20,6 +20,45 @@ void PadPedestal::clear() { } +bool PadPedestal::operator==(const PadPedestal& rhs) const +{ + if (mPedestalValues.size() != rhs.mPedestalValues.size()) { + LOG(debug) << "Error size: this " << mPedestalValues.size() << ", other " << rhs.mPedestalValues.size(); + return false; + } + bool failure = false; + // check equalty of content based on other object + for (auto [channel, pedestal] : rhs.mPedestalValues) { + auto found = mPedestalValues.find(channel); + if (found == mPedestalValues.end()) { + LOG(debug) << "Key not found this: Layer " << channel.mLayer << ", channel " << channel.mChannel; + failure = true; + } else { + if (std::abs(found->second - pedestal) > DBL_EPSILON) { + LOG(debug) << "Value error channel layer " << channel.mLayer << ", channel " << channel.mChannel << " this " << found->second << ", other " << pedestal; + failure = true; + } + } + } + if (failure) { + return false; + } + // check equality of content based on this object + for (auto [channel, pedestal] : mPedestalValues) { + auto found = rhs.mPedestalValues.find(channel); + if (found == rhs.mPedestalValues.end()) { + LOG(debug) << "Key not found other: Layer " << channel.mLayer << ", channel: " << channel.mChannel; + failure = true; + } else { + if (std::abs(found->second - pedestal) > DBL_EPSILON) { + LOG(debug) << "Value error channel layer " << channel.mLayer << ", channel " << channel.mChannel << " other " << found->second << ", this " << pedestal; + failure = true; + } + } + } + return !failure; +} + void PadPedestal::setPedestal(std::size_t layer, std::size_t channel, double pedestal) { if (layer >= constants::PADS_NLAYERS) { diff --git a/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h b/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h index eb6f4bb9471e3..d0cb95bc9b6b1 100644 --- a/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h +++ b/Detectors/FOCAL/calibration/include/FOCALCalibration/PadPedestalCalibDevice.h @@ -34,7 +34,7 @@ class PadPedestalCalibDevice : public framework::Task MEAN, FIT }; - PadPedestalCalibDevice(bool updateCCDB, const std::string& path); + PadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debugMode); ~PadPedestalCalibDevice() final = default; void init(framework::InitContext& ctx) final; @@ -50,12 +50,13 @@ class PadPedestalCalibDevice : public framework::Task PadDecoder mDecoder; std::unique_ptr mPedestalContainer; bool mUpdateCCDB = true; + bool mDebug = false; std::string mPath = "./"; Method_t mExtractionMethod = Method_t::MAX; std::array, 18> mADCDistLayer; }; -o2::framework::DataProcessorSpec getPadPedestalCalibDevice(bool updateCCDB, const std::string& path); +o2::framework::DataProcessorSpec getPadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debug); } // namespace o2::focal diff --git a/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx b/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx index c0b802f0c0ac5..86d3566cb5304 100644 --- a/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx +++ b/Detectors/FOCAL/calibration/src/PadPedestalCalibDevice.cxx @@ -30,7 +30,7 @@ using namespace o2::focal; -PadPedestalCalibDevice::PadPedestalCalibDevice(bool updateCCDB, const std::string& path) : mUpdateCCDB(updateCCDB), mPath(path) {} +PadPedestalCalibDevice::PadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debugMode) : mUpdateCCDB(updateCCDB), mPath(path), mDebug(debugMode) {} void PadPedestalCalibDevice::init(framework::InitContext& ctx) { @@ -135,6 +135,12 @@ void PadPedestalCalibDevice::sendData(o2::framework::DataAllocator& output) output.snapshot(framework::Output{o2::calibration::Utils::gDataOriginCDBWrapper, "FOC_PADPEDESTALS", subSpec}, info); } + if (mDebug) { + TFile pedestalWriter("FOCALPedestalCalib.root", "RECREATE"); + pedestalWriter.cd(); + pedestalWriter.WriteObjectAny(mPedestalContainer.get(), o2::focal::PadPedestal::Class(), "ccdb_object"); + } + // store ADC distributions in local file std::filesystem::path filepath(mPath); filepath.append("FOCALPadPedestals.root"); @@ -209,7 +215,7 @@ double PadPedestalCalibDevice::evaluatePedestal(TH1* channel) return pedestal; } -o2::framework::DataProcessorSpec o2::focal::getPadPedestalCalibDevice(bool updateCCDB, const std::string& path) +o2::framework::DataProcessorSpec o2::focal::getPadPedestalCalibDevice(bool updateCCDB, const std::string& path, bool debug) { std::vector outputs; outputs.emplace_back(framework::ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "FOC_PADPEDESTALS"}, o2::framework::Lifetime::Sporadic); @@ -218,7 +224,7 @@ o2::framework::DataProcessorSpec o2::focal::getPadPedestalCalibDevice(bool updat return o2::framework::DataProcessorSpec{"FOCALPadPedestalCalibDevice", o2::framework::select("A:FOC/RAWDATA"), outputs, - o2::framework::adaptFromTask(updateCCDB, path), + o2::framework::adaptFromTask(updateCCDB, path, debug), o2::framework::Options{ {"calibmethod", o2::framework::VariantType::String, "max", {"Method used for pedestal evaluation"}}}}; } \ No newline at end of file diff --git a/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx b/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx index 050586df43e31..3433ba0c9c0aa 100644 --- a/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx +++ b/Detectors/FOCAL/calibration/src/pad-pedestal-calibration-workflow.cxx @@ -17,6 +17,7 @@ void customize(std::vector& workflowOptions) { workflowOptions.push_back(o2::framework::ConfigParamSpec{"use-ccdb", o2::framework::VariantType::Bool, false, {"enable access to ccdb cpv calibration objects"}}); + workflowOptions.push_back(o2::framework::ConfigParamSpec{"debug", o2::framework::VariantType::Bool, false, {"debug mode (store calibration objects to local file)"}}); workflowOptions.push_back(o2::framework::ConfigParamSpec{"path", o2::framework::VariantType::String, "./", {"path to store temp files"}}); workflowOptions.push_back(o2::framework::ConfigParamSpec{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings"}}); } @@ -28,8 +29,9 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co o2::framework::WorkflowSpec specs; o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useCCDB = configcontext.options().get("use-ccdb"); + auto debugMode = configcontext.options().get("debug"); auto path = configcontext.options().get("path"); - specs.emplace_back(o2::focal::getPadPedestalCalibDevice(useCCDB, path)); + specs.emplace_back(o2::focal::getPadPedestalCalibDevice(useCCDB, path, debugMode)); return specs; } \ No newline at end of file diff --git a/Detectors/FOCAL/doxymodules.h b/Detectors/FOCAL/doxymodules.h new file mode 100644 index 0000000000000..5fee41475ce28 --- /dev/null +++ b/Detectors/FOCAL/doxymodules.h @@ -0,0 +1,72 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/** + * @defgroup DetectorFOCAL FOCAL + * @brief FOCAL simulation and reconstruction + * + * See \ref refDetectorsFOCAL for more information + */ + +/** + * @defgroup FOCALbase FOCAL base + * @brief Main FOCAL components + * @ingroup DetectorFOCAL + * + * Main FOCAL components used in various libraries + * - Geometry + * - Mapping + * - Basic data types not part of the FOCAL format + */ + +/** + * @defgroup FOCALcalibration FOCAL calibration + * @brief FOCAL calibration objects + * @ingroup DetectorFOCAL + * + * FOCAL calibration objects + */ + +/** + * @defgroup FOCALcalib FOCAL calib + * @brief FOCAL bad channel calibration + * @ingroup DetectorFOCAL + * + * FOCAL calibrators and calibration workflows + * + */ + +/** + * @defgroup FOCALsimulation FOCAL simulation + * @brief FOCAL simulation code + * @ingroup DetectorFOCAL + * + * FOCAL simulation package. See \ref refFOCALsimulation + * for more information + */ + +/** + * @defgroup FOCALreconstruction FOCAL reconstruction + * @brief FOCAL reconstruction code + * @ingroup DetectorFOCAL + * + * FOCAL reconstruction package. See \ref refFOCALreconstruction + * for more information + */ + +/** + * @defgroup FOCALworkflow FOCAL workflow + * @brief FOCAL reconstruction workflow + * @ingroup DetectorFOCAL + * + * FOCAL reconstruction workflow package. See \ref refFOCALworkflow + * for more information. + */ \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/CMakeLists.txt b/Detectors/FOCAL/reconstruction/CMakeLists.txt index 289e83014b113..759430cc329e1 100644 --- a/Detectors/FOCAL/reconstruction/CMakeLists.txt +++ b/Detectors/FOCAL/reconstruction/CMakeLists.txt @@ -34,4 +34,6 @@ o2_target_root_dictionary( o2_add_executable(rawreader-pad-rootify COMPONENT_NAME focal PUBLIC_LINK_LIBRARIES O2::DataFormatsFOCAL O2::FOCALReconstruction O2::DetectorsRaw ROOT::RIO ROOT::Tree - SOURCES run/rawReaderPadRootify.cxx) \ No newline at end of file + SOURCES run/rawReaderPadRootify.cxx) + +o2_data_file(COPY files DESTINATION Detectors/FOC) \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/files/mapping_ib.data b/Detectors/FOCAL/reconstruction/files/mapping_ib.data new file mode 100644 index 0000000000000..45978a6334324 --- /dev/null +++ b/Detectors/FOCAL/reconstruction/files/mapping_ib.data @@ -0,0 +1,41 @@ +// FEE, Lane, Chip, Layer, Col, Row, Inv Col, Inv Row +// Layer 0, version 0 +0, 0, 0, 0, 0, 4, 0, 0 +0, 0, 1, 0, 1, 4, 0, 0 +0, 0, 2, 0, 2, 4, 0, 0 +0, 0, 3, 0, 0, 2, 0, 0 +0, 0, 4, 0, 1, 2, 0, 0 +0, 0, 5, 0, 2, 2, 0, 0 +0, 0, 6, 0, 0, 0, 0, 0 +0, 0, 7, 0, 1, 0, 0, 0 +0, 0, 8, 0, 2, 0, 0, 0 +// Layer 0, version 1 +1, 0, 0, 0, 0, 5, 0, 0 +1, 0, 1, 0, 1, 5, 0, 0 +1, 0, 2, 0, 2, 5, 0, 0 +1, 0, 3, 0, 0, 3, 0, 0 +1, 0, 4, 0, 1, 3, 0, 0 +1, 0, 5, 0, 2, 3, 0, 0 +1, 0, 6, 0, 0, 1, 0, 0 +1, 0, 7, 0, 1, 1, 0, 0 +1, 0, 8, 0, 2, 1, 0, 0 +// Layer 1, version 0 +2, 0, 0, 1, 0, 4, 0, 0 +2, 0, 1, 1, 1, 4, 0, 0 +2, 0, 2, 1, 2, 4, 0, 0 +2, 0, 3, 1, 0, 2, 0, 0 +2, 0, 4, 1, 1, 2, 0, 0 +2, 0, 5, 1, 2, 2, 0, 0 +2, 0, 6, 1, 0, 0, 0, 0 +2, 0, 7, 1, 1, 0, 0, 0 +2, 0, 8, 1, 2, 0, 0, 0 +// Layer 1, version 1 +3, 0, 0, 1, 0, 5, 0, 0 +3, 0, 1, 1, 1, 5, 0, 0 +3, 0, 2, 1, 2, 5, 0, 0 +3, 0, 3, 1, 0, 3, 0, 0 +3, 0, 4, 1, 1, 3, 0, 0 +3, 0, 5, 1, 2, 3, 0, 0 +3, 0, 6, 1, 0, 1, 0, 0 +3, 0, 7, 1, 1, 1, 0, 0 +3, 0, 8, 1, 2, 1, 0, 0 \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/files/mapping_ob.data b/Detectors/FOCAL/reconstruction/files/mapping_ob.data new file mode 100644 index 0000000000000..47e471ead73e6 --- /dev/null +++ b/Detectors/FOCAL/reconstruction/files/mapping_ob.data @@ -0,0 +1,89 @@ +// FEE, Lane, Chip, Layer, Col, Row, Inv Col, Inv Row +// Layer0, v0 (upper and lower) +0, 8, 6, 0, 0, 5, 1, 0 +0, 8, 5, 0, 1, 5, 1, 0 +0, 8, 4, 0, 2, 5, 1, 0 +0, 8, 3, 0, 3, 5, 1, 0 +0, 8, 2, 0, 4, 5, 1, 0 +0, 8, 1, 0, 5, 5, 1, 0 +0, 8, 0, 0, 6, 5, 1, 0 +0, 6, 8, 0, 0, 4, 0, 1 +0, 6, 9, 0, 1, 4, 0, 1 +0, 6, 10, 0, 2, 4, 0, 1 +0, 6, 11, 0, 3, 4, 0, 1 +0, 6, 12, 0, 4, 4, 0, 1 +0, 6, 13, 0, 5, 4, 0, 1 +0, 6, 14, 0, 6, 4, 0, 1 +0, 24, 6, 0, 0, 1, 1, 0 +0, 24, 5, 0, 1, 1, 1, 0 +0, 24, 4, 0, 2, 1, 1, 0 +0, 24, 3, 0, 3, 1, 1, 0 +0, 24, 2, 0, 4, 1, 1, 0 +0, 24, 1, 0, 5, 1, 1, 0 +0, 24, 0, 0, 6, 1, 1, 0 +0, 22, 8, 0, 0, 0, 0, 1 +0, 22, 9, 0, 1, 0, 0, 1 +0, 22, 10, 0, 2, 0, 0, 1 +0, 22, 11, 0, 3, 0, 0, 1 +0, 22, 12, 0, 4, 0, 0, 1 +0, 22, 13, 0, 5, 0, 0, 1 +0, 22, 14, 0, 6, 0, 0, 1 +// Layer0, v1 (middle) +1, 6, 8, 0, 0, 3, 0, 0 +1, 6, 9, 0, 1, 3, 0, 0 +1, 6, 10, 0, 2, 3, 0, 0 +1, 6, 11, 0, 3, 3, 0, 0 +1, 6, 12, 0, 4, 3, 0, 0 +1, 6, 13, 0, 5, 3, 0, 0 +1, 6, 14, 0, 6, 3, 0, 0 +1, 8, 6, 0, 0, 2, 1, 1 +1, 8, 5, 0, 1, 2, 1, 1 +1, 8, 4, 0, 2, 2, 1, 1 +1, 8, 3, 0, 3, 2, 1, 1 +1, 8, 2, 0, 4, 2, 1, 1 +1, 8, 1, 0, 5, 2, 1, 1 +1, 8, 0, 0, 6, 2, 1, 1 +// Layer1, v0 (upper and lower) +2, 8, 6, 1, 0, 5, 1, 0 +2, 8, 5, 1, 1, 5, 1, 0 +2, 8, 4, 1, 2, 5, 1, 0 +2, 8, 3, 1, 3, 5, 1, 0 +2, 8, 2, 1, 4, 5, 1, 0 +2, 8, 1, 1, 5, 5, 1, 0 +2, 8, 0, 1, 6, 5, 1, 0 +2, 6, 8, 1, 0, 4, 0, 1 +2, 6, 9, 1, 1, 4, 0, 1 +2, 6, 10, 1, 2, 4, 0, 1 +2, 6, 11, 1, 3, 4, 0, 1 +2, 6, 12, 1, 4, 4, 0, 1 +2, 6, 13, 1, 5, 4, 0, 1 +2, 6, 14, 1, 6, 4, 0, 1 +2, 24, 6, 1, 0, 1, 1, 0 +2, 24, 5, 1, 1, 1, 1, 0 +2, 24, 4, 1, 2, 1, 1, 0 +2, 24, 3, 1, 3, 1, 1, 0 +2, 24, 2, 1, 4, 1, 1, 0 +2, 24, 1, 1, 5, 1, 1, 0 +2, 24, 0, 1, 6, 1, 1, 0 +2, 22, 8, 1, 0, 0, 0, 1 +2, 22, 9, 1, 1, 0, 0, 1 +2, 22, 10, 1, 2, 0, 0, 1 +2, 22, 11, 1, 3, 0, 0, 1 +2, 22, 12, 1, 4, 0, 0, 1 +2, 22, 13, 1, 5, 0, 0, 1 +2, 22, 14, 1, 6, 0, 0, 1 +// Layer1, v1 (middle) +1, 22, 8, 1, 0, 3, 0, 0 +1, 22, 9, 1, 1, 3, 0, 0 +1, 22, 10, 1, 2, 3, 0, 0 +1, 22, 11, 1, 3, 3, 0, 0 +1, 22, 12, 1, 4, 3, 0, 0 +1, 22, 13, 1, 5, 3, 0, 0 +1, 22, 14, 1, 6, 3, 0, 0 +1, 24, 6, 1, 0, 2, 1, 1 +1, 24, 5, 1, 1, 2, 1, 1 +1, 24, 4, 1, 2, 2, 1, 1 +1, 24, 3, 1, 3, 2, 1, 1 +1, 24, 2, 1, 4, 2, 1, 1 +1, 24, 1, 1, 5, 2, 1, 1 +1, 24, 0, 1, 6, 2, 1, 1 \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h index c35a7d3671b7f..fc56c3658e996 100644 --- a/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h +++ b/Detectors/FOCAL/reconstruction/include/FOCALReconstruction/PixelMapper.h @@ -19,26 +19,35 @@ #include #include #include +#include namespace o2::focal { -class PixelMapping +class PixelMapper { public: + enum class MappingType_t { + MAPPING_IB, + MAPPING_OB, + MAPPING_UNKNOWN + }; + struct ChipIdentifier { + unsigned int mFEEID; unsigned int mLaneID; unsigned int mChipID; - bool operator==(const ChipIdentifier& other) const { return mLaneID == other.mLaneID && mChipID == other.mChipID; } + bool operator==(const ChipIdentifier& other) const { return mFEEID == other.mFEEID && mLaneID == other.mLaneID && mChipID == other.mChipID; } }; struct ChipPosition { unsigned int mColumn; unsigned int mRow; + unsigned int mLayer; bool mInvertColumn; bool mInvertRow; - bool operator==(const ChipPosition& other) const { return mColumn == other.mColumn && mRow == other.mRow; } + bool operator==(const ChipPosition& other) const { return mLayer == other.mLayer && mColumn == other.mColumn && mRow == other.mRow; } }; struct ChipIdentifierHasher { @@ -48,6 +57,7 @@ class PixelMapping size_t operator()(const ChipIdentifier& s) const { std::size_t seed = 0; + boost::hash_combine(seed, s.mFEEID); boost::hash_combine(seed, s.mChipID); boost::hash_combine(seed, s.mLaneID); return seed; @@ -57,112 +67,79 @@ class PixelMapping class InvalidChipException : public std::exception { public: - InvalidChipException(unsigned int mappingVersion, PixelMapping::ChipIdentifier& identifier) : mMappingVersion(mappingVersion), mIdentifier(identifier), mMessage() + InvalidChipException(PixelMapper::ChipIdentifier& identifier) : mIdentifier(identifier), mMessage() { - mMessage = "Invalid chip identifier for mapping " + std::to_string(mMappingVersion) + ": lane " + std::to_string(mIdentifier.mLaneID) + ", chip " + std::to_string(mIdentifier.mChipID); + mMessage = "Invalid chip identifier: FEE " + std::to_string(mIdentifier.mFEEID) + ", lane " + std::to_string(mIdentifier.mLaneID) + ", chip " + std::to_string(mIdentifier.mChipID); } ~InvalidChipException() noexcept final = default; const char* what() const noexcept final { return mMessage.data(); } - const PixelMapping::ChipIdentifier& getIdentifier() const { return mIdentifier; } + const PixelMapper::ChipIdentifier& getIdentifier() const { return mIdentifier; } unsigned int getLane() const noexcept { return mIdentifier.mLaneID; } unsigned int getChipID() const noexcept { return mIdentifier.mChipID; } - int getMappingVersion() const noexcept { return mMappingVersion; } + unsigned int getFEEID() const noexcept { return mIdentifier.mFEEID; } void print(std::ostream& stream) const; private: - unsigned int mMappingVersion; - PixelMapping::ChipIdentifier mIdentifier; + PixelMapper::ChipIdentifier mIdentifier; std::string mMessage; }; - class VersionException : public std::exception + class UninitException : public std::exception { public: - VersionException(unsigned int version) : mMappingVersion(version), mMessage() {} - ~VersionException() noexcept final = default; + UninitException() = default; + ~UninitException() noexcept final = default; - const char* what() const noexcept final { return mMessage.data(); } - int getMappingVersion() const noexcept { return mMappingVersion; } + const char* what() const noexcept final { return "Mapping is not initalized"; } void print(std::ostream& stream) const; - - private: - unsigned int mMappingVersion; - std::string mMessage; }; - PixelMapping() = default; - PixelMapping(unsigned int version); - virtual ~PixelMapping() = default; - - ChipPosition getPosition(unsigned int laneID, unsigned int chipID) const; - ChipPosition getPosition(const PixelChip& chip) const + class MappingNotSetException : public std::exception { - return getPosition(chip.mLaneID, chip.mChipID); + public: + MappingNotSetException() = default; + ~MappingNotSetException() noexcept final = default; + const char* what() const noexcept final { return "Mapping file not set"; } + void print(std::ostream& stream) const; }; - virtual unsigned int getNumberOfRows() const = 0; - virtual unsigned int getNumberOfColumns() const = 0; - - protected: - int mVersion = -1; - bool mUseLanes = false; - std::unordered_map mMapping; -}; - -class PixelMappingOB : public PixelMapping -{ - public: - PixelMappingOB() = default; - PixelMappingOB(unsigned int version); - ~PixelMappingOB() final = default; - - void init(unsigned int version); - unsigned int getNumberOfRows() const final { return 6; } - unsigned int getNumberOfColumns() const final { return 7; } - - private: - void buildVersion0(); - void buildVersion1(); -}; - -class PixelMappingIB : public PixelMapping -{ - public: - PixelMappingIB() = default; - PixelMappingIB(unsigned int version); - ~PixelMappingIB() final = default; - - void init(unsigned int version); - unsigned int getNumberOfRows() const final { return 6; } - unsigned int getNumberOfColumns() const final { return 3; } - - private: - void buildVersion0(); - void buildVersion1(); -}; - -class PixelMapper -{ - public: - enum class MappingType_t { - MAPPING_IB, - MAPPING_OB - }; PixelMapper(MappingType_t mappingtype); ~PixelMapper() = default; - const PixelMapping& getMapping(unsigned int feeID) const; + ChipPosition getPosition(unsigned int feeID, unsigned int laneID, unsigned int chipID) const; + ChipPosition getPosition(unsigned int feeID, const PixelChip& chip) const + { + return getPosition(feeID, chip.mLaneID, chip.mChipID); + }; + + int getNumberOfColumns() const { return mNumberOfColumns; } + int getNumberOfRows() const { return mNumberOfRows; } MappingType_t getMappingType() const { return mMappingType; } + void setMappingFile(const std::string_view mappingfile, MappingType_t mappingtype) + { + mMappingFile = mappingfile; + mMappingType = mappingtype; + init(); + } + private: - MappingType_t mMappingType; - std::array, 2> mMappings; + void checkInitialized() const; + void init(); + std::unordered_map mMapping; + std::string mMappingFile; + MappingType_t mMappingType = MappingType_t::MAPPING_UNKNOWN; + int mNumberOfColumns = 0; + int mNumberOfRows = 0; + + ClassDefNV(PixelMapper, 1); }; -std::ostream& operator<<(std::ostream& stream, const PixelMapping::InvalidChipException& error); -std::ostream& operator<<(std::ostream& stream, const PixelMapping::VersionException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::InvalidChipException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::UninitException& error); +std::ostream& operator<<(std::ostream& stream, const PixelMapper::MappingNotSetException& error); } // namespace o2::focal -#endif // ALICEO2_FOCAL_PIXELMAPPER_H \ No newline at end of file +#endif // ALICEO2_FOCAL_PixelMapper_H \ No newline at end of file diff --git a/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h b/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h index e021f5e4c81af..e54263f45f830 100644 --- a/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h +++ b/Detectors/FOCAL/reconstruction/src/FOCALReconstructionLinkDef.h @@ -21,9 +21,6 @@ #pragma link C++ class o2::focal::PadDecoder + ; #pragma link C++ class o2::focal::PadMapper + ; #pragma link C++ class o2::focal::PixelMapper + ; -#pragma link C++ class o2::focal::PixelMapping + ; -#pragma link C++ class o2::focal::PixelMappingIB + ; -#pragma link C++ class o2::focal::PixelMappingOB + ; #pragma link C++ class o2::focal::PixelDecoder + ; #pragma link C++ class o2::focal::PixelLaneHandler + ; #pragma link C++ class o2::focal::PixelLanePayload + ; diff --git a/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx b/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx index 20104f1b25d51..68a662a67fa2c 100644 --- a/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx +++ b/Detectors/FOCAL/reconstruction/src/PixelDecoder.cxx @@ -25,7 +25,6 @@ void PixelDecoder::reset() void PixelDecoder::decodeEvent(gsl::span payload) { - o2::InteractionRecord currentIR; bool physicsTrigger = false; mPixelData.clear(); @@ -37,6 +36,9 @@ void PixelDecoder::decodeEvent(gsl::span payload) if (word.isDataTrailer()) { // to be defined } + if (word.isDiagnosticWord()) { + // to be defined + } if (word.isTriggerWord()) { auto lastIR = currentIR; currentIR.orbit = word.orbit; @@ -182,7 +184,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmIdentifier) << 8; decodedChips.push_back({0, laneID, static_cast(chipID), currentChipStatus, hits}); } else { // std::cout << "Skipping existing empty frame" << std::endl; @@ -196,7 +198,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmChipID; activeChip = true; - currentChipStatus = PixelChip::EMPTYFRAME; + currentChipStatus = (chipheader->mIdentifier) << 8; // std::cout << "New chip (" << std::bitset<4>(chipheader->mIdentifier) << ") " << int(chipheader->mChipID) << ", BC (" << int(chipheader->mBunchCrossing) << "), empty " << (chipheader->isEmptyFrame() ? "yes" : "no") << std::endl; LOG(debug) << "New chip (" << std::bitset<4>(chipheader->mIdentifier) << ") " << int(chipheader->mChipID) << ", BC (" << int(chipheader->mBunchCrossing) << "), empty " << (chipheader->isEmptyFrame() ? "yes" : "no"); break; @@ -204,10 +206,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span(currentptr); wordsize = sizeof(PixelWord::ChipTrailer) / sizeof(uint8_t); - if (currentChipStatus & PixelChip::DATAFRAME) { - // remove emptyframe bit in case dataframe bit is found - currentChipStatus &= ~(PixelChip::EMPTYFRAME); - } + currentChipStatus |= trailer->mReadoutFlags; // -> Combine hits to chip // -> Write hits to output container // std::cout << "Finished chip (" << std::bitset<4>(trailer->mIdentifier) << ") " << int(currentChipID) << " with " << hits.size() << " hits .. (Readout flags " << std::bitset<4>(trailer->mReadoutFlags) << ")" << std::endl; @@ -216,7 +215,7 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::spanmHits.size(); std::copy(hits.begin(), hits.end(), std::back_inserter(found->mHits)); - found->mStatusCode |= currentChipStatus; + found->mStatusCode = currentChipStatus; found->removeEmptyframe(); // std::cout << "Merging data with existing chip, Hits before: " << hitsbefore << ", after: " << found->mHits.size() << std::endl; LOG(debug) << "Merging data with existing chip, Hits before: " << hitsbefore << ", after: " << found->mHits.size(); @@ -242,7 +241,6 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span(datashort.mData) << "] word (" << std::bitset<2>(datashort.mIdentifier) << ") with encoder " << std::bitset<4>(datashort.mEncoderID) << " and address " << std::bitset<10>(datashort.mAddress); wordsize = sizeof(PixelWord::DataShort) / sizeof(uint8_t); hits.push_back({AlpideX(currentRegion, datashort.mEncoderID, datashort.mAddress), AlpideY(datashort.mAddress)}); - currentChipStatus |= PixelChip::DATAFRAME; break; } case PixelWord::PixelWordType::DATA_LONG: { @@ -259,16 +257,13 @@ std::vector PixelDecoder::decodeLane(uint8_t laneID, gsl::span #include -#include - -using namespace o2::focal; - -PixelMapping::PixelMapping(unsigned int version) : mVersion(version) {} - -PixelMapping::ChipPosition PixelMapping::getPosition(unsigned int laneID, unsigned int chipID) const -{ - ChipIdentifier identifier{mUseLanes ? laneID : 0, chipID}; - auto found = mMapping.find(identifier); - if (found == mMapping.end()) { - throw InvalidChipException(mVersion, identifier); - } - return found->second; -} +#include -void PixelMapping::InvalidChipException::print(std::ostream& stream) const -{ - stream << mMessage; -} +#include -void PixelMapping::VersionException::print(std::ostream& stream) const -{ - stream << mMessage; -} +#include +#include -PixelMappingOB::PixelMappingOB(unsigned int version) : PixelMapping(version) { init(version); } +using namespace o2::focal; -void PixelMappingOB::init(unsigned int version) +PixelMapper::PixelMapper(PixelMapper::MappingType_t mappingtype) : mMappingType(mappingtype) { - if (version >= 2) { - throw VersionException(version); - } - std::cout << "Initializing OB mapping (" << version << ")" << std::endl; - switch (version) { - case 0: - buildVersion0(); - break; - - case 1: - buildVersion1(); + switch (mappingtype) { + case MappingType_t::MAPPING_IB: + mMappingFile = Form("%s/share/Detectors/FOC/files/mapping_ib.data", gSystem->Getenv("O2_ROOT")); break; - + case MappingType_t::MAPPING_OB: + mMappingFile = Form("%s/share/Detectors/FOC/files/mapping_ob.data", gSystem->Getenv("O2_ROOT")); default: break; + }; + + if (mMappingFile.length()) { + init(); } - mUseLanes = true; } -void PixelMappingOB::buildVersion0() +void PixelMapper::init() { + if (gSystem->AccessPathName(mMappingFile.data())) { + throw MappingNotSetException(); + } + LOG(debug) << "Reading pixel mapping from file: " << mMappingFile; mMapping.clear(); - /** Lane, Chip, Col, Row, Inv Col, Inv Row **/ - mMapping.insert({{8, 6}, {0, 5, true, false}}); - mMapping.insert({{8, 5}, {1, 5, true, false}}); - mMapping.insert({{8, 4}, {2, 5, true, false}}); - mMapping.insert({{8, 3}, {3, 5, true, false}}); - mMapping.insert({{8, 2}, {4, 5, true, false}}); - mMapping.insert({{8, 1}, {5, 5, true, false}}); - mMapping.insert({{8, 0}, {6, 5, true, false}}); - mMapping.insert({{6, 8}, {0, 4, false, true}}); - mMapping.insert({{6, 9}, {1, 4, false, true}}); - mMapping.insert({{6, 10}, {2, 4, false, true}}); - mMapping.insert({{6, 11}, {3, 4, false, true}}); - mMapping.insert({{6, 12}, {4, 4, false, true}}); - mMapping.insert({{6, 13}, {5, 4, false, true}}); - mMapping.insert({{6, 14}, {6, 4, false, true}}); - mMapping.insert({{24, 6}, {0, 1, true, false}}); - mMapping.insert({{24, 5}, {1, 1, true, false}}); - mMapping.insert({{24, 4}, {2, 1, true, false}}); - mMapping.insert({{24, 3}, {3, 1, true, false}}); - mMapping.insert({{24, 2}, {4, 1, true, false}}); - mMapping.insert({{24, 1}, {5, 1, true, false}}); - mMapping.insert({{24, 0}, {6, 1, true, false}}); - mMapping.insert({{22, 8}, {0, 0, false, true}}); - mMapping.insert({{22, 9}, {1, 0, false, true}}); - mMapping.insert({{22, 10}, {2, 0, false, true}}); - mMapping.insert({{22, 11}, {3, 0, false, true}}); - mMapping.insert({{22, 12}, {4, 0, false, true}}); - mMapping.insert({{22, 13}, {5, 0, false, true}}); - mMapping.insert({{22, 14}, {6, 0, false, true}}); + std::ifstream reader(mMappingFile); + std::string buffer; + while (std::getline(reader, buffer)) { + auto delimiter = buffer.find("//"); + std::string data = buffer; + if (delimiter == 0) { + // whole line commented out + continue; + } + if (delimiter != std::string::npos) { + data = buffer.substr(0, delimiter); + } + LOG(debug) << "Processing line: " << data; + std::stringstream decoder(data); + std::string linebuffer; + std::vector identifiers; + while (std::getline(decoder, linebuffer, ',')) { + identifiers.push_back(std::stoi(linebuffer)); + } + if (identifiers.size() < 8) { + LOG(error) << "Chip coordinates not fully defined (" << data << "), skipping ..."; + } + ChipIdentifier nextIdentifier; + nextIdentifier.mFEEID = identifiers[0]; + nextIdentifier.mLaneID = identifiers[1]; + nextIdentifier.mChipID = identifiers[2]; + ChipPosition nextPosition; + nextPosition.mLayer = identifiers[3]; + nextPosition.mColumn = identifiers[4]; + nextPosition.mRow = identifiers[5]; + nextPosition.mInvertColumn = (identifiers[6] == 1 ? true : false); + nextPosition.mInvertRow = (identifiers[7] == 1 ? true : false); + LOG(debug) << "Inserting chip: (FEE " << nextIdentifier.mFEEID << ", Lane " << nextIdentifier.mLaneID << ", Chip " << nextIdentifier.mChipID << ") -> (Layer " << nextPosition.mLayer << ", Col " << nextPosition.mColumn << ", Row " << nextPosition.mRow << ", Inv Col " << (nextPosition.mInvertColumn ? "yes" : "no") << ", Inv Row " << (nextPosition.mInvertRow ? "yes" : "no") << ")"; + if (mMapping.find(nextIdentifier) != mMapping.end()) { + LOG(error) << "Chip with FEE" << nextIdentifier.mFEEID << ", Lane " << nextIdentifier.mLaneID << ", Chip " << nextIdentifier.mChipID << " already present, not overwriting ..."; + continue; + } + mMapping.insert({nextIdentifier, nextPosition}); + if (nextPosition.mRow + 1 > mNumberOfRows) { + mNumberOfRows = nextPosition.mRow + 1; + } + if (nextPosition.mColumn + 1 > mNumberOfColumns) { + mNumberOfColumns = nextPosition.mColumn + 1; + } + } + LOG(info) << "Pixel Mapper: Found " << mMapping.size() << " chips, in " << mNumberOfColumns << " colums and " << mNumberOfRows << " rows"; + reader.close(); } -void PixelMappingOB::buildVersion1() +PixelMapper::ChipPosition PixelMapper::getPosition(unsigned int feeID, unsigned int laneID, unsigned int chipID) const { - mMapping.clear(); - /** Lane, Chip, Col, Row, Inv Col, Inv Row **/ - mMapping.insert({{6, 8}, {0, 3, false, false}}); - mMapping.insert({{6, 9}, {1, 3, false, false}}); - mMapping.insert({{6, 10}, {2, 3, false, false}}); - mMapping.insert({{6, 11}, {3, 3, false, false}}); - mMapping.insert({{6, 12}, {4, 3, false, false}}); - mMapping.insert({{6, 13}, {5, 3, false, false}}); - mMapping.insert({{6, 14}, {6, 3, false, false}}); - mMapping.insert({{8, 6}, {0, 2, true, true}}); - mMapping.insert({{8, 5}, {1, 2, true, true}}); - mMapping.insert({{8, 4}, {2, 2, true, true}}); - mMapping.insert({{8, 3}, {3, 2, true, true}}); - mMapping.insert({{8, 2}, {4, 2, true, true}}); - mMapping.insert({{8, 1}, {5, 2, true, true}}); - mMapping.insert({{8, 0}, {6, 2, true, true}}); + auto cardindex = feeID & 0x00FF; + checkInitialized(); + ChipIdentifier identifier{cardindex, laneID, chipID}; + auto found = mMapping.find(identifier); + if (found == mMapping.end()) { + throw InvalidChipException(identifier); + } + return found->second; } -PixelMappingIB::PixelMappingIB(unsigned int version) : PixelMapping(version) { init(version); } - -void PixelMappingIB::init(unsigned int version) +void PixelMapper::checkInitialized() const { - if (version >= 2) { - throw VersionException(version); - } - std::cout << "Initializing IB mapping (" << version << ")" << std::endl; - switch (version) { - case 0: - buildVersion0(); - break; - - case 1: - buildVersion1(); - break; - - default: - break; + if (!mMapping.size()) { + throw UninitException(); } - mUseLanes = false; } -void PixelMappingIB::buildVersion0() +void PixelMapper::InvalidChipException::print(std::ostream& stream) const { - mMapping.clear(); - mMapping.insert({{0, 0}, {0, 4, false, false}}); - mMapping.insert({{0, 1}, {1, 4, false, false}}); - mMapping.insert({{0, 2}, {2, 4, false, false}}); - mMapping.insert({{0, 3}, {0, 2, false, false}}); - mMapping.insert({{0, 4}, {1, 2, false, false}}); - mMapping.insert({{0, 5}, {2, 2, false, false}}); - mMapping.insert({{0, 6}, {0, 0, false, false}}); - mMapping.insert({{0, 7}, {1, 0, false, false}}); - mMapping.insert({{0, 8}, {2, 0, false, false}}); + stream << mMessage; } -void PixelMappingIB::buildVersion1() +void PixelMapper::UninitException::print(std::ostream& stream) const { - mMapping.clear(); - mMapping.insert({{0, 0}, {0, 5, false, false}}); - mMapping.insert({{0, 1}, {1, 5, false, false}}); - mMapping.insert({{0, 2}, {2, 5, false, false}}); - mMapping.insert({{0, 3}, {0, 3, false, false}}); - mMapping.insert({{0, 4}, {1, 3, false, false}}); - mMapping.insert({{0, 5}, {2, 3, false, false}}); - mMapping.insert({{0, 6}, {0, 1, false, false}}); - mMapping.insert({{0, 7}, {1, 1, false, false}}); - mMapping.insert({{0, 8}, {2, 1, false, false}}); + stream << what(); } -PixelMapper::PixelMapper(PixelMapper::MappingType_t mappingtype) : mMappingType(mappingtype) +void PixelMapper::MappingNotSetException::print(std::ostream& stream) const { - switch (mappingtype) { - case MappingType_t::MAPPING_IB: - for (int iversion = 0; iversion < 2; iversion++) { - mMappings[iversion] = std::make_shared(iversion); - } - break; - case MappingType_t::MAPPING_OB: - for (int iversion = 0; iversion < 2; iversion++) { - mMappings[iversion] = std::make_shared(iversion); - } - }; + stream << what(); } -const PixelMapping& PixelMapper::getMapping(unsigned int feeID) const +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::InvalidChipException& error) { - return *(mMappings[feeID % 2]); + error.print(stream); + return stream; } -std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapping::InvalidChipException& error) +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::UninitException& error) { error.print(stream); return stream; } -std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapping::VersionException& error) +std::ostream& o2::focal::operator<<(std::ostream& stream, const PixelMapper::MappingNotSetException& error) { error.print(stream); return stream; -} +} \ No newline at end of file diff --git a/Detectors/FOCAL/simulation/CMakeLists.txt b/Detectors/FOCAL/simulation/CMakeLists.txt new file mode 100644 index 0000000000000..b838f563a2e0d --- /dev/null +++ b/Detectors/FOCAL/simulation/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(FOCALSimulation + SOURCES src/Detector.cxx + PUBLIC_LINK_LIBRARIES ROOT::TreePlayer O2::FOCALBase O2::DetectorsBase O2::SimConfig O2::SimulationDataFormat O2::Headers O2::DataFormatsFOCAL + AliceO2::InfoLogger + Microsoft.GSL::GSL) + +o2_target_root_dictionary( + FOCALSimulation + HEADERS include/FOCALSimulation/Detector.h +) + +o2_data_file(COPY geometryFiles DESTINATION Detectors/Geometry/FOC/) +o2_data_file(COPY data DESTINATION Detectors/FOC/simulation) diff --git a/Detectors/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat new file mode 100644 index 0000000000000..870e38182f01c --- /dev/null +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -0,0 +1,18 @@ +* FOCAL +* ==== +* +* Med GAM ELEC NHAD CHAD MUON EBREM MUHAB EDEL MUDEL MUPA ANNI BREM COMP DCAY DRAY HADR LOSS MULS PAIR PHOT RAYL STRA +* Tungsten +FOC 0 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Si sensor +FOC 1 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* Si pixel +FOC 2 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 1.e-5 -1. -1 -1 -1 -1 1 -1 1 -1 -1 -1 -1 -1 +* G10 plate +FOC 3 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Alloy +FOC 6 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Aluminium +FOC 11 5.e-5 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 +* Air +FOC 13 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 1.e-4 -1. -1 -1 -1 -1 1 -1 3 -1 -1 -1 -1 -1 diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry.txt new file mode 100644 index 0000000000000..a0508ab16e408 --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry.txt @@ -0,0 +1,86 @@ +# Pad layer materials updated to match the setup used in test beams. Total thickness is 8.5mm +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S0 Pb 0 0 2.5 2.5 3.0 + COMPOSITION_HCAL_S1 Scint 0 0 2.5 2.5 0.2 + COMMAND_NUMBER_OF_HCAL_LAYERS 34 + COMMAND_NUMBER_OF_SEGMENTS 54 +# Pixel layers total thickness is 8.5mm as in test beam simulation. +# TODO: change G10 material to Al and remove copper ground (requires changes in AliFOCALv2.cxx) +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 762.9 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_TOWER_SIZE 2.5 + GLOBAL_HCAL_TOWER_NY 40 + GLOBAL_HCAL_TOWER_NX 38 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 53 2.5 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt new file mode 100644 index 0000000000000..358fcef2cd29d --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Sheets.txt @@ -0,0 +1,86 @@ +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S0 CuHCAL 0 0 49.81 0.20 110 + COMPOSITION_HCAL_S1 Scint 0 0 0.1 0.1 110 + COMPOSITION_HCAL_S2 CuHCAL 0 0 49.81 0.15 110 + COMMAND_NUMBER_OF_HCAL_LAYERS 1 + COMMAND_NUMBER_OF_SEGMENTS 21 +# Strip sectors +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 764.47 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_PITCH_SIZE 0.4 + GLOBAL_HCAL_TOWER_NY 72 + GLOBAL_HCAL_TOWER_NX 62 + GLOBAL_HCAL_BEAMPIPE 8.4 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 20 0.4 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt new file mode 100644 index 0000000000000..31f6940224337 --- /dev/null +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt @@ -0,0 +1,88 @@ +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PAD_S0 Alloy 0 0 9. 8. 0.35 + COMPOSITION_PAD_S1 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S2 SiPad 0 0 9. 8. 0.03 + COMPOSITION_PAD_S3 G10 0 0 9. 8. 0.08 + COMPOSITION_PAD_S4 Cu 0 0 9. 8. 0.014 + COMPOSITION_PAD_S5 Air 0 0 9. 8. 0.296 + # Replica of above pad layers to 50 layers + COMMAND_NUMBER_OF_PAD_LAYERS 18 +# HCAL layers +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_HCAL_S1 Scint 0 0 0.1 0.1 110 + COMPOSITION_HCAL_S0 CuHCAL 0 0 0.25 0.25 110 + COMMAND_NUMBER_OF_HCAL_LAYERS 1 + COMMAND_NUMBER_OF_SEGMENTS 21 +# Strip sectors +# Volume Name Material C-X C-Y X Y Z + COMPOSITION_PIX_S0 Alloy 0 0 3.0 2.74 0.35 + COMPOSITION_PIX_S1 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S2 Si 0 0 3.0 2.74 0.047 + COMPOSITION_PIX_S3 SiPix 0 0 3.0 2.74 0.003 + COMPOSITION_PIX_S4 G10 0 0 3.0 2.74 0.1 + COMPOSITION_PIX_S5 Cu 0 0 3.0 2.74 0.001 + COMPOSITION_PIX_S6 Air 0 0 3.0 2.74 0.249 +COMMAND_INSERT_PIX_AT_L4 +COMMAND_INSERT_PIX_AT_L9 +# COMMAND_INSERT_STR_AT_L4 +# COMMAND_INSERT_STR_AT_L6 +# COMMAND_INSERT_STR_AT_L8 +#Front Matter definition + COMPOSITION_FM_S0 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S1 SiStripX 0 0 5. 5. 0.05 + COMPOSITION_FM_S2 G10 0 0 5. 5. 0.05 + COMPOSITION_FM_S3 Air 0 0 5. 5. 0.01 + COMPOSITION_FM_S4 G10 0 0 5. 5. 0.01 + COMPOSITION_FM_S5 SiStripY 0 0 5. 5. 0.05 + COMPOSITION_FM_S6 G10 0 0 5. 5. 0.05 +# PIXEL readout + GLOBAL_PIX_NX 15 + GLOBAL_PIX_NY 3 + GLOBAL_PIX_OffsetX 1.2 + GLOBAL_PIX_OffsetY 0.09 + GLOBAL_PIX_SKIN 0.004 +# pxel size in cm + COMMAND_PIXEL_READOUT_ON 0.005 +# Pad information + GLOBAL_PAD_SIZE_X_Y 1 + GLOBAL_PAD_NX_NY 8 + GLOBAL_PAD_NX 9 + GLOBAL_PAD_NY 8 + GLOBAL_PAD_PPTOL 0. + GLOBAL_PAD_SKIN 0.2 +# Global information (TOL:1cm of T-T space filled with "AIR") + GLOBAL_PAD_SUPERMODULE_X 5 + GLOBAL_PAD_SUPERMODULE_Y 1 + GLOBAL_SUPERMODULE_TOLX 0. Air + GLOBAL_SUPERMODULE_TOLY 0. Air + GLOBAL_TOWER_TOL 0. Air + GLOBAL_TOWER_TOLX 0.02 Air + GLOBAL_TOWER_TOLY 0.8 Al + GLOBAL_FOCAL_Z 763.5 +# Open the detector on the right and left in cm, +# can only work if the GLOBAL_HCAL_TOWER_NY is odd number and GLOBAL_HCAL_TOWER_NX is even number +# GLOBAL_DetectorOpen_Right 5 +# GLOBAL_DetectorOpen_Left 5 + GLOBAL_Tower_NX 2 + GLOBAL_Tower_NY 11 + GLOBAL_MIDDLE_TOWER_OFFSET 5 + GLOBAL_NSTRIPS 128 + GLOBAL_STRIPSIZE_LONG 9.0 + GLOBAL_STRIPSIZE_WIDTH 0.07 + GLOBAL_HCAL_TOWER_SIZE 6.55 + GLOBAL_HCAL_TOWER_NY 15 + GLOBAL_HCAL_TOWER_NX 15 +# COMMAND_INSERT_FRONT_PAD_LAYERS +# COMMAND_INSERT_HCAL_READOUT +# New VIRTUAL settings + VIRTUAL_N_SEGMENTS 7 + # N Start End PadSize RelThickness IsPixel PixelTreshold [eV] + # Layer Layer + VIRTUAL_SEGMENT_LAYOUT_N0 0 3 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N1 4 4 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N2 5 8 1.0 1.0 0 300000 + VIRTUAL_SEGMENT_LAYOUT_N3 9 9 0.05 1.0 1 4000 + VIRTUAL_SEGMENT_LAYOUT_N4 10 14 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N5 15 19 1.0 1.0 0 375000 + VIRTUAL_SEGMENT_LAYOUT_N6 20 20 0.25 1.0 2 5000 +#EOF diff --git a/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h new file mode 100644 index 0000000000000..5f6bed3a037b0 --- /dev/null +++ b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h @@ -0,0 +1,240 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_FOCAL_DETECTOR_H_ +#define ALICEO2_FOCAL_DETECTOR_H_ + +#include + +#include "DetectorsBase/Detector.h" +#include "FOCALBase/Hit.h" +#include "FOCALBase/Geometry.h" +#include "TGeoManager.h" + +class FairVolume; + +namespace o2::focal +{ + +class Hit; +class Geometry; + +/// \struct Parent +/// \brief Information about superparent (particle entering any FOCAL volume) +/// \ingroup FOCALsimulation +struct Parent { + int mPDG; ///< PDG code + double mEnergy; ///< Total energy + bool mHasTrackReference; ///< Flag indicating whether parent has a track reference +}; + +/// \class Detector +/// \brief FOCAL detector simulation +/// \ingroup FOCALsimulation +/// \author Markus Fasel , Oak Ridge National Laboratory +/// \since June 6, 2024 +/// \author Adam Matyja , Institute of Nuclear Physics, PAN, Cracov, Poland +/// \since June 24, 2024 +/// class based on AliFOCALv2.h in aliroot +/// It builds the ECAL (Adam) and HCAL (Hadi) seperately +/// For the ECAL: it builds it tower by tower +/// For the HCAL: +/// +/// The detector class handles the implementation of the FOCAL detector +/// within the virtual Monte-Carlo framework and the simulation of the +/// FOCAL detector up to hit generation +class Detector : public o2::base::DetImpl +{ + public: + enum MediumType_t { ID_TUNGSTEN = 0, + ID_SIPAD = 1, + ID_SIPIX = 2, + ID_G10 = 3, + ID_COPPER = 4, + ID_STEEL = 5, + ID_ALLOY = 6, + ID_CERAMIC = 7, + ID_PB = 8, + ID_SC = 9, + ID_SIINSENS = 10, + ID_ALUMINIUM = 11, + ID_VAC = 12, + ID_AIR = 13 }; + /// \brief Dummy constructor + Detector() = default; + + /// \brief Main constructor + /// + /// \param isActive Switch whether detector is active in simulation + Detector(Bool_t isActive, std::string geofilename = "default"); + + /// \brief Destructor + ~Detector() override; + + /// \brief Initializing detector + void InitializeO2Detector() override; + + /// \brief Processing hit creation in the FOCAL sensitive volume + /// \param v Current sensitive volume + Bool_t ProcessHits(FairVolume* v = nullptr) final; + + /// \brief Add FOCAL hit + /// \param trackID Index of the track in the MC stack + /// \param primary Index of the primary particle in the MC stack + /// \param initialEnergy Energy of the particle entering the FOCAL + /// \param detID Index of the detector (cell) for which the hit is created + /// \param pos Position vector of the particle at the hit + /// \param mom Momentum vector of the particle at the hit + /// \param time Time of the hit + /// \param energyloss Energy deposit in FOCAL + /// \return Pointer to the current hit + /// + /// Internally adding hits coming from the same track + Hit* AddHit(int trackID, int primary, double initialEnergy, int detID, o2::focal::Hit::Subsystem_t subsystem, + const math_utils::Point3D& pos, double time, double energyloss); + + /// \brief register container with hits + void Register() override; + + /// \brief Get access to the hits + /// \return Hit collection + std::vector* getHits(Int_t iColl) const + { + if (iColl == 0) { + return mHits; + } + return nullptr; + } + + /// \brief Clean point collection + void Reset() final; + + /// \brief Steps to be carried out at the end of the event + /// + /// For FOCAL cleaning the hit collection and the lookup table + void EndOfEvent() final; + + /// \brief Begin primaray + /// + /// Caching current primary ID and set current parent ID to the + /// current primary ID + void BeginPrimary() override; + + /// \brief Finish current primary + /// + /// Reset caches for current primary, current parent and current cell + void FinishPrimary() override; + + /// \brief Get the FOCAL geometry desciption + /// \return Access to the FOCAL Geometry description + /// + /// Will be created the first time the function is called + Geometry* getGeometry(std::string name = ""); + + /// \brief Try to find hit with same cell and parent track ID + /// \param parentID ID of the parent track + /// \param col Column of the cell + /// \param row Row of the cell + /// \param Layer Layer of cell + Hit* FindHit(int parentID, int col, int row, int layer); + + protected: + /// \brief Creating detector materials for the FOCAL detector + void CreateMaterials(); + + virtual void addAlignableVolumes() const override; + void addAlignableVolumesHCAL() const; + void addAlignableVolumesECAL() const; + + void ConstructGeometry() override; + + virtual void CreateHCALSpaghetti(); + virtual void CreateHCALSandwich(); + virtual void CreateHCALSheets(); + + TGeoVolumeAssembly* CreatePitchAssembly(double Lx = 498.1, + double Ly1 = 2.0, + double Ly2 = 1.5, + double Lz = 1100.0, + double hole_diameter = 1.1, + double hole_spacing = 4.0, + int nholes = 124, + double fiber_radius = 0.5, + std::string suffix = ""); + + /// \brief Generate ECAL geometry + void CreateECALGeometry(); + + /// \brief Add new superparent to the container + /// \param trackID Track ID of the superparent + /// \param pdg PDG code of the superparent + /// \param energy Energy of the superparent + Parent* AddSuperparent(int trackID, int pdg, double energy); + + /// \brief Processing hit creation in the ECAL Pad sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsEPad(FairVolume* v = nullptr); + + /// \brief Processing hit creation in the ECAL Pixel sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsEPix(FairVolume* v = nullptr); + + /// \brief Processing hit creation in the HCAL sensitive volume + /// \param v Current sensitive volume + bool ProcessHitsHCAL(FairVolume* v = nullptr); + + private: + /// \brief Copy constructor (used in MT) + Detector(const Detector& rhs); + + Geometry* mGeometry; //! mGeoCompositions; //!* mHits; ///< Container with hits + std::unordered_map mHitIndexMapping; ///< Mapping the hits to a cell in the detector + + std::unordered_map mSuperParentsIndices; //! mSuperParents; //! mSensitive; //! + friend class o2::base::DetImpl; + ClassDefOverride(Detector, 1); +}; +} // namespace o2::focal + +#ifdef USESHM +namespace o2 +{ +namespace base +{ +template <> +struct UseShm { + static constexpr bool value = true; +}; +} // namespace base +} // namespace o2 +#endif + +#endif // ALICEO2_FOCAL_DETECTOR_H_ diff --git a/Detectors/FOCAL/simulation/src/Detector.cxx b/Detectors/FOCAL/simulation/src/Detector.cxx new file mode 100644 index 0000000000000..464e57c07e676 --- /dev/null +++ b/Detectors/FOCAL/simulation/src/Detector.cxx @@ -0,0 +1,1438 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include + +#include + +#include "DetectorsBase/Stack.h" +#include "FOCALSimulation/Detector.h" +#include "FOCALBase/Geometry.h" +#include "FOCALBase/Hit.h" + +using namespace o2::focal; + +Detector::Detector(bool active, std::string geofilename) + : o2::base::DetImpl("FOC", active), + mHits(o2::utils::createSimVector()), + mHitIndexMapping(), + mGeometry(nullptr), + mMedSensHCal(-1), + mMedSensECalPad(-1), + mMedSensECalPix(-1), + mGeoCompositions(), + mSuperParentsIndices(), + mSuperParents(), + mCurrentSuperparent(nullptr), + mCurrentTrack(-1), + mCurrentPrimaryID(-1), + mCurrentParentID(-1), + mVolumeIDScintillator(-1) +{ + mGeometry = getGeometry(geofilename); + if (!mGeometry) { + LOG(fatal) << "Geometry is nullptr"; + } +} + +Detector::Detector(const Detector& rhs) + : o2::base::DetImpl(rhs) +{ + mGeometry = rhs.mGeometry; + mMedSensHCal = rhs.mMedSensHCal; + mMedSensECalPad = rhs.mMedSensECalPad; + mMedSensECalPix = rhs.mMedSensECalPix; + mSensitive = rhs.mSensitive; + // mSensitiveHCAL = rhs.mSensitiveHCAL; + // mSensitiveECALPad = rhs.mSensitiveECALPad; + // mSensitiveECALPix = rhs.mSensitiveECALPix; + mVolumeIDScintillator = rhs.mVolumeIDScintillator; +} + +Detector::~Detector() +{ + o2::utils::freeSimVector(mHits); +} + +Geometry* Detector::getGeometry(std::string name) +{ + if (!mGeometry) { + mGeometry = Geometry::getInstance(name); + } + if (!mGeometry) { + LOG(error) << "Failure accessing geometry"; + } + return mGeometry; +} + +void Detector::InitializeO2Detector() +{ + + LOG(info) << "Intializing FOCAL detector"; + + // All FOCAL volumes must be declared as sensitive, otherwise + // the decay chains are broken by volumes not processed in ProceeHits + for (const auto& child : mSensitive) { + LOG(debug1) << "Adding sensitive volume " << child; + auto svolID = registerSensitiveVolumeAndGetVolID(child); + // HCAL + if (child == "ScintFiber" || child == "HScint") { + LOG(debug1) << "Adding ScintFiber/HScint volume as sensitive volume with ID " << svolID; + mVolumeIDScintillator = svolID; + } + // ECAL Pads + else if (child == "EMSC1" || child == "EMSC2") { + LOG(debug1) << "Adding EMC SILICON volume as sensitive volume with ID " << svolID; + mVolumeIDScintillator = svolID; + } + } + + mMedSensHCal = getMediumID(ID_SC); + mMedSensECalPad = getMediumID(ID_SIPAD); + mMedSensECalPix = getMediumID(ID_SIPIX); +} + +Bool_t Detector::ProcessHits(FairVolume* v) +{ + int track = fMC->GetStack()->GetCurrentTrackNumber(), + directparent = fMC->GetStack()->GetCurrentParentTrackNumber(); + // Like other calorimeters FOCAL will create a huge amount of shower particles during tracking + // Instead, the hits should be assigned to the incoming particle in FOCAL. + // Implementation of the incoming particle search taken from implementation in EMCAL. + if (track != mCurrentTrack) { + LOG(debug4) << "Doing new track " << track << " current (" << mCurrentTrack << "), direct parent (" << directparent << ")"; + // new current track - check parentage + auto hasSuperParent = mSuperParentsIndices.find(directparent); + if (hasSuperParent != mSuperParentsIndices.end()) { + // same superparent as direct parent + mCurrentParentID = hasSuperParent->second; + mSuperParentsIndices[track] = hasSuperParent->second; + auto superparent = mSuperParents.find(mCurrentParentID); + if (superparent != mSuperParents.end()) { + mCurrentSuperparent = &(superparent->second); + } else { + LOG(error) << "Attention: No superparent object found (parent " << mCurrentParentID << ")"; + mCurrentSuperparent = nullptr; + } + LOG(debug4) << "Found superparent " << mCurrentParentID; + } else { + // start of new chain + // for new incoming tracks the super parent index is equal to the track ID (for recursion) + mSuperParentsIndices[track] = track; + mCurrentSuperparent = AddSuperparent(track, fMC->TrackPid(), fMC->Etot()); + mCurrentParentID = track; + } + mCurrentTrack = track; + } + + // Processing HCAL hits + bool flagHCAL = true; + if (fMC->CurrentMedium() == mMedSensHCal) { + flagHCAL = ProcessHitsHCAL(v); + } + + // Processing ECAL Pad hits + bool flagECALPad = true; + if (TVirtualMC::GetMC()->CurrentMedium() == mMedSensECalPad) { + flagECALPad = ProcessHitsEPad(v); + } + + // Processing ECAL Pixel hits + bool flagECALPix = true; + if (fMC->CurrentMedium() == mMedSensECalPix) { + flagECALPix = ProcessHitsEPix(v); + } + + return (flagHCAL || flagECALPad || flagECALPix); + // return true; +} + +Hit* Detector::AddHit(int trackID, int primary, double initialEnergy, int detID, o2::focal::Hit::Subsystem_t subsystem, + const math_utils::Point3D& pos, double time, double eLoss) +{ + LOG(debug3) << "Adding hit for track " << trackID << " with position (" << pos.X() << ", " + << pos.Y() << ", " << pos.Z() << ") with energy " << initialEnergy << " loosing " << eLoss; + mHits->emplace_back(primary, trackID, detID, subsystem, initialEnergy, pos, time, eLoss); + + auto [isin, col, row, layer, segment] = mGeometry->getVirtualInfo(pos.X(), pos.Y(), pos.Z()); + mHitIndexMapping.insert(std::pair({trackID, uint8_t(row), uint8_t(col), uint8_t(layer)}, static_cast(mHits->size() - 1))); + + return &(mHits->back()); +} + +Hit* Detector::FindHit(int parentID, int col, int row, int layer) +{ + Hit::HitID hitToFind{parentID, uint8_t(row), uint8_t(col), uint8_t(layer)}; + auto found = mHitIndexMapping.find(hitToFind); + + if (found == mHitIndexMapping.end()) { + return nullptr; + } + + return &((*mHits)[found->second]); +} + +Parent* Detector::AddSuperparent(int trackID, int pdg, double energy) +{ + LOG(debug3) << "Adding superparent for track " << trackID << " with PID " << pdg << " and energy " << energy; + auto entry = mSuperParents.insert({trackID, {pdg, energy, false}}); + return &(entry.first->second); +} + +void Detector::EndOfEvent() { Reset(); } + +void Detector::Register() +{ + FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, kTRUE); +} + +void Detector::Reset() +{ + LOG(debug) << "Cleaning FOCAL hits ..."; + if (!o2::utils::ShmManager::Instance().isOperational()) { + mHits->clear(); + } + mHitIndexMapping.clear(); + + mSuperParentsIndices.clear(); + mSuperParents.clear(); + mCurrentTrack = -1; + mCurrentParentID = -1; +} + +void Detector::CreateMaterials() +{ + + // --- Define the various materials for GEANT --- + + /// Silicon + float aSi = 28.09; + float zSi = 14.0; + float dSi = 2.33; + float x0Si = 9.36; + Material(1, "Si $", aSi, zSi, dSi, x0Si, 18.5); + + //// W Tungsten + float aW = 183.84; + float zW = 74.0; + float dW = 19.3; + float x0W = 0.35; + Material(0, "W $", aW, zW, dW, x0W, 17.1); + + // Cu + Material(3, "Cu $", 63.54, 29., 8.96, 1.43, 15.); + + // Al + Material(9, "Al$", 26.98, 13.0, 2.7, 8.9, 37.2); + + //// Pb + Material(10, "Pb $", 207.19, 82., 11.35, .56, 18.5); + + //// Scintillator (copied from EMCal) + // --- The polysterene scintillator (CH) --- + float aP[2] = {12.011, 1.00794}; + float zP[2] = {6.0, 1.0}; + float wP[2] = {1.0, 1.0}; + float dP = 1.032; + Mixture(11, "Polystyrene$", aP, zP, dP, -2, wP); + + // G10 + + float aG10[4] = {1., 12.011, 15.9994, 28.086}; + float zG10[4] = {1., 6., 8., 14.}; + // PH float wG10[4]={0.148648649,0.104054054,0.483499056,0.241666667}; + float wG10[4] = {0.15201, 0.10641, 0.49444, 0.24714}; + Mixture(2, "G10 $", aG10, zG10, 1.7, 4, wG10); + + //// 94W-4Ni-2Cu + float aAlloy[3] = {183.84, 58.6934, 63.54}; + float zAlloy[3] = {74.0, 28, 29}; + float wAlloy[3] = {0.94, 0.04, 0.02}; + float dAlloy = wAlloy[0] * 19.3 + wAlloy[1] * 8.908 + wAlloy[2] * 8.96; + Mixture(5, "Alloy $", aAlloy, zAlloy, dAlloy, 3, wAlloy); + + // Steel + float aSteel[4] = {55.847, 51.9961, 58.6934, 28.0855}; + float zSteel[4] = {26., 24., 28., 14.}; + float wSteel[4] = {.715, .18, .1, .005}; + float dSteel = 7.88; + Mixture(4, "STAINLESS STEEL$", aSteel, zSteel, dSteel, 4, wSteel); + + // Air + float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + float zAir[4] = {6., 7., 8., 18.}; + float wAir[4] = {0.000124, 0.755268, 0.231781, 0.012827}; + float dAir1 = 1.20479E-10; + float dAir = 1.20479E-3; + Mixture(98, "Vacum$", aAir, zAir, dAir1, 4, wAir); + Mixture(99, "Air $", aAir, zAir, dAir, 4, wAir); + + // Ceramic + // Ceramic 97.2% Al2O3 , 2.8% SiO2 + // float wcer[2]={0.972,0.028}; // Not used + float aal2o3[2] = {26.981539, 15.9994}; + float zal2o3[2] = {13., 8.}; + float wal2o3[2] = {2., 3.}; + float denscer = 3.6; + // SiO2 + float aglass[2] = {28.0855, 15.9994}; + float zglass[2] = {14., 8.}; + float wglass[2] = {1., 2.}; + float dglass = 2.65; + Mixture(6, "Al2O3 $", aal2o3, zal2o3, denscer, -2, wal2o3); + Mixture(7, "glass $", aglass, zglass, dglass, -2, wglass); + + // Ceramic is a mixtur of glass and Al2O3 ? + // Not clear how to do this with AliMixture + // Not needed; so skip for now + + /* +float acer[2],zcer[2]; +char namate[21]=""; +float a,z,d,radl,absl,buf[1]; +Int_t nbuf; +fMC->Gfmate((*fIdmate)[6], namate, a, z, d, radl, absl, buf, nbuf); +acer[0]=a; +zcer[0]=z; +fMC->Gfmate((*fIdmate)[7], namate, a, z, d, radl, absl, buf, nbuf); +acer[1]=a; +zcer[1]=z; + +AliMixture( 8, "Ceramic $", acer, zcer, denscer, 2, wcer); +*/ + + // Use Al2O3 instead: + + Mixture(8, "Ceramic $", aal2o3, zal2o3, denscer, -2, wal2o3); + + // Define tracking media + // format + + float tmaxfdSi = 10.0; // 0.1; // .10000E+01; // Degree + float stemaxSi = 0.1; // .10000E+01; // cm + float deemaxSi = 0.1; // 0.30000E-02; // Fraction of particle's energy 0 idtmed[3599]; + Medium(ID_TUNGSTEN, "W conv.$", 0, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + /// Si plate -> idtmed[3600]; + Medium(ID_SIPAD, "Si sens pad$", 1, 0, + isxfld, sxmgmx, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi, nullptr, 0); + /// Si plate + Medium(ID_SIPIX, "Si sens pix$", 1, 0, + isxfld, sxmgmx, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi, nullptr, 0); + + //// G10 plate -> idtmed[3601]; + Medium(ID_G10, "G10 plate$", 2, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.01, nullptr, 0); + + //// Cu plate --> idtmed[3602]; + Medium(ID_COPPER, "Cu$", 3, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.0001, nullptr, 0); + + //// S steel --> idtmed[3603]; + Medium(ID_STEEL, "S steel$", 4, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.0001, nullptr, 0); + + //// Alloy --> idtmed[3604]; + Medium(ID_ALLOY, "Alloy conv.$", 5, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + + //// Ceramic --> idtmed[3607] + Medium(ID_CERAMIC, "Ceramic$", 8, 0, + isxfld, sxmgmx, 10.0, 0.01, 0.1, 0.003, 0.003, nullptr, 0); + + // HCAL materials // Need to double-check tracking pars for this + /// Pb plate --> idtmed[3608] + Medium(ID_PB, "Pb // The Scintillator must be first in order in vector for Rin to be set$", 10, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + /// Scintillator --> idtmed[3609] + Medium(ID_SC, "Scint$", 11, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.001, 0.001, nullptr, 0); + + /// Si plate -> idtmed[3610]; + Medium(ID_SIINSENS, "Si insens$", 1, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, epsil, 0.001, nullptr, 0); + + // Al for the cold plates + Medium(ID_ALUMINIUM, "Aluminium$", 9, 0, + isxfld, sxmgmx, 10.0, 0.1, 0.1, 0.1, 0.1, nullptr, 0); + + /// idtmed[3697] + Medium(ID_VAC, "Vacuum $", 98, 0, + isxfld, sxmgmx, 10.0, 1.0, 0.1, 0.1, 1.0, nullptr, 0); + + /// idtmed[3698] + Medium(ID_AIR, "Air gaps$", 99, 0, + isxfld, sxmgmx, 10.0, 1.0, 0.1, epsil, 0.001, nullptr, 0); +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumes() const +{ + // Create entries for alignable volumes associating the symbolic volume + // name with the corresponding volume path. Needs to be syncronized with + // eventual changes in the geometry + // Alignable volumes are: + + addAlignableVolumesECAL(); + addAlignableVolumesHCAL(); +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumesHCAL() const +{ + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/HCAL_1"; + const std::string snsector = "FOCAL/HCAL"; + + if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { + LOG(fatal) << "Alignable entry " << snsector << " not created. Volume path " << vpsector << "not valid."; + } +} + +//____________________________________________________________________________ +void Detector::addAlignableVolumesECAL() const +{ + const std::string vpsector = "/cave_1/barrel_1/FOCAL_1/ECAL_1"; + const std::string snsector = "FOCAL/ECAL"; + + if (!gGeoManager->SetAlignableEntry(snsector.c_str(), vpsector.c_str())) { + LOG(fatal) << "Alignable entry " << snsector << " not created. Volume path " << vpsector << "not valid."; + } +} + +void Detector::ConstructGeometry() +{ + + //// new geometry genetation + //// The FOCAL Geometry has std::vector of FOCAL Composition + //// This Composition knows + ///// 1. What is the material? + //// 2. Layer + //// 3. Stack + //// 4. center x (in local frame of layer and wafer) + //// 5. center y (in local frame of layer and wafer) + //// 6. center z (in local frame of layer and wafer) + //// 7. size of x, y, z + + LOG(debug) << "Creating FOCAL geometry\n"; + + CreateMaterials(); + + /// -1 means get all the material object + mGeoCompositions = mGeometry->getFOCALMicroModule(-1); + if (!mGeoCompositions.size()) { + LOG(error) << "FOCAL compositions not found!!"; + return; + } + + float pars[4]; + pars[0] = (mGeometry->getFOCALSizeX() + 2 * mGeometry->getMiddleTowerOffset() + mGeometry->getDetectorOpeningRight() + mGeometry->getDetectorOpeningLeft()) / 2; + pars[1] = mGeometry->getFOCALSizeY() / 2; + pars[2] = mGeometry->getFOCALSizeZ() / 2; + // Add space to place 2 SiPad layers in front of ECAL + // The global position of ECAL and HCAL remains the same, but the FOCAL box needs to be slightly larger to accomodate + // the 2 SiPad layers which will sit at z=698 and 699cm (2 and 1 cm in front of ECAL) + if (mGeometry->getInsertFrontPadLayers()) { + pars[2] += 1.0; + } + if (mGeometry->getInsertHCalReadoutMaterial()) { + pars[2] += (1.0 + 0.5); // place Aluminium 1cm thick box (0.5 means half) at 2cm behind HCal to simulate SiPM readout material + pars[1] += (10.0 + 1.0); // place Aluminium 1cm thick box at 10 cm below FOCAL + } + pars[3] = 0; + + LOG(info) << "Creating FOCAL with dimensions X: " << (mGeometry->getFOCALSizeX() + 2 * mGeometry->getMiddleTowerOffset()) << ", Y: " + << mGeometry->getFOCALSizeY() << ", Z: " << mGeometry->getFOCALSizeZ() + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 3.0 : 0.0); + + TVirtualMC::GetMC()->Gsvolu("FOCAL", "BOX", getMediumID(ID_AIR), pars, 4); + mSensitive.push_back("FOCAL"); + // mSensitiveHCAL.push_back("FOCAL"); + // mSensitiveECALPad.push_back("FOCAL"); + // mSensitiveECALPix.push_back("FOCAL"); + + // ECAL part + LOG(debug2) << "ECAL geometry : " << GetTitle(); + CreateECALGeometry(); + + // HCAL part + switch (mGeometry->getHCALDesign()) { + case Geometry::HCALDesgin::Sandwich: + CreateHCALSandwich(); + break; + + case Geometry::HCALDesgin::Spaghetti: + CreateHCALSpaghetti(); + break; + + case Geometry::HCALDesgin::Sheets: + CreateHCALSheets(); + break; + + default: + break; + } + + // const float z0 = 1312.5; // center of barrel mother volume + TVirtualMC::GetMC()->Gspos("FOCAL", 1, "barrel", 0, 30., mGeometry->getFOCALZ0() - (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +void Detector::CreateHCALSpaghetti() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + TGeoVolumeAssembly* HcalTube = gGeoManager->MakeVolumeAssembly("ScintCuTubes"); + + TGeoVolume* volCuTube; + TGeoVolume* volSciFi; + + float RScint = 0.; + float Rin = 0.; + float Rout = 0.; + float Length = 0.; + + for (auto& icomp : mGeoCompositions) { + Length = icomp->sizeZ() / 2; + + if (icomp->material() == "Pb") { + Rout = icomp->sizeX() / 2; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_PB)); + volCuTube = gGeoManager->MakeTube("Tube", medium, Rin, Rout, Length); // The Scintillator must be first in order in vector for Rin to be set + volCuTube->SetLineWidth(2); + volCuTube->SetLineColor(kRed); + mSensitive.push_back(volCuTube->GetName()); + // mSensitiveHCAL.push_back(volCuTube->GetName()); + HcalTube->AddNode(volCuTube, 1, nullptr); + } + if (icomp->material() == "Scint") { + RScint = icomp->sizeX() / 2; + Rin = RScint + 0.005; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_SC)); + volSciFi = gGeoManager->MakeTube("ScintFiber", medium, 0., RScint, Length); + volSciFi->SetLineWidth(2); + volSciFi->SetLineColor(kBlue); + // mSensitiveHCAL.push_back(volSciFi->GetName()); + mSensitive.push_back(volSciFi->GetName()); + HcalTube->AddNode(volSciFi, 1, nullptr); + } + if (icomp->material() == "CuHCAL") { + Rout = icomp->sizeX() / 2; + TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + volCuTube = gGeoManager->MakeTube("Tube", medium, Rin, Rout, Length); // The Scintillator must be first in order in vector for Rin to be set + volCuTube->SetLineWidth(2); + volCuTube->SetLineColor(kRed); + // mSensitiveHCAL.push_back(volCuTube->GetName()); + mSensitive.push_back(volCuTube->GetName()); + HcalTube->AddNode(volCuTube, 1, nullptr); + } + } + + bool splitDet = mGeometry->getDetectorOpeningRight() > 0.0 || mGeometry->getDetectorOpeningLeft() > 0.0; + + double TowerSize = mGeometry->getHCALTowerSize(); + double CuBoxThickness = 0.3; // Thickness of the Cu box carrying capillary tubes + + TGeoBBox* ODBox = new TGeoBBox("TowerOD", TowerSize / 2, TowerSize / 2, Length); + TGeoBBox* IDBox = new TGeoBBox("TowerID", (TowerSize - CuBoxThickness) / 2, (TowerSize - CuBoxThickness) / 2, Length + 0.01); + TGeoCompositeShape* TowerHCAL = new TGeoCompositeShape("TowerHCAL", "TowerOD - TowerID"); + TGeoVolume* volTower = new TGeoVolume("volTower", TowerHCAL, gGeoManager->GetMedium(getMediumID(ID_COPPER))); + volTower->SetLineWidth(2); + volTower->SetLineColor(42); + // mSensitiveHCAL.push_back(volTower->GetName()); + mSensitive.push_back(volTower->GetName()); + + TGeoVolumeAssembly* volTowerHCAL = new TGeoVolumeAssembly("volTowerHCAL"); + volTowerHCAL->AddNode(volTower, 1, nullptr); + + int Rows = 0; + float RowPos = 0.; + int Columns = 0; + int NumTubes = 1; + + // Packing circles in Hexagonal shape + while (RowPos + CuBoxThickness / 2 + Rout + 2 * Rout < TowerSize) { + Columns = 0; + float ColumnPos = (Rows % 2 == 0) ? 0. : Rout; + while (ColumnPos + CuBoxThickness / 2 + Rout + 2 * Rout < TowerSize) { + + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - TowerSize / 2 + CuBoxThickness / 2 + Rout, RowPos - TowerSize / 2 + CuBoxThickness / 2 + Rout, 0.); + + trans->SetName(Form("trans_Num_%d", NumTubes)); + trans->RegisterYourself(); + + volTowerHCAL->AddNode(HcalTube, NumTubes, trans); + // volTowerHCAL->AddNode(volCuTube, NumTubes, trans); + // volTowerHCAL->AddNode(volSciFi, NumTubes, trans); + + Columns++; + ColumnPos = Columns * 2 * Rout + ((Rows % 2 == 0) ? 0. : Rout); + NumTubes++; + } + + Rows++; + RowPos = Rows * 2 * Rout * TMath::Sin(TMath::Pi() / 3); + } + + // Define the distance from the beam pipe in which towers will ommitted + Double_t BeamPipeRadius = 3.0; // in cm To be changed later + Double_t TowerHalfDiag = TMath::Sqrt2() * 0.5 * TowerSize; // tower half diagonal + Double_t MinRadius = BeamPipeRadius + TowerSize / 2; + + float SizeXHCAL = mGeometry->getHCALTowersInX() * TowerSize; + float SizeYHCAL = mGeometry->getHCALTowersInY() * TowerSize; + + int nTowersX = mGeometry->getHCALTowersInX(); + int nTowersY = mGeometry->getHCALTowersInY(); + + Rows = 0; + Columns = 0; + RowPos = 0.; + Int_t NumTowers = 1; + + if (splitDet) { + SizeXHCAL = SizeXHCAL / 2; + + TGeoVolumeAssembly* volHalfHCAL = new TGeoVolumeAssembly("HalfHCAL"); + + for (Rows = 0; Rows < nTowersY; Rows++) { + + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX / 2; Columns++) { + ColumnPos = Columns * TowerSize; + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Shit the beampipe towers by TowerSize/2 + if (Rows == nTowersY / 2) { + trans->SetDx(trans->GetTranslation()[0] + TowerSize / 2); + } + + // Adding the Tower to the HCAL + volHalfHCAL->AddNode(volTowerHCAL, NumTowers, trans); + + NumTowers++; + } + volHCAL->AddNode(volHalfHCAL, 1, new TGeoTranslation(SizeXHCAL / 2 + mGeometry->getDetectorOpeningRight(), 0, 0)); + TGeoRotation* rotFlipZ = new TGeoRotation(); + rotFlipZ->RotateY(180); // Flip around Y to reverse Z + TGeoCombiTrans* combHalf = new TGeoCombiTrans(-SizeXHCAL / 2 - mGeometry->getDetectorOpeningLeft(), 0., 0., rotFlipZ); + volHCAL->AddNode(volHalfHCAL, 2, combHalf); + } + } else { + for (Rows = 0; Rows < nTowersY; Rows++) { + + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX; Columns++) { + ColumnPos = Columns * TowerSize; + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Remove the Towers that overlaps with the beam pipe + Double_t RadialDistance = TMath::Power(trans->GetTranslation()[0], 2) + TMath::Power(trans->GetTranslation()[1], 2); + + if (RadialDistance < MinRadius * MinRadius || TMath::Abs(trans->GetTranslation()[0]) > SizeXHCAL / 2) { + continue; + } + + // Adding the Tower to the HCAL + volHCAL->AddNode(volTowerHCAL, NumTowers, trans); + + NumTowers++; + } + } + } + + LOG(info) << "Number of Towers is: " << (NumTowers - 1); + LOG(info) << "Number of tubes is: " << (NumTubes - 1) * (NumTowers - 1); + + // Create Aluminium plate at the back of HCal to simulate the electronics readout material + // Hardcoded thickness of 1cm and placement at 2 cm behind HCAL + TGeoBBox* alHcalBox = new TGeoBBox("AlHCalBox", SizeXHCAL / 2.0, SizeYHCAL / 2.0, 0.5 / 2.0); + TGeoVolume* volumeAlHcalBox = new TGeoVolume("volAlHcalBox", alHcalBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlHcalBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlHcalBox", 9999, "FOCAL", 0.0, 0.0, +1.0 * mGeometry->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveHCAL.push_back("volAlHcalBox"); + mSensitive.push_back("volAlHcalBox"); + } + TGeoBBox* alUnderBox = new TGeoBBox("AlUnderBox", SizeXHCAL / 2.0, 0.5, mGeometry->getFOCALSizeZ() / 2.0 + 1.5); + TGeoVolume* volumeAlUnderBox = new TGeoVolume("volAlUnderBox", alUnderBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlUnderBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlUnderBox", 9999, "FOCAL", 0.0, -1.0 * mGeometry->getFOCALSizeY() / 2 - 10.5, 0.0, 0, "ONLY"); + // mSensitiveHCAL.push_back("volAlUnderBox"); + mSensitive.push_back("volAlUnderBox"); + } + + volHCAL->SetVisibility(); + volHCAL->SetVisDaughters(); + TVirtualMC::GetMC()->Gspos("HCAL", 1, "FOCAL", 0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +//_____________________________________________________________________________ +TGeoVolumeAssembly* Detector::CreatePitchAssembly(double Lx, + double Ly1, + double Ly2, + double Lz, + double hole_diameter, + double hole_spacing, + int nholes, + double fiber_radius, + std::string suffix) +{ + + // Z-alignment doesn't change + double zpos = 0; + + TGeoMedium* copper = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + TGeoMedium* scint = gGeoManager->GetMedium(getMediumID(ID_SC)); + + TGeoVolumeAssembly* pitchAssembly = new TGeoVolumeAssembly("pitchAssembly"); + + // Hardcoded values for hole placement, to be set from outside + float holeStart = 0.15; // cm + float holeEnd = 0.35; // cm + + TGeoVolumeAssembly* volLowerSheetwHoles = new TGeoVolumeAssembly(Form("volLowerSheetwHoles_%s", suffix.c_str())); + TGeoVolume* cuSheet = gGeoManager->MakeBox("cuSheet", copper, Lx / 2, (Ly1 - fiber_radius * 2) / 2, Lz / 2); + cuSheet->SetLineColor(kOrange + 2); + mSensitive.push_back(cuSheet->GetName()); + TGeoVolume* boxbegin = gGeoManager->MakeBox("BoxBegin", copper, holeStart / 2, fiber_radius, Lz / 2); + boxbegin->SetLineColor(kOrange + 2); + mSensitive.push_back(boxbegin->GetName()); + TGeoVolume* boxMiddle = gGeoManager->MakeBox("BoxMiddle", copper, (hole_spacing - hole_diameter) / 2, fiber_radius, Lz / 2); + boxMiddle->SetLineColor(kOrange + 2); + mSensitive.push_back(boxMiddle->GetName()); + TGeoVolume* boxEnd = gGeoManager->MakeBox("BoxEnd", copper, holeEnd / 2, fiber_radius, Lz / 2); + boxEnd->SetLineColor(kOrange + 2); + mSensitive.push_back(boxEnd->GetName()); + + double yPlacement = Ly1 / 2 - fiber_radius; + + // ----------------- + // Layer 1: Lower sheet with holes (y = 0) + // ----------------- + + volLowerSheetwHoles->AddNode(cuSheet, 0, new TGeoTranslation(0, -Ly1 / 2 + (Ly1 - fiber_radius * 2) / 2, zpos)); + + // Add holes starting at x = 1.5 mm + float start_x = -Lx / 2 + holeStart; + + for (int ihole = 0; ihole < nholes; ++ihole) { + float holePlacement = start_x + ihole * hole_spacing + hole_diameter / 2; + if (ihole == 0) { + volLowerSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement - holeStart / 2 - hole_diameter / 2, yPlacement, zpos)); + volLowerSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else if (ihole == nholes - 1) { + if ((holePlacement + hole_diameter / 2 + holeStart) < Lx / 2 - 0.005) { + volLowerSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeEnd / 2, yPlacement, zpos)); + } else { + volLowerSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeStart / 2, yPlacement, zpos)); + } + } else { + volLowerSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } + } + + pitchAssembly->AddNode(volLowerSheetwHoles, 0, new TGeoTranslation(0, Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 2: Full copper sheet + // ----------------- + TGeoVolume* fullSheet1 = gGeoManager->MakeBox("FullSheet1", copper, Lx / 2, Ly2 / 2, Lz / 2); + fullSheet1->SetLineColor(kOrange + 2); + mSensitive.push_back(fullSheet1->GetName()); + pitchAssembly->AddNode(fullSheet1, 0, new TGeoTranslation(0, Ly1 / 2 + Ly2 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 3: Upper sheet with holes (shifted) + // ----------------- + + TGeoVolumeAssembly* volUpperSheetwHoles = new TGeoVolumeAssembly(Form("volUpperSheetwHoles_%s", suffix.c_str())); + + volUpperSheetwHoles->AddNode(cuSheet, 0, new TGeoTranslation(0, -Ly1 / 2 + (Ly1 - fiber_radius * 2) / 2, zpos)); + + // Add holes starting at x = 3.5 mm + float start_x2 = -Lx / 2 + holeEnd; + + for (int ihole = 0; ihole < nholes; ++ihole) { + float holePlacement = start_x2 + ihole * hole_spacing + hole_diameter / 2; + if (ihole == 0) { + volUpperSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement - hole_diameter / 2 - holeEnd / 2, yPlacement, zpos)); + volUpperSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else if (ihole == nholes - 1) { + volUpperSheetwHoles->AddNode(boxbegin, ihole, new TGeoTranslation(holePlacement + holeStart / 2 + hole_diameter / 2, yPlacement, zpos)); + } else { + if ((holePlacement + hole_spacing + hole_diameter / 2) < Lx / 2 - 0.005) { + volUpperSheetwHoles->AddNode(boxMiddle, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + (hole_spacing - hole_diameter) / 2, yPlacement, zpos)); + } else { + volUpperSheetwHoles->AddNode(boxEnd, ihole, new TGeoTranslation(holePlacement + hole_diameter / 2 + holeEnd / 2, yPlacement, zpos)); + break; + } + } + } + + pitchAssembly->AddNode(volUpperSheetwHoles, 0, new TGeoTranslation(0, Ly1 / 2 + Ly2 + Ly1 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Layer 4: Full copper sheet + // ----------------- + pitchAssembly->AddNode(fullSheet1, 1, new TGeoTranslation(0, Ly1 / 2 + Ly2 + Ly1 + Ly2 / 2 + Ly1 / 2, zpos)); // Add Ly1 / 2 so the lower edge of the sheets start y=0 + + // ----------------- + // Scintillator Fibers + // ----------------- + // Lower set of fibers + TGeoVolume* fiber = gGeoManager->MakeTube("Fiber", scint, 0, fiber_radius, Lz / 2); + fiber->SetLineColor(kBlue); + mSensitive.push_back(fiber->GetName()); + for (int i = 0; i < nholes; ++i) { + float x_fiber = start_x + i * hole_spacing + hole_diameter / 2; + pitchAssembly->AddNode(fiber, i, new TGeoTranslation(x_fiber, Ly1 / 2 - fiber_radius + Ly1 / 2, zpos)); + } + + // Upper set of fibers + for (int i = 0; i < nholes; ++i) { + float x_fiber = start_x2 + i * hole_spacing + hole_diameter / 2; + if (x_fiber > Lx / 2 - 0.05) { + break; + } + pitchAssembly->AddNode(fiber, i + nholes, new TGeoTranslation(x_fiber, Ly1 / 2 + Ly2 + Ly1 / 2 + Ly1 / 2 - fiber_radius + Ly1 / 2, zpos)); + } + + return pitchAssembly; +} + +//_____________________________________________________________________________ +void Detector::CreateHCALSheets() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + // Dimensions + double Lx = 49.81; // cm + double Ly1 = 0.20; // cm (sheets with holes) + double Ly2 = 0.15; // cm (full sheets) + double Lz = 110.0; // cm + + double fiber_radius = 0.05; + + // HCal materials + int icomp = 0; + for (auto& comp : mGeoCompositions) { + Lz = comp->sizeZ(); + + if (comp->material() == "Scint") { + fiber_radius = comp->sizeX() / 2; + } + if (comp->material() == "CuHCAL" && icomp == 0) { + Lx = comp->sizeX(); + Ly1 = comp->sizeY(); + } + if (comp->material() == "CuHCAL" && icomp == 2) { + Ly2 = comp->sizeY(); + } + icomp++; + } + + double hole_diameter = fiber_radius * 2 + 0.01; // hole radius + double hole_spacing = mGeometry->getHCALPitchSize(); + int nholes = (int)(Lx / hole_spacing); // Number of holes in one HCAL sheet + + double beamPipeHole = mGeometry->getHCALBeamPipeHoleSize(); // cm The size of the beam pipe opening + int nBeamPipeHoles = (int)((Lx - beamPipeHole / 2) / hole_spacing); // Number of beam pipe holes + + // Compute module height (two sheets with holes + two full sheets) + float pitch_height = Ly1 + Ly2 + Ly1 + Ly2; + + int totalNumberOfPitches = mGeometry->getHCALTowersInY() * 2; // Number of pitches in the whole HCAL + int numberOfPitchesBeamPipe = (int)((beamPipeHole + 0.001) / pitch_height); // Number of pitches in the beam pipe region + int numberofPitchesOnYaxis = (totalNumberOfPitches - numberOfPitchesBeamPipe); // Number of pitches in the HCAL ouside the beam pipe region + + TGeoVolumeAssembly* pitchAssembly = CreatePitchAssembly(Lx, Ly1, Ly2, Lz, hole_diameter, hole_spacing, nholes, fiber_radius, "Main"); + pitchAssembly->SetVisibility(true); + TGeoVolumeAssembly* beamPipeAssembly = CreatePitchAssembly(Lx - beamPipeHole / 2, Ly1, Ly2, Lz, hole_diameter, hole_spacing, nBeamPipeHoles, fiber_radius, "BeamPipe"); + beamPipeAssembly->SetVisibility(true); + + TGeoVolumeAssembly* HalfHCAL = new TGeoVolumeAssembly("HalfHCAL"); + + for (int iPitch = 0; iPitch < numberofPitchesOnYaxis; iPitch++) { + float placement = iPitch * pitch_height - pitch_height * (totalNumberOfPitches) / 2.0; + if (placement < -beamPipeHole / 2.0) { + HalfHCAL->AddNode(pitchAssembly, iPitch, new TGeoTranslation(0, placement, 0.)); + } else { + placement += beamPipeHole; + HalfHCAL->AddNode(pitchAssembly, iPitch, new TGeoTranslation(0, placement, 0.)); + } + } + + for (int iPitch = 0; iPitch < numberOfPitchesBeamPipe; iPitch++) { + float placement = iPitch * pitch_height - beamPipeHole / 2.0; + HalfHCAL->AddNode(beamPipeAssembly, iPitch, new TGeoTranslation(-beamPipeHole / 4, placement, 0.)); + } + + HalfHCAL->SetVisibility(true); + HalfHCAL->SetVisDaughters(true); + + volHCAL->AddNode(HalfHCAL, 0, new TGeoTranslation(-Lx / 2, 0, 0.)); + TGeoRotation* rotFlipZ = new TGeoRotation(); + rotFlipZ->RotateY(180); // Flip around Y to reverse Z + TGeoCombiTrans* combHalf = new TGeoCombiTrans(Lx / 2, 0., 0., rotFlipZ); + volHCAL->AddNode(HalfHCAL, 1, combHalf); + + gMC->Gspos("HCAL", 1, "FOCAL", 0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +//_____________________________________________________________________________ +void Detector::CreateHCALSandwich() +{ + TGeoVolumeAssembly* volHCAL = new TGeoVolumeAssembly("HCAL"); + + /// make big volume containing all the longitudinal layers + Float_t pars[4]; // this is HMSC Assembly + pars[0] = mGeometry->getHCALTowerSize() / 2; + pars[1] = mGeometry->getHCALTowerSize() / 2; + pars[2] = mGeometry->getECALSizeZ() + mGeometry->getHCALSizeZ() / 2; // ECAL sizeZ is already added to the HCAL materials CenterZ, so it is also treated as offset + pars[3] = 0; + + float offset = pars[2]; + + TGeoVolumeAssembly* volTower = new TGeoVolumeAssembly("Tower"); + + int iCu(0), iScint(0); + for (auto& icomp : mGeoCompositions) { + + pars[0] = icomp->sizeX() / 2; + pars[1] = icomp->sizeY() / 2; + pars[2] = icomp->sizeZ() / 2; + pars[3] = 0; + + // HCal materials + + if (icomp->material() == "Pb") { + iCu++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_PB)); + const TGeoBBox* HPadBox = new TGeoBBox("HPadBox", pars[0], pars[1], pars[2]); + TGeoVolume* HPad = new TGeoVolume("HPad", HPadBox, medium); + HPad->SetLineColor(kGray); + // mSensitiveHCAL.push_back(HPad->GetName()); + mSensitive.push_back(HPad->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HPad, iCu, trans); + } + if (icomp->material() == "Scint") { + iScint++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_SC)); + const TGeoBBox* HScintBox = new TGeoBBox("HScintBox", pars[0], pars[1], pars[2]); + TGeoVolume* HScint = new TGeoVolume("HScint", HScintBox, medium); + HScint->SetLineColor(kBlue); + // mSensitiveHCAL.push_back(HScint->GetName()); + mSensitive.push_back(HScint->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HScint, iScint, trans); + } + if (icomp->material() == "CuHCAL") { + iCu++; + const TGeoMedium* medium = gGeoManager->GetMedium(getMediumID(ID_COPPER)); + const TGeoBBox* HPadBox = new TGeoBBox("HPadBox", pars[0], pars[1], pars[2]); + TGeoVolume* HPad = new TGeoVolume("HPad", HPadBox, medium); + HPad->SetLineColor(kRed); + // mSensitiveHCAL.push_back(HPad->GetName()); + mSensitive.push_back(HPad->GetName()); + TGeoTranslation* trans = new TGeoTranslation(icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset); + volTower->AddNode(HPad, iCu, trans); + } + } + double TowerSize = mGeometry->getHCALTowerSize(); + + // Define the distance from the beam pipe in which towers will ommitted + double BeamPipeRadius = 3.6; // in cm + double TowerHalfDiag = TMath::Sqrt2() * 0.5 * TowerSize; // tower half diagonal + double MinRadius = BeamPipeRadius + TowerHalfDiag; + + float SizeXHCAL = mGeometry->getHCALTowersInX() * TowerSize; + float SizeYHCAL = mGeometry->getHCALTowersInY() * TowerSize; + + int nTowersX = mGeometry->getHCALTowersInX(); + int nTowersY = mGeometry->getHCALTowersInY(); + + int Rows = 0; + int Columns = 0; + double RowPos = 0.; + int NumTowers = 1; + + // Arranging towers + for (Rows = 0; Rows < nTowersY; Rows++) { + Columns = 0; + float ColumnPos = 0.; + RowPos = Rows * TowerSize; + for (Columns = 0; Columns < nTowersX; Columns++) { + ColumnPos = Columns * TowerSize; + + TGeoTranslation* trans = new TGeoTranslation(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, RowPos - SizeYHCAL / 2 + TowerSize / 2, 0.); + + // Remove the Towers that overlaps with the beam pipe + double RadialDistance = TMath::Power(ColumnPos - SizeXHCAL / 2 + TowerSize / 2, 2) + TMath::Power(RowPos - SizeYHCAL / 2 + TowerSize / 2, 2); + + if (RadialDistance < MinRadius * MinRadius) { + continue; + } + + // Adding the Tower to the HCAL + volHCAL->AddNode(volTower, NumTowers, trans); + + NumTowers++; + } + } + LOG(info) << "Number of Towers is: " << (NumTowers - 1); + + // Create an Aluminium plate at the back of HCal to simulate the electronics readout material + // Hardcoded thickness of 1cm and placement at 2 cm behind HCAL + TGeoBBox* alHcalBox = new TGeoBBox("AlHCalBox", SizeXHCAL / 2.0, SizeYHCAL / 2.0, 0.5 / 2.0); + TGeoVolume* volumeAlHcalBox = new TGeoVolume("volAlHcalBox", alHcalBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlHcalBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlHcalBox", 9999, "FOCAL", 0.0, 0.0, +1.0 * mGeometry->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + } + TGeoBBox* alUnderBox = new TGeoBBox("AlUnderBox", SizeXHCAL / 2.0, 0.5, mGeometry->getFOCALSizeZ() / 2.0 + 1.5); + TGeoVolume* volumeAlUnderBox = new TGeoVolume("volAlUnderBox", alUnderBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + volumeAlUnderBox->SetLineColor(kOrange); + if (mGeometry->getInsertHCalReadoutMaterial()) { + TVirtualMC::GetMC()->Gspos("volAlUnderBox", 9999, "FOCAL", 0.0, -1.0 * mGeometry->getFOCALSizeY() / 2 - 10.5, 0.0, 0, "ONLY"); + } + + TGeoVolume* volFOCAL = gGeoManager->GetVolume("FOCAL"); + volFOCAL->AddNode(volHCAL, 1, new TGeoTranslation(0, 0, mGeometry->getHCALCenterZ() - mGeometry->getFOCALSizeZ() / 2 + 0.01 + (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) - (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0))); // 0.01 to avoid overlap with ECAL +} + +//_____________________________________________________________________________ +void Detector::CreateECALGeometry() +{ + // using boost::algorithm::contains; // only when string operations + Geometry* geom = getGeometry(); + + // Int_t *idtmed = fIdtmed->GetArray() - 3599; //599 -> 3599 + + ////// strategy to create the supermodule (tower) + ////// 1. create tower correspinding to 5 PAD wafer + ////// 2. create tower with PIX layers (NX:NY) + + /// make big volume containing all the longitudinal layers + double pars[4]; // this is EMSC Assembly + pars[0] = geom->getTowerSizeX() / 2. + geom->getTowerGapSizeX() / 2.; + pars[1] = geom->getTowerSizeY() / 2. + geom->getTowerGapSizeY() / 2.; + // pars[2] = mGeometry->GetFOCALSizeZ() / 2; + pars[2] = geom->getECALSizeZ() / 2; + pars[3] = 0; + // this shifts all the pixel layers to the center near the beampipe + double pixshift = geom->getTowerSizeX() - (geom->getGlobalPixelWaferSizeX() * geom->getNumberOfPIXsInX()); + + bool splitDet = mGeometry->getDetectorOpeningRight() > 0.0 || mGeometry->getDetectorOpeningLeft() > 0.0; + + float offset = pars[2]; + // gMC->Gsvolu("EMSC1", "BOX", idtmed[3698], pars, 4);//Left towers (pixels shifted right) + // gMC->Gsvolu("EMSC2", "BOX", idtmed[3698], pars, 4);//Right towers (pixels shifted left) + + TVirtualMC::GetMC()->Gsvolu("EMSC1", "BOX", getMediumID(ID_AIR), pars, 4); // Left towers (pixels shifted right) + TVirtualMC::GetMC()->Gsvolu("EMSC2", "BOX", getMediumID(ID_AIR), pars, 4); // Right towers (pixels shifted left) + // mSensitiveECALPad.push_back("EMSC1"); + // mSensitiveECALPad.push_back("EMSC2"); + mSensitive.push_back("EMSC1"); + mSensitive.push_back("EMSC2"); + + // const Composition *icomp = new Composition(); //to be removed + // for(int i = 0; i < 20; i++){ // old + // icomp = geom->getComposition(i, 0); // obsolete + + // loop over geometry composition elements + for (auto& icomp : mGeoCompositions) { + + pars[0] = icomp->sizeX() / 2.; + pars[1] = icomp->sizeY() / 2.; + pars[2] = icomp->sizeZ() / 2.; + pars[3] = 0; + + if (icomp->material() == "PureW") { + // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3599], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_TUNGSTEN), pars, 4); + // mSensitiveECALPad.push_back("EW1"); + mSensitive.push_back("EW1"); + gGeoManager->GetVolume("EW1")->SetLineColor(kBlue); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + if (icomp->material() == "Alloy") { + // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3604], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", getMediumID(ID_ALLOY), pars, 4); + // mSensitiveECALPad.push_back("EW1"); + mSensitive.push_back("EW1"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EW1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "G10") { + // TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", idtmed[3601], pars, 4); + TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", getMediumID(ID_G10), pars, 4); + // mSensitiveECALPad.push_back("G10RO1"); + mSensitive.push_back("G10RO1"); + gGeoManager->GetVolume("G10RO1")->SetLineColor(kGreen); + TVirtualMC::GetMC()->Gspos("G10RO1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("G10RO1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Cu") { + // TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", idtmed[3602], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", getMediumID(ID_COPPER), pars, 4); + // mSensitiveECALPad.push_back("EWCU"); + mSensitive.push_back("EWCU"); + gGeoManager->GetVolume("EWCU")->SetLineColor(kViolet); + TVirtualMC::GetMC()->Gspos("EWCU", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWCU", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Air") { + // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3698], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_AIR), pars, 4); + // mSensitiveECALPad.push_back("EWAIR1"); + mSensitive.push_back("EWAIR1"); + gGeoManager->GetVolume("EWAIR1")->SetLineColor(kGray); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "Ceramic") { + // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3607], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", getMediumID(ID_CERAMIC), pars, 4); + // mSensitiveECALPad.push_back("EWAIR1"); + mSensitive.push_back("EWAIR1"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWAIR1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + if (icomp->material() == "SiPad") { + // TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", idtmed[3600], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", getMediumID(ID_SIPAD), pars, 4); + // mSensitiveECALPad.push_back("EWSIPAD1"); + mSensitive.push_back("EWSIPAD1"); + gGeoManager->GetVolume("EWSIPAD1")->SetLineColor(kOrange - 7); + int number = (icomp->id()) + (icomp->stack() << 12) + (icomp->layer() << 16); + // cout<<" pad : "<< icomp->material()<<" "<centerZ()-offset <Gspos("EWSIPAD1", number + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSIPAD1", number + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + // Pixels (sensitive layer) + if (icomp->material() == "SiPix") { + // TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", idtmed[3600], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", getMediumID(ID_SIPIX), pars, 4); + // mSensitiveECALPix.push_back("EWSIPIX1"); + mSensitive.push_back("EWSIPIX1"); + gGeoManager->GetVolume("EWSIPIX1")->SetLineColor(kPink); + + int number = (icomp->id()) + (icomp->stack() << 12) + (icomp->layer() << 16); + TVirtualMC::GetMC()->Gspos("EWSIPIX1", number + 1, "EMSC1", + icomp->centerX() - geom->getGlobalPixelOffsetX() + pixshift, icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSIPIX1", number + 1, "EMSC2", + icomp->centerX() + geom->getGlobalPixelOffsetX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + + // Passive silicon + if (icomp->material() == "Si") { + // TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", idtmed[3610], pars, 4); + TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", getMediumID(ID_SIINSENS), pars, 4); + // mSensitiveECALPix.push_back("EWSI1"); + mSensitive.push_back("EWSI1"); + gGeoManager->GetVolume("EWSI1")->SetLineColor(kPink); + TVirtualMC::GetMC()->Gspos("EWSI1", icomp->id() + 1, "EMSC1", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("EWSI1", icomp->id() + 1, "EMSC2", + icomp->centerX(), icomp->centerY(), icomp->centerZ() - offset, 0, "ONLY"); + } + } // end of loop over composition elements + + // Add the coldplates to each of the left and right towers + TGeoBBox* coldPlateBox = new TGeoBBox("ColdPlateBox", geom->getTowerSizeX() / 2.0, geom->getTowerGapSizeY() / 2.0, geom->getECALSizeZ() / 2.0); + TGeoVolume* volumeColdPlate = nullptr; + + if (geom->getTowerGapMaterial() == "Cu") { // Copper + // if (contains(geom->getTowerGapMaterial(), "Cu")) { // Copper + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_COPPER))); + } else if (geom->getTowerGapMaterial() == "Al") { // Aluminium + // else if (contains(geom->getTowerGapMaterial(), "Al")) { // Aluminium + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); + } else { + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_AIR))); + } + // mSensitiveECALPad.push_back(volumeColdPlate->GetName()); + mSensitive.push_back(volumeColdPlate->GetName()); + volumeColdPlate->SetLineColor(kOrange); + TVirtualMC::GetMC()->Gspos("volColdPlate", 1, "EMSC1", 0.0, geom->getTowerSizeY() / 2.0 + geom->getTowerGapSizeY() / 2.0, 0.0, 0, "ONLY"); + TVirtualMC::GetMC()->Gspos("volColdPlate", 1, "EMSC2", 0.0, geom->getTowerSizeY() / 2.0 + geom->getTowerGapSizeY() / 2.0, 0.0, 0, "ONLY"); + + // Place the towers in the ECAL + // --- Place the ECAL in FOCAL + float fcal_pars[4]; + fcal_pars[0] = (geom->getFOCALSizeX() + 2. * geom->getMiddleTowerOffset() + mGeometry->getDetectorOpeningRight() + mGeometry->getDetectorOpeningLeft()) / 2.; + fcal_pars[1] = geom->getFOCALSizeY() / 2.; + fcal_pars[2] = geom->getECALSizeZ() / 2.; + fcal_pars[3] = 0.; + + // TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", idtmed[3698], fcal_pars, 4); + TVirtualMC::GetMC()->Gsvolu("ECAL", "BOX", getMediumID(ID_AIR), fcal_pars, 4); + // mSensitiveECALPad.push_back("ECAL"); + mSensitive.push_back("ECAL"); + + // Create SiPad box for the two sensitive layers to be placed in front of ECAL + TGeoBBox* siPadBox = new TGeoBBox("SiPadBox", geom->getTowerSizeX() / 2. + geom->getTowerGapSizeX() / 2., + geom->getTowerSizeY() / 2. + geom->getTowerGapSizeY() / 2., 0.03 / 2.0); + TGeoVolume* volumeSiPad = new TGeoVolume("volSiPad", siPadBox, gGeoManager->GetMedium(getMediumID(ID_SIPAD))); + volumeSiPad->SetLineColor(kOrange + 7); + // mSensitiveECALPad.push_back(volumeSiPad->GetName()); + if (geom->getInsertFrontPadLayers()) { + mSensitive.push_back(volumeSiPad->GetName()); + } + + double xp, yp, zp; + int itowerx, itowery; + // int number = i + j * geom->getNumberOfTowersInX(); + for (int number = 0; number < geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY(); number++) { + itowerx = number % geom->getNumberOfTowersInX(); + itowery = number / geom->getNumberOfTowersInX(); + // const auto towerCenter = geom->getGeoTowerCenter(number); //only ECAL part, second parameter = -1 by default + // xp = std::get<0>towerCenter; + // std::tie(xp, yp, zp) = geom->getGeoTowerCenter(number); + auto [xp, yp, zp] = geom->getGeoTowerCenter(number); // only ECAL part, second parameter = -1 by default + + if (itowerx == 0) { + if (splitDet) { + xp -= geom->getDetectorOpeningLeft(); + } + + TVirtualMC::GetMC()->Gspos("EMSC1", number + 1, "ECAL", xp, yp, 0, 0, "ONLY"); + // Add the SiPad front volumes directly under the FOCAL volume + if (geom->getInsertFrontPadLayers()) { + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (number + 1), "FOCAL", xp, yp, -1.0 * geom->getFOCALSizeZ() / 2.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY() + number + 1), "FOCAL", xp - 0.5, yp + 0.5, -1.0 * geom->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + } + } + if (itowerx == 1) { + if (splitDet) { + xp += geom->getDetectorOpeningRight(); + } + + TVirtualMC::GetMC()->Gspos("EMSC2", number + 1, "ECAL", xp, yp, 0, 0, "ONLY"); + // Add the SiPad front volumes directly under the FOCAL volume + if (geom->getInsertFrontPadLayers()) { + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (number + 1), "FOCAL", xp, yp, -1.0 * geom->getFOCALSizeZ() / 2.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + TVirtualMC::GetMC()->Gspos("volSiPad", -1 * (geom->getNumberOfTowersInX() * geom->getNumberOfTowersInY() + number + 1), "FOCAL", xp + 0.5, yp + 0.5, -1.0 * geom->getFOCALSizeZ() / 2.0 + 1.0, 0, "ONLY"); + // mSensitiveECALPad.push_back("volSiPad"); + mSensitive.push_back("volSiPad"); + } + } + } // end of loop over ECAL towers (TowersInX x TowersInY) + + TVirtualMC::GetMC()->Gspos("ECAL", 1, "FOCAL", 0, 0, geom->getECALCenterZ() - geom->getFOCALSizeZ() / 2.0 + (geom->getInsertFrontPadLayers() ? 2.0 : 0.0) - (geom->getInsertHCalReadoutMaterial() ? 1.5 : 0.0), 0, "ONLY"); +} + +void Detector::BeginPrimary() +{ + mCurrentPrimaryID = fMC->GetStack()->GetCurrentTrackNumber(); + LOG(debug) << "Starting primary " << mCurrentPrimaryID << " with energy " << fMC->GetStack()->GetCurrentTrack()->Energy(); +} + +void Detector::FinishPrimary() +{ + LOG(debug) << "Finishing primary " << mCurrentPrimaryID; + // Resetting primary and parent ID + mCurrentPrimaryID = -1; +} + +bool Detector::ProcessHitsEPad(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << TVirtualMC::GetMC()->CurrentVolPath(); + + double eloss = TVirtualMC::GetMC()->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(TVirtualMC::GetMC()->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + TVirtualMC::GetMC()->TrackPosition(x, y, z); + TVirtualMC::GetMC()->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, TVirtualMC::GetMC()->TrackLength(), TVirtualMC::GetMC()->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + TVirtualMC::GetMC()->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = TVirtualMC::GetMC()->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::EPADS, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} + +bool Detector::ProcessHitsHCAL(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << fMC->CurrentVolPath(); + + double eloss = fMC->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(fMC->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + fMC->TrackPosition(x, y, z); + fMC->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, fMC->TrackLength(), fMC->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + fMC->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = fMC->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::HCAL, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} + +bool Detector::ProcessHitsEPix(FairVolume* v) +{ + LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << fMC->CurrentVolPath(); + + double eloss = fMC->Edep() * 1e9; // energy in eV (GeV->eV) + if (eloss < DBL_EPSILON) { + return false; // only process hits which actually deposit some energy in the FOCAL + } + + // In case of new parent track create new track reference + auto o2stack = static_cast(fMC->GetStack()); + if (!mCurrentSuperparent->mHasTrackReference) { + float x, y, z, px, py, pz, e; + fMC->TrackPosition(x, y, z); + fMC->TrackMomentum(px, py, pz, e); + o2::TrackReference trackref(x, y, z, px, py, pz, fMC->TrackLength(), fMC->TrackTime(), mCurrentParentID, GetDetId()); + o2stack->addTrackReference(trackref); + mCurrentSuperparent->mHasTrackReference = true; + } + + float posX, posY, posZ; + fMC->TrackPosition(posX, posY, posZ); + + auto [indetector, col, row, layer, segment] = mGeometry->getVirtualInfo(posX, posY, posZ); + + if (!indetector) { + // particle outside the detector + return true; + } + + auto currenthit = FindHit(mCurrentParentID, col, row, layer); + if (!currenthit) { + // Condition for new hit: + // - Processing different partent track (parent track must be produced outside FOCAL) + // - Inside different cell + // - First track of the event + Double_t time = fMC->TrackTime() * 1e9; // time in ns + LOG(debug3) << "Adding new hit for parent " << mCurrentParentID << " and cell Col: " << col << " Row: " << row << " segment: " << segment; + + /// check handling of primary particles + AddHit(mCurrentParentID, mCurrentPrimaryID, mCurrentSuperparent->mEnergy, row * col + col, o2::focal::Hit::Subsystem_t::EPIXELS, math_utils::Point3D(posX, posY, posZ), time, eloss); + o2stack->addHit(GetDetId()); + } else { + LOG(debug3) << "Adding energy to the current hit"; + currenthit->SetEnergyLoss(currenthit->GetEnergyLoss() + eloss); + } + return true; +} diff --git a/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h b/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h new file mode 100644 index 0000000000000..3105f04ae2c99 --- /dev/null +++ b/Detectors/FOCAL/simulation/src/FOCALSimulationLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::focal::Detector + ; +#pragma link C++ class o2::base::DetImpl < o2::focal::Detector> + ; + +#endif diff --git a/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h b/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h index 680e5596a8405..5e8e64bc00fd8 100644 --- a/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h +++ b/Detectors/FOCAL/workflow/include/FOCALWorkflow/RawDecoderSpec.h @@ -61,7 +61,7 @@ class RawDecoderSpec : public framework::Task void decodePadEvent(const gsl::span padWords, o2::InteractionRecord& hbIR); int decodePixelData(const gsl::span pixelWords, o2::InteractionRecord& hbIR, int fecID); std::array createPadLayerEvent(const o2::focal::PadData& data) const; - void fillChipsToLayer(PixelLayerEvent& pixellayer, const gsl::span& chipData, int feeID); + void fillChipToLayer(PixelLayerEvent& pixellayer, const PixelChip& chipData, int feeID); void fillEventPixeHitContainer(std::vector& eventHits, std::vector& eventChips, const PixelLayerEvent& pixelLayer, int layerIndex); int filterIncompletePixelsEventsHBF(HBFData& data, const std::vector& expectFEEs); void buildEvents(); @@ -82,6 +82,7 @@ class RawDecoderSpec : public framework::Task uint32_t mOutputSubspec = 0; PadDecoder mPadDecoder; PixelDecoder mPixelDecoder; + std::unique_ptr mPixelMapping; std::map mHBFs; std::vector mOutputTriggerRecords; std::vector mOutputPixelHits; diff --git a/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx b/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx index c78c7aebfa1f3..c7260ae3c9815 100644 --- a/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/FOCAL/workflow/src/RawDecoderSpec.cxx @@ -37,6 +37,26 @@ void RawDecoderSpec::init(framework::InitContext& ctx) LOG(info) << "Display additional information in case of inconsistency between pixel links"; mDisplayInconsistent = true; } + auto mappingfile = ctx.options().get("pixelmapping"); + PixelMapper::MappingType_t mappingtype = PixelMapper::MappingType_t::MAPPING_UNKNOWN; + auto chiptype = ctx.options().get("pixeltype"); + if (chiptype == "IB") { + LOG(info) << "Using mapping type: IB"; + mappingtype = PixelMapper::MappingType_t::MAPPING_IB; + } else if (chiptype == "OB") { + LOG(info) << "Using mapping type: OB"; + mappingtype = PixelMapper::MappingType_t::MAPPING_OB; + } else { + LOG(fatal) << "Unkown mapping type for pixels: " << chiptype; + } + if (mappingfile == "default") { + LOG(info) << "Using default pixel mapping for pixel type " << chiptype; + mPixelMapping = std::make_unique(mappingtype); + } else { + LOG(info) << "Using user-defined mapping: " << mappingfile; + mPixelMapping = std::make_unique(PixelMapper::MappingType_t::MAPPING_UNKNOWN); + mPixelMapping->setMappingFile(mappingfile, mappingtype); + } } void RawDecoderSpec::run(framework::ProcessingContext& ctx) @@ -68,6 +88,24 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) if (mDebugMode) { o2::raw::RDHUtils::printRDH(rdh); } + + if (o2::raw::RDHUtils::getMemorySize(rdh) > o2::raw::RDHUtils::getHeaderSize(rdh)) { + // non-0 payload size: + auto payloadsize = o2::raw::RDHUtils::getMemorySize(rdh) - o2::raw::RDHUtils::getHeaderSize(rdh); + int endpoint = static_cast(o2::raw::RDHUtils::getEndPointID(rdh)); + auto fee = o2::raw::RDHUtils::getFEEID(rdh); + LOG(debug) << "Next RDH: "; + LOG(debug) << "Found fee 0x" << std::hex << fee << std::dec << " (System " << (fee == 0xcafe ? "Pads" : "Pixels") << ")"; + LOG(debug) << "Found trigger BC: " << o2::raw::RDHUtils::getTriggerBC(rdh); + LOG(debug) << "Found trigger Oribt: " << o2::raw::RDHUtils::getTriggerOrbit(rdh); + LOG(debug) << "Found payload size: " << payloadsize; + LOG(debug) << "Found offset to next: " << o2::raw::RDHUtils::getOffsetToNext(rdh); + LOG(debug) << "Stop bit: " << (o2::raw::RDHUtils::getStop(rdh) ? "yes" : "no"); + LOG(debug) << "Number of GBT words: " << (payloadsize * sizeof(char) / (fee == 0xcafe ? sizeof(o2::focal::PadGBTWord) : sizeof(o2::itsmft::GBTWord))); + auto page_payload = databuffer.subspan(currentpos + o2::raw::RDHUtils::getHeaderSize(rdh), payloadsize); + std::copy(page_payload.begin(), page_payload.end(), std::back_inserter(rawbuffer)); + } + auto trigger = o2::raw::RDHUtils::getTriggerType(rdh); if (trigger & o2::trigger::HB) { // HB trigger received @@ -130,8 +168,8 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) } else { LOG(debug) << "Payload size 0 - skip empty HBF"; } - } else { rawbuffer.clear(); + } else { // Get interaction record for HBF currentIR.bc = o2::raw::RDHUtils::getTriggerBC(rdh); currentIR.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); @@ -140,26 +178,6 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) } } - if (o2::raw::RDHUtils::getMemorySize(rdh) == o2::raw::RDHUtils::getHeaderSize(rdh)) { - // Skip page if emtpy - currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); - continue; - } - - // non-0 payload size: - auto payloadsize = o2::raw::RDHUtils::getMemorySize(rdh) - o2::raw::RDHUtils::getHeaderSize(rdh); - int endpoint = static_cast(o2::raw::RDHUtils::getEndPointID(rdh)); - auto fee = o2::raw::RDHUtils::getFEEID(rdh); - LOG(debug) << "Next RDH: "; - LOG(debug) << "Found fee 0x" << std::hex << fee << std::dec << " (System " << (fee == 0xcafe ? "Pads" : "Pixels") << ")"; - LOG(debug) << "Found trigger BC: " << o2::raw::RDHUtils::getTriggerBC(rdh); - LOG(debug) << "Found trigger Oribt: " << o2::raw::RDHUtils::getTriggerOrbit(rdh); - LOG(debug) << "Found payload size: " << payloadsize; - LOG(debug) << "Found offset to next: " << o2::raw::RDHUtils::getOffsetToNext(rdh); - LOG(debug) << "Stop bit: " << (o2::raw::RDHUtils::getStop(rdh) ? "yes" : "no"); - LOG(debug) << "Number of GBT words: " << (payloadsize * sizeof(char) / (fee == 0xcafe ? sizeof(o2::focal::PadGBTWord) : sizeof(o2::itsmft::GBTWord))); - auto page_payload = databuffer.subspan(currentpos + o2::raw::RDHUtils::getHeaderSize(rdh), payloadsize); - std::copy(page_payload.begin(), page_payload.end(), std::back_inserter(rawbuffer)); currentpos += o2::raw::RDHUtils::getOffsetToNext(rdh); } } else { @@ -256,10 +274,10 @@ void RawDecoderSpec::endOfStream(o2::framework::EndOfStreamContext& ec) void RawDecoderSpec::sendOutput(framework::ProcessingContext& ctx) { - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PADLAYERS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPadLayers); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELHITS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPixelHits); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELCHIPS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputPixelChips); - ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "TRIGGERS", mOutputSubspec, framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PADLAYERS", mOutputSubspec}, mOutputPadLayers); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELHITS", mOutputSubspec}, mOutputPixelHits); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "PIXELCHIPS", mOutputSubspec}, mOutputPixelChips); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginFOC, "TRIGGERS", mOutputSubspec}, mOutputTriggerRecords); } void RawDecoderSpec::resetContainers() @@ -301,9 +319,6 @@ void RawDecoderSpec::decodePadEvent(const gsl::span padWords, o2::In int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2::InteractionRecord& hbIR, int feeID) { LOG(debug) << "Decoding pixel data for Orbit " << hbIR.orbit << ", BC " << hbIR.bc; - auto fee = feeID & 0x00FF, - branch = (feeID & 0x0F00) >> 8; - int layer = fee < 2 ? 0 : 1; gsl::span pixelpayload(reinterpret_cast(pixelWords.data()), pixelWords.size() / sizeof(o2::itsmft::GBTWord)); LOG(debug) << pixelWords.size() << " Bytes -> " << pixelpayload.size() << " GBT words"; @@ -344,7 +359,14 @@ int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2:: if (triggerfound != foundHBF->second.mPixelTriggers.end()) { // auto index = triggerfound - foundHBF->second.mPixelTriggers.begin(); - fillChipsToLayer(foundHBF->second.mPixelEvent[index][layer], chipdata, fee); + for (const auto& chip : chipdata) { + try { + auto chipPosition = mPixelMapping->getPosition(feeID, chip); + fillChipToLayer(foundHBF->second.mPixelEvent[index][chipPosition.mLayer], chip, feeID); + } catch (PixelMapper::InvalidChipException& e) { + LOG(warning) << e; + } + } foundHBF->second.mFEEs[index].push_back(feeID); } else { // new trigger @@ -352,7 +374,14 @@ int RawDecoderSpec::decodePixelData(const gsl::span pixelWords, o2:: foundHBF->second.mPixelEvent.push_back(nextevent); foundHBF->second.mPixelTriggers.push_back(trigger); auto& current = foundHBF->second.mPixelEvent.back(); - fillChipsToLayer(current[layer], chipdata, fee); + for (const auto& chip : chipdata) { + try { + auto chipPosition = mPixelMapping->getPosition(feeID, chip); + fillChipToLayer(current[chipPosition.mLayer], chip, feeID); + } catch (PixelMapper::InvalidChipException& e) { + LOG(warning) << e; + } + } foundHBF->second.mFEEs.push_back({feeID}); } nevents++; @@ -398,16 +427,14 @@ std::array RawDeco return result; } -void RawDecoderSpec::fillChipsToLayer(o2::focal::PixelLayerEvent& pixellayer, const gsl::span& chipData, int feeID) +void RawDecoderSpec::fillChipToLayer(o2::focal::PixelLayerEvent& pixellayer, const o2::focal::PixelChip& chipData, int feeID) { int nhitsBefore = 0; int nchipsBefore = pixellayer.getChips().size(); for (auto& chip : pixellayer.getChips()) { nhitsBefore += chip.mHits.size(); } - for (const auto& chip : chipData) { - pixellayer.addChip(feeID, chip.mLaneID, chip.mChipID, chip.mStatusCode, chip.mHits); - } + pixellayer.addChip(feeID, chipData.mLaneID, chipData.mChipID, chipData.mStatusCode, chipData.mHits); int nhitsAfter = 0; int nchipsAfter = pixellayer.getChips().size(); for (auto& chip : pixellayer.getChips()) { @@ -473,7 +500,7 @@ void RawDecoderSpec::buildEvents() } std::copy(eventHits.begin(), eventHits.end(), std::back_inserter(mOutputPixelHits)); std::copy(eventPixels.begin(), eventPixels.end(), std::back_inserter(mOutputPixelChips)); - mOutputTriggerRecords.emplace_back(hbf.mPixelTriggers[itrg], startPads, 0, startHits, eventPixels.size(), startHits, eventHits.size()); + mOutputTriggerRecords.emplace_back(hbf.mPixelTriggers[itrg], startPads, 0, startChips, eventPixels.size(), startHits, eventHits.size()); } } else if (mTimeframeHasPadData) { // only pad data available, set pad layers and use IR of the HBF @@ -487,7 +514,7 @@ void RawDecoderSpec::buildEvents() for (std::size_t ilayer = 0; ilayer < constants::PADS_NLAYERS; ilayer++) { mOutputPadLayers.push_back(hbf.mPadEvents[itrg][ilayer]); } - mOutputTriggerRecords.emplace_back(hbir, startPads, constants::PADS_NLAYERS, startHits, 0, startHits, 0); + mOutputTriggerRecords.emplace_back(hbir, startPads, constants::PADS_NLAYERS, startChips, 0, startHits, 0); } } } @@ -648,7 +675,7 @@ o2::framework::DataProcessorSpec o2::focal::reco_workflow::getRawDecoderSpec(boo outputs.emplace_back(originFOC, "PIXELCHIPS", outputSubspec, o2::framework::Lifetime::Timeframe); outputs.emplace_back(originFOC, "TRIGGERS", outputSubspec, o2::framework::Lifetime::Timeframe); - std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originFOC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Optional}}; + std::vector inputs{{"stf", o2::framework::ConcreteDataTypeMatcher{originFOC, o2::header::gDataDescriptionRawData}, o2::framework::Lifetime::Timeframe}}; if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); } @@ -659,5 +686,7 @@ o2::framework::DataProcessorSpec o2::focal::reco_workflow::getRawDecoderSpec(boo o2::framework::adaptFromTask(outputSubspec, usePadData, usePixelData, debugMode), o2::framework::Options{ {"filterIncomplete", o2::framework::VariantType::Bool, false, {"Filter incomplete pixel events"}}, - {"displayInconsistent", o2::framework::VariantType::Bool, false, {"Display information about inconsistent timeframes"}}}}; -} \ No newline at end of file + {"displayInconsistent", o2::framework::VariantType::Bool, false, {"Display information about inconsistent timeframes"}}, + {"pixeltype", o2::framework::VariantType::String, "OB", {"Pixel mapping type"}}, + {"pixelmapping", o2::framework::VariantType::String, "default", {"File with pixel mapping"}}}}; +} diff --git a/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx b/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx index ed779ebacc19e..dabab2e9c5415 100644 --- a/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx +++ b/Detectors/FOCAL/workflow/src/event-writer-workflow.cxx @@ -29,6 +29,7 @@ void customize(std::vector& workflowOptions) { std::vector options{{"event-writer-name", VariantType::String, "focal-event-writer", {"Workflow name"}}, {"subspec", VariantType::UInt32, 0U, {"Input subspecification"}}, + {"no-subspec", VariantType::Bool, false, {"No subspecification (for output from raw STFs)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); } @@ -48,10 +49,20 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); WorkflowSpec specs; - specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", "FOC", "PADLAYERS", subspec}, "FOCALPadLayer", "pad-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", "FOC", "PIXELHITS", subspec}, "FOCALPixelHit", "pixel-hit-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", "FOC", "PIXELCHIPS", subspec}, "FOCALPixelChip", "pixel-chip-branch-name"}, - MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", "FOC", "TRIGGERS", subspec}, "FOCALTrigger", "trigger-branch-name"})()); + if (cfgc.options().get("no-subspec")) { + // No subspecification defined (i.e. output from the raw-tf-reader workflow): Use concrete data type matcher + specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", o2::framework::ConcreteDataTypeMatcher{"FOC", "PADLAYERS"}}, "FOCALPadLayer", "pad-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", o2::framework::ConcreteDataTypeMatcher{"FOC", "PIXELHITS"}}, "FOCALPixelHit", "pixel-hit-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", o2::framework::ConcreteDataTypeMatcher{"FOC", "PIXELCHIPS"}}, "FOCALPixelChip", "pixel-chip-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", o2::framework::ConcreteDataTypeMatcher{"FOC", "TRIGGERS"}}, "FOCALTrigger", "trigger-branch-name"})()); + } else { + // Subspecification specified: Use full specification + specs.emplace_back(MakeRootTreeWriterSpec(workflowname.data(), "focalevents.root", "o2sim", + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"dataapd", "FOC", "PADLAYERS", subspec}, "FOCALPadLayer", "pad-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelhit", "FOC", "PIXELHITS", subspec}, "FOCALPixelHit", "pixel-hit-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datapixelchip", "FOC", "PIXELCHIPS", subspec}, "FOCALPixelChip", "pixel-chip-branch-name"}, + MakeRootTreeWriterSpec::BranchDefinition>{InputSpec{"datatrigger", "FOC", "TRIGGERS", subspec}, "FOCALTrigger", "trigger-branch-name"})()); + } return specs; } diff --git a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx index 3165208aef246..22fe1370040db 100644 --- a/Detectors/Filtering/src/FilteredTFReaderSpec.cxx +++ b/Detectors/Filtering/src/FilteredTFReaderSpec.cxx @@ -45,15 +45,15 @@ void FilteredTFReader::run(ProcessingContext& pc) LOG(info) << "Pushing filtered TF: " << mFiltTF.header.asString(); // ITS - pc.outputs().snapshot(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, mFiltTF.ITSTrackROFs); - pc.outputs().snapshot(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}, mFiltTF.ITSTracks); - pc.outputs().snapshot(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterIndices); + pc.outputs().snapshot(Output{"ITS", "ITSTrackROF", 0}, mFiltTF.ITSTrackROFs); + pc.outputs().snapshot(Output{"ITS", "TRACKS", 0}, mFiltTF.ITSTracks); + pc.outputs().snapshot(Output{"ITS", "TRACKCLSID", 0}, mFiltTF.ITSClusterIndices); if (mUseMC) { - pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}, mFiltTF.ITSTrackMCTruth); + pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0}, mFiltTF.ITSTrackMCTruth); } - pc.outputs().snapshot(Output{"ITS", "CLUSTERSROF", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterROFs); - pc.outputs().snapshot(Output{"ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe}, mFiltTF.ITSClusters); - pc.outputs().snapshot(Output{"ITS", "PATTERNS", 0, Lifetime::Timeframe}, mFiltTF.ITSClusterPatterns); + pc.outputs().snapshot(Output{"ITS", "CLUSTERSROF", 0}, mFiltTF.ITSClusterROFs); + pc.outputs().snapshot(Output{"ITS", "COMPCLUSTERS", 0}, mFiltTF.ITSClusters); + pc.outputs().snapshot(Output{"ITS", "PATTERNS", 0}, mFiltTF.ITSClusterPatterns); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index 93a58b20890ab..bcf3c6c3539d4 100644 --- a/Detectors/Filtering/src/FilteringSpec.cxx +++ b/Detectors/Filtering/src/FilteringSpec.cxx @@ -38,7 +38,6 @@ #include "Framework/InputRecordWalker.h" #include "Framework/Logger.h" #include "Framework/TableBuilder.h" -#include "Framework/TableTreeHelpers.h" #include "Framework/CCDBParamSpec.h" #include "FDDBase/Constants.h" #include "FT0Base/Geometry.h" @@ -327,13 +326,13 @@ DataProcessorSpec getDataFilteringSpec(GID::mask_t src, bool enableSV, bool useM auto dataRequest = std::make_shared(); dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); if (src[GID::CTP]) { LOGF(info, "Requesting CTP digits"); dataRequest->requestCTPDigits(useMC); } if (enableSV) { - dataRequest->requestSecondaryVertertices(useMC); + dataRequest->requestSecondaryVertices(useMC); } if (src[GID::TPC]) { dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks diff --git a/Detectors/ForwardAlign/CMakeLists.txt b/Detectors/ForwardAlign/CMakeLists.txt new file mode 100644 index 0000000000000..bb7d35444e4ff --- /dev/null +++ b/Detectors/ForwardAlign/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ForwardAlign + SOURCES src/MatrixSparse.cxx + src/MatrixSq.cxx + src/MillePede2.cxx + src/MillePedeRecord.cxx + src/MilleRecordReader.cxx + src/MilleRecordWriter.cxx + src/MinResSolve.cxx + src/RectMatrix.cxx + src/SymBDMatrix.cxx + src/SymMatrix.cxx + src/VectorSparse.cxx + src/MilleRecordWriterSpec.cxx + PUBLIC_LINK_LIBRARIES O2::CCDB + O2::Steer + ROOT::TreePlayer) + +o2_target_root_dictionary(ForwardAlign + HEADERS include/ForwardAlign/MatrixSparse.h + include/ForwardAlign/MatrixSq.h + include/ForwardAlign/MillePede2.h + include/ForwardAlign/MillePedeRecord.h + include/ForwardAlign/MilleRecordReader.h + include/ForwardAlign/MilleRecordWriter.h + include/ForwardAlign/MinResSolve.h + include/ForwardAlign/RectMatrix.h + include/ForwardAlign/SymBDMatrix.h + include/ForwardAlign/SymMatrix.h + include/ForwardAlign/VectorSparse.h + include/ForwardAlign/MilleRecordWriterSpec.h + LINKDEF src/ForwardAlignLinkDef.h) + + + +o2_add_executable( + millerecord-writer-workflow + SOURCES src/MilleRecordWriterSpec.cxx src/millerecord-writer-workflow.cxx + COMPONENT_NAME fwdalign + PUBLIC_LINK_LIBRARIES O2::Framework O2::DPLUtils O2::ReconstructionDataFormats O2::SimulationDataFormat O2::ForwardAlign) diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h rename to Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h index 25ea6da8928a6..6c60b4275b583 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSparse.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSparse.h @@ -13,15 +13,15 @@ /// \brief Sparse matrix class (from AliROOT), used as a global matrix for MillePede2 /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MATRIXSPARSE_H -#define ALICEO2_MFT_MATRIXSPARSE_H +#ifndef ALICEO2_FWDALIGN_MATRIXSPARSE_H +#define ALICEO2_FWDALIGN_MATRIXSPARSE_H -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/VectorSparse.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/VectorSparse.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class MatrixSparse @@ -157,7 +157,7 @@ inline Double_t& MatrixSparse::DiagElem(Int_t row) } } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h rename to Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h index 8c154b359f816..e79fc19004629 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MatrixSq.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MatrixSq.h @@ -13,15 +13,15 @@ /// \brief Abstract class (from AliROOT) for square matrix used for millepede2 operation /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MATRIXSQ_H -#define ALICEO2_MFT_MATRIXSQ_H +#ifndef ALICEO2_FWDALIGN_MATRIXSQ_H +#define ALICEO2_FWDALIGN_MATRIXSQ_H #include #include namespace o2 { -namespace mft +namespace fwdalign { /// \class MatrixSq @@ -153,7 +153,7 @@ inline void MatrixSq::MultiplyByVec(const TVectorD& vecIn, TVectorD& vecOut) con MultiplyByVec(vecIn.GetMatrixArray(), vecOut.GetMatrixArray()); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h b/Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h similarity index 88% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h rename to Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h index 1ec7ab7bc8df7..2c5bb8ef6dd8d 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePede2.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MillePede2.h @@ -16,20 +16,20 @@ /// Based on the original milliped2 by Volker Blobel /// http://www.desy.de/~blobel/mptalks.html -#ifndef ALICEO2_MFT_MILLEPEDE2_H -#define ALICEO2_MFT_MILLEPEDE2_H +#ifndef ALICEO2_FWDALIGN_MILLEPEDE2_H +#define ALICEO2_FWDALIGN_MILLEPEDE2_H #include #include #include -#include "MFTAlignment/MinResSolve.h" -#include "MFTAlignment/MillePedeRecord.h" -#include "MFTAlignment/SymMatrix.h" -#include "MFTAlignment/RectMatrix.h" -#include "MFTAlignment/MatrixSparse.h" -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/MilleRecordWriter.h" -#include "MFTAlignment/MilleRecordReader.h" +#include "ForwardAlign/MinResSolve.h" +#include "ForwardAlign/MillePedeRecord.h" +#include "ForwardAlign/SymMatrix.h" +#include "ForwardAlign/RectMatrix.h" +#include "ForwardAlign/MatrixSparse.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/MilleRecordWriter.h" +#include "ForwardAlign/MilleRecordReader.h" class TFile; class TStopwatch; @@ -38,7 +38,7 @@ class TArrayF; namespace o2 { -namespace mft +namespace fwdalign { class MillePede2 @@ -156,6 +156,7 @@ class MillePede2 fNGroupsSet = grID; } } + void ResetRecord() { fRecord->Reset(); } void SetNGloPar(const int n) { fNGloPar = n; } void SetNLocPar(const int n) { fNLocPar = n; } void SetNMaxIterations(const int n = 10) { fMaxIter = n; } @@ -256,17 +257,17 @@ class MillePede2 /// \brief write tree and close file where are stored chi2 from LocalFit() void EndChi2Storage(); - o2::mft::MillePedeRecord* GetRecord() const { return fRecord; } + o2::fwdalign::MillePedeRecord* GetRecord() const { return fRecord; } long GetSelFirst() const { return fSelFirst; } long GetSelLast() const { return fSelLast; } void SetSelFirst(long v) { fSelFirst = v; } void SetSelLast(long v) { fSelLast = v; } - void SetRecord(o2::mft::MillePedeRecord* aRecord) { fRecord = aRecord; } - void SetRecordWriter(o2::mft::MilleRecordWriter* myP) { fRecordWriter = myP; } - void SetConstraintsRecWriter(o2::mft::MilleRecordWriter* myP) { fConstraintsRecWriter = myP; } - void SetRecordReader(o2::mft::MilleRecordReader* myP) { fRecordReader = myP; } - void SetConstraintsRecReader(o2::mft::MilleRecordReader* myP) { fConstraintsRecReader = myP; } + void SetRecord(o2::fwdalign::MillePedeRecord* aRecord) { fRecord = aRecord; } + void SetRecordWriter(o2::fwdalign::MilleRecordWriter* myP) { fRecordWriter = myP; } + void SetConstraintsRecWriter(o2::fwdalign::MilleRecordWriter* myP) { fConstraintsRecWriter = myP; } + void SetRecordReader(o2::fwdalign::MilleRecordReader* myP) { fRecordReader = myP; } + void SetConstraintsRecReader(o2::fwdalign::MilleRecordReader* myP) { fConstraintsRecReader = myP; } /// \brief return the limit in chi^2/nd for n sigmas stdev authorized /// @@ -285,6 +286,9 @@ class MillePede2 fIsLinear[id] = !v; } + /// \brief Disable record writer for DPL process + void DisableRecordWriter() { fDisableRecordWriter = true; } + protected: /// \brief read data record (if any) at entry recID void ReadRecordData(const long recID, const bool doPrint = false); @@ -340,11 +344,11 @@ class MillePede2 std::vector fCGlo2Glo; ///< [fNGloPar] compressed ID to global ID buffer // Matrices - o2::mft::SymMatrix* fMatCLoc; ///< Matrix C local - o2::mft::MatrixSq* fMatCGlo; ///< Matrix C global - o2::mft::RectMatrix* fMatCGloLoc; ///< Rectangular matrix C g*l - std::vector fFillIndex; ///< [fNGloPar] auxilary index array for fast matrix fill - std::vector fFillValue; ///< [fNGloPar] auxilary value array for fast matrix fill + o2::fwdalign::SymMatrix* fMatCLoc; ///< Matrix C local + o2::fwdalign::MatrixSq* fMatCGlo; ///< Matrix C global + o2::fwdalign::RectMatrix* fMatCGloLoc; ///< Rectangular matrix C g*l + std::vector fFillIndex; ///< [fNGloPar] auxilary index array for fast matrix fill + std::vector fFillValue; ///< [fNGloPar] auxilary value array for fast matrix fill TFile* fRecChi2File; TString fRecChi2FName; @@ -354,12 +358,13 @@ class MillePede2 bool fIsChi2BelowLimit; int fRecNDoF; - o2::mft::MillePedeRecord* fRecord; ///< Buffer of measurements records + o2::fwdalign::MillePedeRecord* fRecord; ///< Buffer of measurements records long fCurrRecDataID; ///< ID of the current data record long fCurrRecConstrID; ///< ID of the current constraint record bool fLocFitAdd; ///< Add contribution of carrent track (and not eliminate it) bool fUseRecordWeight; ///< force or ignore the record weight + bool fDisableRecordWriter; ///< disable record writer for DPL process int fMinRecordLength; ///< ignore shorter records int fSelFirst; ///< event selection start int fSelLast; ///< event selection end @@ -380,15 +385,15 @@ class MillePede2 static int fgNKrylovV; ///< size of Krylov vectors buffer in FGMRES // processed data record bufferization - o2::mft::MilleRecordWriter* fRecordWriter; ///< data record writer - o2::mft::MilleRecordWriter* fConstraintsRecWriter; ///< constraints record writer - o2::mft::MilleRecordReader* fRecordReader; ///< data record reader - o2::mft::MilleRecordReader* fConstraintsRecReader; ///< constraints record reader + o2::fwdalign::MilleRecordWriter* fRecordWriter; ///< data record writer + o2::fwdalign::MilleRecordWriter* fConstraintsRecWriter; ///< constraints record writer + o2::fwdalign::MilleRecordReader* fRecordReader; ///< data record reader + o2::fwdalign::MilleRecordReader* fConstraintsRecReader; ///< constraints record reader ClassDef(MillePede2, 0); }; -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h b/Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h rename to Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h index ecc28d6f04b98..43dcbf2671c71 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MillePedeRecord.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MillePedeRecord.h @@ -16,14 +16,14 @@ /// The records for all processed tracks are stored in the temporary tree in orgder to be /// reused for multiple iterations of MillePede -#ifndef ALICEO2_MFT_MILLEPEDERECORD_H -#define ALICEO2_MFT_MILLEPEDERECORD_H +#ifndef ALICEO2_FWDALIGN_MILLEPEDERECORD_H +#define ALICEO2_FWDALIGN_MILLEPEDERECORD_H #include namespace o2 { -namespace mft +namespace fwdalign { /// \brief Store residuals and local/global deriavtives from a single track processing /// @@ -152,7 +152,7 @@ inline Bool_t MillePedeRecord::IsGroupPresent(Int_t id) const return kFALSE; } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h new file mode 100644 index 0000000000000..61c710f509f31 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordReader.h @@ -0,0 +1,87 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordReader.h +/// \author arakotoz@cern.ch +/// \brief Class dedicated to read MillePedeRecords from ROOT files + +#ifndef ALICEO2_FWDALIGN_MILLERECORD_READER_H +#define ALICEO2_FWDALIGN_MILLERECORD_READER_H + +#include +#include +#include +#include +#include + +#include "ForwardAlign/MillePedeRecord.h" + +namespace o2 +{ +namespace fwdalign +{ + +class MilleRecordReader +{ + public: + /// \brief constructor + MilleRecordReader(); + + /// \brief destructor + virtual ~MilleRecordReader(); + + /// \brief choose data records filename + void changeDataBranchName(const bool isConstraintsRec = true); + + /// \brief connect to input TChain + void connectToChain(TChain* ch); + + /// \brief check if connect to input TChain went well + bool isReaderOk() const { return mIsSuccessfulInit; } + + /// \brief check if the last operation readNextEntry() was ok + bool isReadEntryOk() const { return mIsReadEntryOk; } + + /// \brief return the record + o2::fwdalign::MillePedeRecord* getRecord() { return mRecord; }; + + /// \brief return the ID of the current record in the TTree + long getCurrentDataID() const { return mCurrentDataID; } + + /// \brief read the next entry in the tree + void readNextEntry(const bool doPrint = false); + + /// \brief read the entry # id in the tree + void readEntry(const Long_t id, const bool doPrint = false); + + /// \brief return the number of entries + Long64_t getNEntries() const { return mNEntries; } + + /// \brief return the name of record data tree + TString getDataTreeName() const { return mDataTreeName; } + + protected: + TChain* mDataTree; ///< TChain container that stores the records + bool mIsSuccessfulInit; ///< boolean to monitor the success of the initialization + bool mIsConstraintsRec; ///< boolean to know if these are data records or constraints records + bool mIsReadEntryOk; ///< boolean to know if the last operation readNextEntry() was ok + TString mDataTreeName; ///< name of the record TTree/TChain + TString mDataBranchName; ///< name of the branch where records will be stored + o2::fwdalign::MillePedeRecord* mRecord; ///< the running record + Long64_t mCurrentDataID; ///< counter indicating the ID of the current record in the tree + Long64_t mNEntries; ///< number of entries in the read TChain + + ClassDef(MilleRecordReader, 0); +}; +} // namespace fwdalign +} // namespace o2 + +#endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h new file mode 100644 index 0000000000000..7af35092c6ff9 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriter.h @@ -0,0 +1,90 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriter.h +/// \author arakotoz@cern.ch +/// \brief Class dedicated to write MillePedeRecords to output file for FWDALIGN + +#ifndef ALICEO2_FWDALIGN_MILLERECORD_WRITER_H +#define ALICEO2_FWDALIGN_MILLERECORD_WRITER_H + +#include +#include + +#include "ForwardAlign/MillePedeRecord.h" + +class TFile; +class TTree; +namespace o2 +{ +namespace fwdalign +{ + +class MilleRecordWriter +{ + public: + /// \brief constructor + MilleRecordWriter(); + + /// \brief destructor + virtual ~MilleRecordWriter(); + + /// \brief Set the number of entries to be used by TTree::AutoSave() + void setCyclicAutoSave(const long nEntries); + + /// \brief choose data records filename + void setDataFileName(TString fname) { mDataFileName = fname; } + + /// \brief choose data records filename + void changeDataBranchName(const bool isConstraintsRec = true); + + /// \brief init output file and tree + void init(); + + /// \brief check if init went well + bool isInitOk() const { return mIsSuccessfulInit; } + + /// \brief return the record + o2::fwdalign::MillePedeRecord* getRecord() { return mRecord; }; + + /// \brief return the ID of the current record in the TTree + Long64_t getCurrentDataID() const { return mCurrentDataID; } + + /// \brief fill tree + void fillRecordTree(const bool doPrint = false); + + /// \brief write tree and close output file + void terminate(); + + /// \brief assign run + void setRecordRun(int run); + + /// \brief assign weight + void setRecordWeight(double wgh); + + protected: + TTree* mDataTree; ///< TTree container that stores the records + TFile* mDataFile; ///< output file where the records are written + bool mIsSuccessfulInit; ///< boolean to monitor the success of the initialization + bool mIsConstraintsRec; ///< boolean to know if these are data records or constraints records + long mNEntriesAutoSave; ///< max entries in the buffer after which TTree::AutoSave() is automatically used + TString mDataFileName; ///< name of the output file that will store the record TTree + TString mDataTreeName; ///< name of the record TTree + TString mDataBranchName; ///< name of the branch where records will be stored + o2::fwdalign::MillePedeRecord* mRecord; ///< the running record + Long64_t mCurrentDataID; ///< counter increasing when adding a record to the tree + + ClassDef(MilleRecordWriter, 0); +}; +} // namespace fwdalign +} // namespace o2 + +#endif diff --git a/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h new file mode 100644 index 0000000000000..b10ab502dace8 --- /dev/null +++ b/Detectors/ForwardAlign/include/ForwardAlign/MilleRecordWriterSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriterSpec.h +/// \brief Implementation of a data processor to write MillePede record in a root file +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#ifndef ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H +#define ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace fwdalign +{ + +framework::DataProcessorSpec getMilleRecordWriterSpec(bool useMC, const char* specName = "fwdalign-millerecord-writer", + const char* fileName = "millerecords.root"); + +} // namespace fwdalign +} // namespace o2 + +#endif // ALICEO2_FWDALIGN_MILLERECORDWRITERSPEC_H \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h b/Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h rename to Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h index 69cbe7faec624..5c0f06e93c49a 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/MinResSolve.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/MinResSolve.h @@ -13,8 +13,8 @@ /// \brief General class (from AliROOT) for solving large system of linear equations /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_MINRESSOLVE_H -#define ALICEO2_MFT_MINRESSOLVE_H +#ifndef ALICEO2_FWDALIGN_MINRESSOLVE_H +#define ALICEO2_FWDALIGN_MINRESSOLVE_H #include #include @@ -22,7 +22,7 @@ namespace o2 { -namespace mft +namespace fwdalign { class MatrixSq; @@ -135,7 +135,7 @@ class MinResSolve : public TObject ClassDefOverride(MinResSolve, 0); }; -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h similarity index 94% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h index 125478f087b61..fae47c5b22eb4 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/RectMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/RectMatrix.h @@ -13,15 +13,15 @@ /// \brief Class for rectangular matrix used for millepede2 operation /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_RECTMATRIX_H -#define ALICEO2_MFT_RECTMATRIX_H +#ifndef ALICEO2_FWDALIGN_RECTMATRIX_H +#define ALICEO2_FWDALIGN_RECTMATRIX_H #include "TObject.h" class TString; namespace o2 { -namespace mft +namespace fwdalign { /// \brief Class for rectangular matrix used for millepede2 operation @@ -82,7 +82,7 @@ inline Double_t& RectMatrix::operator()(Int_t row, Int_t col) return (Double_t&)fRows[row][col]; } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h index 8ae1056c1c184..27b7e9df0d1ff 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymBDMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/SymBDMatrix.h @@ -13,16 +13,16 @@ /// \brief Symmetric Band Diagonal matrix (from AliROOT) with half band width W (+1 for diagonal) /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_SYMBDMATRIX_H -#define ALICEO2_MFT_SYMBDMATRIX_H +#ifndef ALICEO2_FWDALIGN_SYMBDMATRIX_H +#define ALICEO2_FWDALIGN_SYMBDMATRIX_H #include #include -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class SymBDMatrix @@ -170,7 +170,7 @@ inline void SymBDMatrix::MultiplyByVec(const TVectorD& vecIn, TVectorD& vecOut) MultiplyByVec(vecIn.GetMatrixArray(), vecOut.GetMatrixArray()); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h b/Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h rename to Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h index e86c3bb1f9886..8de3867419202 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/SymMatrix.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/SymMatrix.h @@ -13,17 +13,17 @@ /// \brief Fast symmetric matrix (from AliROOT) with dynamically expandable size /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_SYMMATRIX_H -#define ALICEO2_MFT_SYMMATRIX_H +#ifndef ALICEO2_FWDALIGN_SYMMATRIX_H +#define ALICEO2_FWDALIGN_SYMMATRIX_H #include #include -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" namespace o2 { -namespace mft +namespace fwdalign { /// \class SymMatrix @@ -277,7 +277,7 @@ inline void SymMatrix::AddToRow(Int_t r, Double_t* valc, Int_t* indc, Int_t n) } } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h b/Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h rename to Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h index 993ce59014ce7..0e36af5a62c7a 100644 --- a/Detectors/ITSMFT/MFT/alignment/include/MFTAlignment/VectorSparse.h +++ b/Detectors/ForwardAlign/include/ForwardAlign/VectorSparse.h @@ -13,15 +13,15 @@ /// \brief Sparse vector class (from AliROOT), used as row of the MatrixSparse class /// \author ruben.shahoyan@cern.ch -#ifndef ALICEO2_MFT_VECTORSPARSE_H -#define ALICEO2_MFT_VECTORSPARSE_H +#ifndef ALICEO2_FWDALIGN_VECTORSPARSE_H +#define ALICEO2_FWDALIGN_VECTORSPARSE_H #include #include namespace o2 { -namespace mft +namespace fwdalign { /// \class VectorSparse @@ -97,7 +97,7 @@ inline Double_t& VectorSparse::operator()(Int_t ind) return FindIndexAdd(ind); } -} // namespace mft +} // namespace fwdalign } // namespace o2 #endif diff --git a/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h b/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h new file mode 100644 index 0000000000000..eb447d28e7868 --- /dev/null +++ b/Detectors/ForwardAlign/src/ForwardAlignLinkDef.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::fwdalign::MatrixSparse + ; +#pragma link C++ class o2::fwdalign::MatrixSq + ; +#pragma link C++ class o2::fwdalign::MillePede2 + ; +#pragma link C++ class o2::fwdalign::MillePedeRecord + ; +#pragma link C++ class std::vector < o2::fwdalign::MillePedeRecord> + ; +#pragma link C++ class o2::fwdalign::MilleRecordReader + ; +#pragma link C++ class o2::fwdalign::MilleRecordWriter + ; +#pragma link C++ class o2::fwdalign::MinResSolve + ; +#pragma link C++ class o2::fwdalign::RectMatrix + ; +#pragma link C++ class o2::fwdalign::SymBDMatrix + ; +#pragma link C++ class o2::fwdalign::SymMatrix + ; +#pragma link C++ class o2::fwdalign::VectorSparse + ; + +#endif diff --git a/Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx b/Detectors/ForwardAlign/src/MatrixSparse.cxx similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx rename to Detectors/ForwardAlign/src/MatrixSparse.cxx index acbb7ea94a334..210b0c3a77f5c 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MatrixSparse.cxx +++ b/Detectors/ForwardAlign/src/MatrixSparse.cxx @@ -15,9 +15,9 @@ #include #include "Framework/Logger.h" -#include "MFTAlignment/MatrixSparse.h" +#include "ForwardAlign/MatrixSparse.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MatrixSparse); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx b/Detectors/ForwardAlign/src/MatrixSq.cxx similarity index 96% rename from Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx rename to Detectors/ForwardAlign/src/MatrixSq.cxx index abb794def134d..1aadc3b6563d5 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MatrixSq.cxx +++ b/Detectors/ForwardAlign/src/MatrixSq.cxx @@ -17,9 +17,9 @@ #include "TMath.h" #include "Framework/Logger.h" -#include "MFTAlignment/MatrixSq.h" +#include "ForwardAlign/MatrixSq.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MatrixSq); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx b/Detectors/ForwardAlign/src/MillePede2.cxx similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx rename to Detectors/ForwardAlign/src/MillePede2.cxx index af445c14f09c1..5ad475d893271 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MillePede2.cxx +++ b/Detectors/ForwardAlign/src/MillePede2.cxx @@ -10,8 +10,8 @@ // or submit itself to any jurisdiction. /// @file MillePede2.cxx - -#include "MFTAlignment/MillePede2.h" +#include +#include "ForwardAlign/MillePede2.h" #include "Framework/Logger.h" #include #include @@ -27,14 +27,14 @@ #include #include -//#define _DUMP_EQ_BEFORE_ -//#define _DUMP_EQ_AFTER_ +// #define _DUMP_EQ_BEFORE_ +// #define _DUMP_EQ_AFTER_ -//#define _DUMPEQ_BEFORE_ -//#define _DUMPEQ_AFTER_ +// #define _DUMPEQ_BEFORE_ +// #define _DUMPEQ_AFTER_ using std::ifstream; -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MillePede2); @@ -147,7 +147,8 @@ MillePede2::MillePede2(const MillePede2& src) fRecordWriter(nullptr), fConstraintsRecWriter(nullptr), fRecordReader(nullptr), - fConstraintsRecReader(nullptr) + fConstraintsRecReader(nullptr), + fDisableRecordWriter(false) { fWghScl[0] = src.fWghScl[0]; fWghScl[1] = src.fWghScl[1]; @@ -314,16 +315,17 @@ void MillePede2::EndChi2Storage() void MillePede2::SetLocalEquation(std::vector& dergb, std::vector& derlc, const double lMeas, const double lSigma) { - if (!fRecordWriter) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; - return; - } - if (!fRecordWriter->isInitOk()) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; - return; + if (!fDisableRecordWriter) { + if (!fRecordWriter) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; + return; + } + if (!fRecordWriter->isInitOk()) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; + return; + } + SetRecord(fRecordWriter->getRecord()); } - SetRecord(fRecordWriter->getRecord()); - // write data of single measurement if (lSigma <= 0.0) { // If parameter is fixed, then no equation for (int i = fNLocPar; i--;) { @@ -336,7 +338,6 @@ void MillePede2::SetLocalEquation(std::vector& dergb, std::vectorAddResidual(lMeas); - // Retrieve local param interesting indices for (int i = 0; i < fNLocPar; i++) { if (!IsZero(derlc[i])) { @@ -364,16 +365,17 @@ void MillePede2::SetLocalEquation(std::vector& indgb, std::vector& std::vector& derlc, const int nlc, const double lMeas, const double lSigma) { - if (!fRecordWriter) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; - return; - } - if (!fRecordWriter->isInitOk()) { - LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; - return; + if (!fDisableRecordWriter) { + if (!fRecordWriter) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: null pointer to record writer"; + return; + } + if (!fRecordWriter->isInitOk()) { + LOG(fatal) << "MillePede2::SetLocalEquation() - aborted: unintialised record writer"; + return; + } + SetRecord(fRecordWriter->getRecord()); } - SetRecord(fRecordWriter->getRecord()); - if (lSigma <= 0.0) { // If parameter is fixed, then no equation for (int i = nlc; i--;) { derlc[i] = 0.0; @@ -1384,7 +1386,7 @@ int MillePede2::SolveGlobalMatEq() if (fgIterSol == MinResSolve::kSolFGMRes) { res = slv->SolveFGMRES(sol, fgMinResCondType, fgMinResMaxIter, fgMinResTol, fgNKrylovV); } else { - LOGF(warning, "MillePede2 - Undefined Iteritive Solver ID=%d, only %d are defined", fgIterSol, MinResSolve::kNSolvers); + LOGF(warning, "MillePede2 - Undefined Iteritive Solver ID=%d, only %d are defined", fgIterSol, (int)MinResSolve::kNSolvers); } } diff --git a/Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx b/Detectors/ForwardAlign/src/MillePedeRecord.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx rename to Detectors/ForwardAlign/src/MillePedeRecord.cxx index d87a7ee9a77d6..447b01bc60035 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MillePedeRecord.cxx +++ b/Detectors/ForwardAlign/src/MillePedeRecord.cxx @@ -11,11 +11,11 @@ /// @file MillePedeRecord.cxx -#include "MFTAlignment/MillePedeRecord.h" +#include "ForwardAlign/MillePedeRecord.h" #include #include "Framework/Logger.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MillePedeRecord); diff --git a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx b/Detectors/ForwardAlign/src/MilleRecordReader.cxx similarity index 96% rename from Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx rename to Detectors/ForwardAlign/src/MilleRecordReader.cxx index 0cab0726c7e24..927e7972c222b 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordReader.cxx +++ b/Detectors/ForwardAlign/src/MilleRecordReader.cxx @@ -13,11 +13,11 @@ #include "Framework/Logger.h" -#include "MFTAlignment/MilleRecordReader.h" +#include "ForwardAlign/MilleRecordReader.h" -using namespace o2::mft; +using namespace o2::fwdalign; -ClassImp(o2::mft::MilleRecordReader); +ClassImp(o2::fwdalign::MilleRecordReader); //__________________________________________________________________________ MilleRecordReader::MilleRecordReader() @@ -25,7 +25,7 @@ MilleRecordReader::MilleRecordReader() mIsSuccessfulInit(false), mIsConstraintsRec(false), mIsReadEntryOk(false), - mDataTreeName("milleRecords"), + mDataTreeName("o2sim"), mDataBranchName("data"), mRecord(nullptr), mCurrentDataID(-1), diff --git a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx b/Detectors/ForwardAlign/src/MilleRecordWriter.cxx similarity index 95% rename from Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx rename to Detectors/ForwardAlign/src/MilleRecordWriter.cxx index 91b6438d968fd..e36c47562c81e 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MilleRecordWriter.cxx +++ b/Detectors/ForwardAlign/src/MilleRecordWriter.cxx @@ -16,11 +16,11 @@ #include "Framework/Logger.h" -#include "MFTAlignment/MilleRecordWriter.h" +#include "ForwardAlign/MilleRecordWriter.h" -using namespace o2::mft; +using namespace o2::fwdalign; -ClassImp(o2::mft::MilleRecordWriter); +ClassImp(o2::fwdalign::MilleRecordWriter); //__________________________________________________________________________ MilleRecordWriter::MilleRecordWriter() @@ -29,8 +29,8 @@ MilleRecordWriter::MilleRecordWriter() mIsSuccessfulInit(false), mIsConstraintsRec(false), mNEntriesAutoSave(10000), - mDataFileName("mft_mille_records.root"), - mDataTreeName("milleRecords"), + mDataFileName("millerecords.root"), + mDataTreeName("o2sim"), mDataBranchName("data"), mRecord(nullptr), mCurrentDataID(-1) @@ -135,6 +135,7 @@ void MilleRecordWriter::terminate() mDataTree->Write(); LOG(info) << "MilleRecordWriter::terminate() - wrote tree " << mDataTreeName.Data(); + mDataFile->Close(); } } diff --git a/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx b/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx new file mode 100644 index 0000000000000..e3fe8c12a9ad0 --- /dev/null +++ b/Detectors/ForwardAlign/src/MilleRecordWriterSpec.cxx @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MilleRecordWriterSpec.cxx +/// \brief Implementation of a data processor to write MillePede record in a root file +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#include "ForwardAlign/MilleRecordWriterSpec.h" + +#include +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "ForwardAlign/MillePedeRecord.h" + +namespace o2 +{ +namespace fwdalign +{ + +using namespace o2::framework; + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; + +DataProcessorSpec getMilleRecordWriterSpec(bool useMC, const char* specName, const char* fileName) +{ + return MakeRootTreeWriterSpec(specName, + fileName, + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree MillePede records for MCH-MID tracks"}, + BranchDefinition{InputSpec{"data", "MUON", "RECORD_MCHMID", Lifetime::Sporadic}, "data"})(); +} + +} // namespace fwdalign +} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx b/Detectors/ForwardAlign/src/MinResSolve.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx rename to Detectors/ForwardAlign/src/MinResSolve.cxx index 14eae8262c45d..6d1982307fc1e 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/MinResSolve.cxx +++ b/Detectors/ForwardAlign/src/MinResSolve.cxx @@ -16,12 +16,12 @@ #include #include "Framework/Logger.h" -#include "MFTAlignment/MinResSolve.h" -#include "MFTAlignment/MatrixSq.h" -#include "MFTAlignment/MatrixSparse.h" -#include "MFTAlignment/SymBDMatrix.h" +#include "ForwardAlign/MinResSolve.h" +#include "ForwardAlign/MatrixSq.h" +#include "ForwardAlign/MatrixSparse.h" +#include "ForwardAlign/SymBDMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(MinResSolve); diff --git a/Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx b/Detectors/ForwardAlign/src/RectMatrix.cxx similarity index 97% rename from Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx rename to Detectors/ForwardAlign/src/RectMatrix.cxx index a9b958b1fe308..94eb9fdf70359 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/RectMatrix.cxx +++ b/Detectors/ForwardAlign/src/RectMatrix.cxx @@ -12,9 +12,9 @@ /// @file RectMatrix.cxx #include -#include "MFTAlignment/RectMatrix.h" +#include "ForwardAlign/RectMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(RectMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx b/Detectors/ForwardAlign/src/SymBDMatrix.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx rename to Detectors/ForwardAlign/src/SymBDMatrix.cxx index 6dd4129e1d7c7..84d002d0933ea 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/SymBDMatrix.cxx +++ b/Detectors/ForwardAlign/src/SymBDMatrix.cxx @@ -15,9 +15,9 @@ #include "TClass.h" #include "TMath.h" -#include "MFTAlignment/SymBDMatrix.h" +#include "ForwardAlign/SymBDMatrix.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(SymBDMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx b/Detectors/ForwardAlign/src/SymMatrix.cxx similarity index 99% rename from Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx rename to Detectors/ForwardAlign/src/SymMatrix.cxx index 498cad5d080fc..c5e5267645148 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/SymMatrix.cxx +++ b/Detectors/ForwardAlign/src/SymMatrix.cxx @@ -15,10 +15,10 @@ #include #include -#include "MFTAlignment/SymMatrix.h" +#include "ForwardAlign/SymMatrix.h" #include "Framework/Logger.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(SymMatrix); diff --git a/Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx b/Detectors/ForwardAlign/src/VectorSparse.cxx similarity index 98% rename from Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx rename to Detectors/ForwardAlign/src/VectorSparse.cxx index 94463b8650297..b2e7f346b91d2 100644 --- a/Detectors/ITSMFT/MFT/alignment/src/VectorSparse.cxx +++ b/Detectors/ForwardAlign/src/VectorSparse.cxx @@ -13,9 +13,9 @@ #include -#include "MFTAlignment/VectorSparse.h" +#include "ForwardAlign/VectorSparse.h" -using namespace o2::mft; +using namespace o2::fwdalign; ClassImp(VectorSparse); diff --git a/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx b/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx new file mode 100644 index 0000000000000..9144ef1cb1025 --- /dev/null +++ b/Detectors/ForwardAlign/src/millerecord-writer-workflow.cxx @@ -0,0 +1,41 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file millerecord-writer-workflow.cxx +/// \brief Implementation of a DPL device to run the MillePede record writer +/// +/// \author Chi Zhang, CEA-Saclay, chi.zhang@cern.ch + +#include "ForwardAlign/MilleRecordWriterSpec.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:FWDALIGN|fwdalign).*[W,w]riter.*")); +} + +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + workflowOptions.emplace_back("disable-mc", VariantType::Bool, false, + ConfigParamSpec::HelpString{"disable MC propagation even if available"}); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(const ConfigContext& configcontext) +{ + auto useMC = !configcontext.options().get("disable-mc"); + return WorkflowSpec{o2::fwdalign::getMilleRecordWriterSpec(useMC)}; +} \ No newline at end of file diff --git a/Detectors/GLOQC/CMakeLists.txt b/Detectors/GLOQC/CMakeLists.txt new file mode 100644 index 0000000000000..9d2db85460b69 --- /dev/null +++ b/Detectors/GLOQC/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC) + +o2_add_library(GLOQC + TARGETVARNAME targetName + SOURCES src/MatchITSTPCQC.cxx + src/ITSTPCMatchingQCParams.cxx + + PUBLIC_LINK_LIBRARIES O2::DetectorsVertexing + PRIVATE_LINK_LIBRARIES O2::GPUO2Interface + O2::GPUTracking) + +o2_target_root_dictionary(GLOQC + HEADERS include/GLOQC/MatchITSTPCQC.h + include/GLOQC/ITSTPCMatchingQCParams.h + +) + + diff --git a/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h new file mode 100644 index 0000000000000..024497b1b918e --- /dev/null +++ b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Chiara.Zampolli@cern.ch + +#ifndef ALICEO2_ITSTPCMATCHINGQC_PARAMS_H +#define ALICEO2_ITSTPCMATCHINGQC_PARAMS_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::gloqc +{ + +// There are configurable params for TPC-ITS matching +struct ITSTPCMatchingQCParams : public o2::conf::ConfigurableParamHelper { + + int nBinsPt = 100; + float minPtITSCut = 0.1; + float etaITSCut = 1.4; + int32_t minNITSClustersCut = 0; + float maxChi2PerClusterITS = 1e10; + float minPtTPCCut = 0.1; + float etaTPCCut = 1.4; + int32_t minNTPCClustersCut = 60; + float minDCACut = 100.; + float minDCACutY = 10.; + float minPtCut = 0.1; + float maxPtCut = 20; + float etaCut = 1.4; + float etaNo0Cut = 0.05; + float cutK0Mass = 0.05f; + float maxEtaK0 = 0.8f; + float K0Scaling = 1.f; + float minTPCOccpp = 0.f; + float maxTPCOccpp = 1.e6; + int nBinsTPCOccpp = 6; + float minTPCOccPbPb = 0.f; + float maxTPCOccPbPb = 8.e6; + int nBinsTPCOccPbPb = 8; + float maxK0DCA = 0.01; + float minK0CosPA = 0.995; + + O2ParamDef(ITSTPCMatchingQCParams, "ITSTPCMatchingQC"); +}; + +} // namespace o2::gloqc + // end namespace o2 + +#endif diff --git a/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h new file mode 100644 index 0000000000000..356d3e80d210e --- /dev/null +++ b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h @@ -0,0 +1,465 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MatchITSTPCQC.h +/// \brief Class to perform QC for ITSTPC matching +/// \author chiara.zampolli@cern.ch + +#ifndef ALICEO2_GLOQC_MATCHITSTPC_QC_ +#define ALICEO2_GLOQC_MATCHITSTPC_QC_ + +#include +#include +#include +#include +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "Framework/ProcessingContext.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/PID.h" +#include "DCAFitter/DCAFitterN.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUParam.h" +#include "GPUParam.inc" + +#include +#include +#include +#include + +namespace o2 +{ + +namespace gloqc +{ + +using GID = o2::dataformats::GlobalTrackID; + +struct LblInfo { + + int mIdx = -1; + bool mIsPhysicalPrimary = false; +}; + +class MatchITSTPCQC +{ + public: + enum matchType : uint8_t { TPC = 0, + ITS, + SIZE }; + + MatchITSTPCQC() = default; + MatchITSTPCQC(const MatchITSTPCQC&) = delete; + MatchITSTPCQC(MatchITSTPCQC&&) = delete; + MatchITSTPCQC& operator=(const MatchITSTPCQC&) = delete; + MatchITSTPCQC& operator=(MatchITSTPCQC&&) = delete; + ~MatchITSTPCQC(); + + bool init(); + void initDataRequest(); + void run(o2::framework::ProcessingContext& ctx); + void setDataRequest(const std::shared_ptr& dr) { mDataRequest = dr; } + void finalize(); + void reset(); + bool processV0(int iv, o2::globaltracking::RecoContainer& recoData, std::vector& mTBinClOcc, float pvTime); + bool refitV0(const o2::dataformats::V0Index& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData); + + TH1D* getHistoPtNum(matchType m) const { return mPtNum[m]; } + TH1D* getHistoPtDen(matchType m) const { return mPtDen[m]; } + TEfficiency* getFractionITSTPCmatch(matchType m) const { return mFractionITSTPCmatch[m]; } + + TH1D* getHistoPtNumNoEta0(matchType m) const { return mPtNum_noEta0[m]; } + TH1D* getHistoPtDenNoEta0(matchType m) const { return mPtDen_noEta0[m]; } + TEfficiency* getFractionITSTPCmatchNoEta0(matchType m) const { return mFractionITSTPCmatch_noEta0[m]; } + + TH1F* getHistoPhiNum(matchType m) const { return mPhiNum[m]; } + TH1F* getHistoPhiDen(matchType m) const { return mPhiDen[m]; } + TEfficiency* getFractionITSTPCmatchPhi(matchType m) const { return mFractionITSTPCmatchPhi[m]; } + + TH2F* getHistoPhiVsPtNum(matchType m) const { return mPhiVsPtNum[m]; } + TH2F* getHistoPhiVsPtDen(matchType m) const { return mPhiVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiVsPt(matchType m) const { return mFractionITSTPCmatchPhiVsPt[m]; } + + TH1F* getHistoEtaNum(matchType m) const { return mEtaNum[m]; } + TH1F* getHistoEtaDen(matchType m) const { return mEtaDen[m]; } + TEfficiency* getFractionITSTPCmatchEta(matchType m) const { return mFractionITSTPCmatchEta[m]; } + + TH2F* getHistoEtaVsPtNum(matchType m) const { return mEtaVsPtNum[m]; } + TH2F* getHistoEtaVsPtDen(matchType m) const { return mEtaVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaVsPt(matchType m) const { return mFractionITSTPCmatchEtaVsPt[m]; } + + TH2F* getHistoClsVsPtNum(matchType m) const { return mClsVsPtNum[m]; } + TH2F* getHistoClsVsPtDen(matchType m) const { return mClsVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchClsVsPt(matchType m) const { return mFractionITSTPCmatchClsVsPt[m]; } + + TH2F* getHistoChi2VsPtNum(matchType m) const { return mChi2VsPtNum[m]; } + TH2F* getHistoChi2VsPtDen(matchType m) const { return mChi2VsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchChi2VsPt(matchType m) const { return mFractionITSTPCmatchChi2VsPt[m]; } + + TH1F* getHistoPtPhysPrimNum(matchType m) const { return mPtPhysPrimNum[m]; } + TH1F* getHistoPtPhysPrimDen(matchType m) const { return mPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim(matchType m) const { return mFractionITSTPCmatchPhysPrim[m]; } + + TH1F* getHistoPhiPhysPrimNum(matchType m) const { return mPhiPhysPrimNum[m]; } + TH1F* getHistoPhiPhysPrimDen(matchType m) const { return mPhiPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiPhysPrim(matchType m) const { return mFractionITSTPCmatchPhiPhysPrim[m]; } + + TH1F* getHistoEtaPhysPrimNum(matchType m) const { return mEtaPhysPrimNum[m]; } + TH1F* getHistoEtaPhysPrimDen(matchType m) const { return mEtaPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaPhysPrim(matchType m) const { return mFractionITSTPCmatchEtaPhysPrim[m]; } + + TH2F* getHistoResidualPt() const { return mResidualPt; } + TH2F* getHistoResidualPhi() const { return mResidualPhi; } + TH2F* getHistoResidualEta() const { return mResidualEta; } + + TH1F* getHistoChi2Matching() const { return mChi2Matching; } + TH1F* getHistoChi2Refit() const { return mChi2Refit; } + TH2F* getHistoTimeResVsPt() const { return mTimeResVsPt; } + TH1F* getHistoDCAr() const { return mDCAr; } + TH2F* getHistoDCArVsPtNum() const { return mDCArVsPtNum; } + TH2F* getHistoDCArVsPtDen() const { return mDCArVsPtDen; } + TEfficiency* getFractionITSTPCmatchDCArVsPt() const { return mFractionITSTPCmatchDCArVsPt; } + + TH1D* getHisto1OverPtNum(matchType m) const { return m1OverPtNum[m]; } + TH1D* getHisto1OverPtDen(matchType m) const { return m1OverPtDen[m]; } + TEfficiency* getFractionITSTPCmatch1OverPt(matchType m) const { return mFractionITSTPCmatch1OverPt[m]; } + + TH1D* getHisto1OverPtPhysPrimNum(matchType m) const { return m1OverPtPhysPrimNum[m]; } + TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } + + TH3F* getHistoEtaPhiPtNum(matchType m) const { return mEtaPhiPtNum[m]; } + TH3F* getHistoEtaPhiPtDen(matchType m) const { return mEtaPhiPtDen[m]; } + + TH3F* getHistoK0MassVsPtVsOccpp() const { return mK0MassVsPtVsOccpp; } + TH3F* getHistoK0MassVsPtVsOccPbPb() const { return mK0MassVsPtVsOccPbPb; } + + void getHistos(TObjArray& objar); + + /// \brief Publishes the histograms to the publisher e.g. the one provided by the QC task + /// \tparam T type of the publisher + /// \param publisher the publisher e.g. getObjectsManager() + template + void publishHistograms(const std::shared_ptr& publisher) + { + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + publisher->startPublishing(mPtNum[i]); + publisher->startPublishing(mPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch[i]); + + publisher->startPublishing(mPtNum_noEta0[i]); + publisher->startPublishing(mPtDen_noEta0[i]); + publisher->startPublishing(mFractionITSTPCmatch_noEta0[i]); + + // Phi + publisher->startPublishing(mPhiNum[i]); + publisher->startPublishing(mPhiDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhi[i]); + + publisher->startPublishing(mPhiVsPtNum[i]); + publisher->startPublishing(mPhiVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsPt[i]); + + // Eta + publisher->startPublishing(mEtaNum[i]); + publisher->startPublishing(mEtaDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEta[i]); + + publisher->startPublishing(mEtaVsPtNum[i]); + publisher->startPublishing(mEtaVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsPt[i]); + + // Clusters + publisher->startPublishing(mClsVsPtNum[i]); + publisher->startPublishing(mClsVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchClsVsPt[i]); + + // Chi2 + publisher->startPublishing(mChi2VsPtNum[i]); + publisher->startPublishing(mChi2VsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchChi2VsPt[i]); + + // 1/pt + publisher->startPublishing(m1OverPtNum[i]); + publisher->startPublishing(m1OverPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch1OverPt[i]); + + // 3D eta/phi/pt + publisher->startPublishing(mEtaPhiPtNum[i]); + publisher->startPublishing(mEtaPhiPtDen[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + publisher->startPublishing(mPtNumVsTrkPID[i][j]); + publisher->startPublishing(mPtDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPtVsTrkPID[i][j]); + + // Phi + publisher->startPublishing(mPhiNumVsTrkPID[i][j]); + publisher->startPublishing(mPhiDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + + // Eta + publisher->startPublishing(mEtaNumVsTrkPID[i][j]); + publisher->startPublishing(mEtaDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + if (mUseMC) { + publisher->startPublishing(mPhiPhysPrimNum[i]); + publisher->startPublishing(mPhiPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiPhysPrim[i]); + publisher->startPublishing(mPtPhysPrimNum[i]); + publisher->startPublishing(mPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim[i]); + publisher->startPublishing(mEtaPhysPrimNum[i]); + publisher->startPublishing(mEtaPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaPhysPrim[i]); + publisher->startPublishing(m1OverPtPhysPrimNum[i]); + publisher->startPublishing(m1OverPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } + } + publisher->startPublishing(mChi2Matching); + publisher->startPublishing(mChi2Refit); + publisher->startPublishing(mTimeResVsPt); + publisher->startPublishing(mResidualPt); + publisher->startPublishing(mResidualPhi); + publisher->startPublishing(mResidualEta); + publisher->startPublishing(mDCAr); + publisher->startPublishing(mDCArVsPtNum); + publisher->startPublishing(mDCArVsPtDen); + publisher->startPublishing(mFractionITSTPCmatchDCArVsPt); + if (mDoK0QC) { + publisher->startPublishing(mK0MassVsPtVsOccpp); + publisher->startPublishing(mK0MassVsPtVsOccPbPb); + } + } + + void setTrkSources(GID::mask_t src) { mSrc = src; } + void setUseTrkPID(bool b) { mUseTrkPID = b; } + bool getUseTrkPID() const { return mUseTrkPID; } + void setUseMC(bool b) { mUseMC = b; } + bool getUseMC() const { return mUseMC; } + void deleteHistograms(); + void setBz(float bz) { mBz = bz; } + void setDoK0QC(bool v) { mDoK0QC = v; } + bool getDoK0QC() const { return mDoK0QC; } + + // ITS track + void setMinPtITSCut(float v) { mPtITSCut = v; }; + void setEtaITSCut(float v) { mEtaITSCut = v; }; // TODO: define 2 different values for min and max (**) + void setMinNClustersITS(int v) { mMinNClustersITS = v; } + void setMaxChi2PerClusterITS(float v) { mMaxChi2PerClusterITS = v; } + // TO DO: define an agreed way to implement the setter for ITS matching (min. # layers, which layers) + // [...] --> exploit the method TrackCuts::setRequireHitsInITSLayers(...) + // TPC track + void setMinPtTPCCut(float v) { mPtTPCCut = v; }; + void setEtaTPCCut(float v) { mEtaTPCCut = v; }; // TODO: define 2 different values for min and max (***) + void setMinNTPCClustersCut(int v) { mNTPCClustersCut = v; } + void setMinDCAtoBeamPipeCut(std::array v) + { + setMinDCAtoBeamPipeDistanceCut(v[0]); + setMinDCAtoBeamPipeYCut(v[1]); + } + void setMinDCAtoBeamPipeDistanceCut(float v) { mDCATPCCut = v; } + void setMinDCAtoBeamPipeYCut(float v) { mDCATPCCutY = v; } + // ITS-TPC kinematics + void setNBinsPt(int v) { mPtBins = v; } + void setPtCut(float v) { mPtCut = v; } + void setMaxPtCut(float v) { mPtMaxCut = v; } + void setEtaCut(float v) { mEtaCut = v; } + void setEtaNo0Cut(float v) { mEtaNo0Cut = v; } + + // K0 + void setMaxK0Eta(float v) { mMaxEtaK0 = v; } + void setRefitK0(bool v) { mRefit = v; } + void setCutK0Mass(float v) { mCutK0Mass = v; } + void setMinTPCOccpp(float v) { mMinTPCOccpp = v; } + void setMaxTPCOccpp(float v) { mMaxTPCOccpp = v; } + void setNBinsTPCOccpp(int v) { mNBinsTPCOccpp = v; } + void setMinTPCOccPbPb(float v) { mMinTPCOccPbPb = v; } + void setMaxTPCOccPbPb(float v) { mMaxTPCOccPbPb = v; } + void setNBinsTPCOccPbPb(int v) { mNBinsTPCOccPbPb = v; } + void setK0Scaling(float v) { mK0Scaling = v; } + float getK0Scaling() const { return mK0Scaling; } + void setK0MaxDCA(float v) { mK0MaxDCA = v; } + float getK0MaxDCA() const { return mK0MaxDCA; } + void setK0MinCosPA(float v) { mK0MinCosPA = v; } + float getK0MinCosPA() const { return mK0MinCosPA; } + + void printParams() const; + + private: + std::shared_ptr mDataRequest; + o2::globaltracking::RecoContainer mRecoCont; + std::string mRequestedSources = "ITS,TPC,ITS-TPC"; + GID::mask_t mSrc = GID::getSourcesMask("ITS,TPC,ITS-TPC"); + GID::mask_t mAllowedSources = GID::getSourcesMask("all"); + // TPC + gsl::span mTPCTracks; + // ITS + gsl::span mITSTracks; + // ITS-TPC + gsl::span mITSTPCTracks; + bool mUseMC = false; // Usage of the MC information + bool mUseTrkPID = false; // Usage of the PID hypothesis in tracking + float mBz = 0; ///< nominal Bz + std::array, matchType::SIZE> mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, + // value is the LbLinfo with the id of the track with the highest pT found with that label so far, + // and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapRefLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, + // value is the LblInfo with the id of the track with the highest number of TPC clusters found + // with that label so far, and the flag to say if it is a physical primary or not + o2::steer::MCKinematicsReader mcReader; // reader of MC information + + // Pt + TH1D* mPtNum[matchType::SIZE] = {}; + TH1D* mPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch[matchType::SIZE] = {}; + TH1D* mPtNum_noEta0[matchType::SIZE] = {}; + TH1D* mPtDen_noEta0[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch_noEta0[matchType::SIZE] = {}; + TH1F* mPtPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim[matchType::SIZE] = {}; + // Pt split per PID hypothesis in tracking + TH1D* mPtNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPtDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPtVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Phi + TH1F* mPhiNum[matchType::SIZE] = {}; + TH1F* mPhiDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhi[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiPhysPrim[matchType::SIZE] = {}; + TH2F* mPhiVsPtNum[matchType::SIZE] = {}; + TH2F* mPhiVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsPt[matchType::SIZE] = {}; + // Phi split per PID hypothesis in tracking + TH1D* mPhiNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPhiDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Eta + TH1F* mEtaNum[matchType::SIZE] = {}; + TH1F* mEtaDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEta[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimNum[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaPhysPrim[matchType::SIZE] = {}; + TH2F* mEtaVsPtNum[matchType::SIZE] = {}; + TH2F* mEtaVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsPt[matchType::SIZE] = {}; + // Clusters + TH2F* mClsVsPtNum[matchType::SIZE] = {}; + TH2F* mClsVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchClsVsPt[matchType::SIZE] = {}; + // Chi2 + TH2F* mChi2VsPtNum[matchType::SIZE] = {}; + TH2F* mChi2VsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchChi2VsPt[matchType::SIZE] = {}; + // Eta split per PID hypothesis in tracking + TH1D* mEtaNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mEtaDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + // Residuals + TH2F* mResidualPt = nullptr; + TH2F* mResidualPhi = nullptr; + TH2F* mResidualEta = nullptr; + // Others + TH1F* mChi2Matching = nullptr; + TH1F* mChi2Refit = nullptr; + TH2F* mTimeResVsPt = nullptr; + TH1F* mDCAr = nullptr; + TH2F* mDCArVsPtNum = nullptr; + TH2F* mDCArVsPtDen = nullptr; + TEfficiency* mFractionITSTPCmatchDCArVsPt = nullptr; + // 1/Pt + TH1D* m1OverPtNum[matchType::SIZE] = {}; + TH1D* m1OverPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch1OverPt[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimNum[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim1OverPt[matchType::SIZE] = {}; + // 3D Efficiency in eta/phi/pt + TH3F* mEtaPhiPtNum[matchType::SIZE] = {}; + TH3F* mEtaPhiPtDen[matchType::SIZE] = {}; + + template + void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden); + + int mNTPCSelectedTracks = 0; + int mNITSSelectedTracks = 0; + int mNITSTPCSelectedTracks[matchType::SIZE] = {0, 0}; + + // cut values + // ITS track + float mPtITSCut = 0.1f; // min pT for ITS track + float mEtaITSCut = 1.4f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS = 0; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + // TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS-TPC kinematics + int mPtBins = 100; + float mPtCut = 0.1f; + float mPtMaxCut = 20; + float mEtaCut = 1.4f; + float mEtaNo0Cut = 0.05f; + // TODO: define 2 different values for min and max (*) + + // for V0s + o2::vertexing::DCAFitterN<2> mFitterV0; + TH3F* mK0MassVsPtVsOccpp = nullptr; + TH3F* mK0MassVsPtVsOccPbPb = nullptr; + bool mDoK0QC = false; // whether to fill the K0 QC plot(s) + float mCutK0Mass = 0.05; // cut on the difference between the K0 mass and the PDG mass + bool mRefit = false; // whether to refit or not + float mMaxEtaK0 = 0.8; // cut on the K0 eta + long int mTimestamp = -1; // timestamp used to load the SVertexParam object: if differnt from -1, we don't load (it means we already did it) + std::unique_ptr mConfig; + std::unique_ptr mConfParam; + // std::unique_ptr mParam; + std::shared_ptr mParam = nullptr; + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv{}; + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map + bool mIsHI = false; + float mK0Scaling = 1.f; // permill that we want to keep of K0S + uint64_t mNK0 = 0; // number of found V0s + float mMinTPCOccpp = 0.f; // min TPC occupancy for K0s plot for pp collisions + float mMaxTPCOccpp = 1.e6; // max TPC occupancy for K0s plot for pp collisions + int mNBinsTPCOccpp = 6; // number of bins in TPC occupancy for K0s plot for pp collisions + float mMinTPCOccPbPb = 0.f; // min TPC occupancy for K0s plot for PbPb collisions + float mMaxTPCOccPbPb = 8.e6; // max TPC occupancy for K0s plot for PbPb collisions + int mNBinsTPCOccPbPb = 8; // number of bins in TPC occupancy for K0s plot for PbPb collisions + float mK0MaxDCA = 0.01; // max DCA to select the K0 + float mK0MinCosPA = 0.995; // min cosPA to select the K0 + + ClassDefNV(MatchITSTPCQC, 4); +}; +} // namespace gloqc +} // namespace o2 + +#endif diff --git a/Detectors/GLOQC/src/GLOQCLinkDef.h b/Detectors/GLOQC/src/GLOQCLinkDef.h new file mode 100644 index 0000000000000..f7092a4394d08 --- /dev/null +++ b/Detectors/GLOQC/src/GLOQCLinkDef.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::gloqc::ITSTPCMatchingQCParams> + ; +#pragma link C++ class o2::gloqc::ITSTPCMatchingQCParams + ; + +#endif diff --git a/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx b/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx new file mode 100644 index 0000000000000..b5c9f6842dfc3 --- /dev/null +++ b/Detectors/GLOQC/src/ITSTPCMatchingQCParams.cxx @@ -0,0 +1,16 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSTPCMatchingQCParams.h +/// \brief Configurable params for ITSTPC matching QC + +#include "GLOQC/ITSTPCMatchingQCParams.h" +O2ParamImpl(o2::gloqc::ITSTPCMatchingQCParams); diff --git a/Detectors/GLOQC/src/MatchITSTPCQC.cxx b/Detectors/GLOQC/src/MatchITSTPCQC.cxx new file mode 100644 index 0000000000000..e1832056f072c --- /dev/null +++ b/Detectors/GLOQC/src/MatchITSTPCQC.cxx @@ -0,0 +1,1449 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include + +#include "GLOQC/MatchITSTPCQC.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DetectorsBase/Propagator.h" +#include "SimulationDataFormat/MCUtils.h" +#include "GlobalTracking/TrackCuts.h" +#include +#include +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/V0.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "Framework/InputRecord.h" +#include "Framework/TimingInfo.h" +#include "GPUO2InterfaceUtils.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "GPUO2InterfaceRefit.h" + +using namespace o2::gloqc; +using namespace o2::mcutils; +using MCTrack = o2::MCTrackT; +using DetID = o2::detectors::DetID; + +MatchITSTPCQC::~MatchITSTPCQC() +{ + deleteHistograms(); +} + +//_______________________________________________________ + +void MatchITSTPCQC::deleteHistograms() +{ + + LOG(debug) << "Deleting histos..."; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + delete mPtNum[i]; + delete mPtDen[i]; + delete mFractionITSTPCmatch[i]; + delete mPtNum_noEta0[i]; + delete mPtDen_noEta0[i]; + delete mFractionITSTPCmatch_noEta0[i]; + delete mPtPhysPrimNum[i]; + delete mPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim[i]; + + // Phi + delete mPhiNum[i]; + delete mPhiDen[i]; + delete mFractionITSTPCmatchPhi[i]; + delete mPhiPhysPrimNum[i]; + delete mPhiPhysPrimDen[i]; + delete mFractionITSTPCmatchPhiPhysPrim[i]; + delete mPhiVsPtNum[i]; + delete mPhiVsPtDen[i]; + delete mFractionITSTPCmatchPhiVsPt[i]; + + // Eta + delete mEtaNum[i]; + delete mEtaDen[i]; + delete mFractionITSTPCmatchEta[i]; + delete mEtaPhysPrimNum[i]; + delete mEtaPhysPrimDen[i]; + delete mFractionITSTPCmatchEtaPhysPrim[i]; + delete mEtaVsPtNum[i]; + delete mEtaVsPtDen[i]; + delete mFractionITSTPCmatchEtaVsPt[i]; + + // Clusters + delete mClsVsPtNum[i]; + delete mClsVsPtDen[i]; + delete mFractionITSTPCmatchClsVsPt[i]; + + // Chi2 + delete mChi2VsPtNum[i]; + delete mChi2VsPtDen[i]; + delete mFractionITSTPCmatchChi2VsPt[i]; + + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + delete mPtNumVsTrkPID[i][j]; + delete mPtDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPtVsTrkPID[i][j]; + // Phi + delete mPhiNumVsTrkPID[i][j]; + delete mPhiDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPhiVsTrkPID[i][j]; + // Eta + delete mEtaNumVsTrkPID[i][j]; + delete mEtaDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchEtaVsTrkPID[i][j]; + } + + // 1/Pt + delete m1OverPtNum[i]; + delete m1OverPtDen[i]; + delete mFractionITSTPCmatch1OverPt[i]; + delete m1OverPtPhysPrimNum[i]; + delete m1OverPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + + // 3D eta/phi/pt + delete mEtaPhiPtNum[i]; + delete mEtaPhiPtDen[i]; + } + + // Residuals + delete mResidualPt; + delete mResidualPhi; + delete mResidualEta; + // Others + delete mChi2Matching; + delete mChi2Refit; + delete mTimeResVsPt; + delete mDCAr; + delete mDCArVsPtNum; + delete mDCArVsPtDen; + delete mFractionITSTPCmatchDCArVsPt; + + // K0 + delete mK0MassVsPtVsOccpp; + delete mK0MassVsPtVsOccPbPb; +} + +//__________________________________________________________ + +void MatchITSTPCQC::reset() +{ + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i]->Reset(); + mPtDen[i]->Reset(); + mPtNum_noEta0[i]->Reset(); + mPtDen_noEta0[i]->Reset(); + + // Phi + mPhiNum[i]->Reset(); + mPhiDen[i]->Reset(); + mPhiVsPtNum[i]->Reset(); + mPhiVsPtDen[i]->Reset(); + + // Eta + mEtaNum[i]->Reset(); + mEtaDen[i]->Reset(); + mEtaVsPtNum[i]->Reset(); + mEtaVsPtDen[i]->Reset(); + + // Clusters + mClsVsPtNum[i]->Reset(); + mClsVsPtDen[i]->Reset(); + + // Chi2 + mChi2VsPtNum[i]->Reset(); + mChi2VsPtDen[i]->Reset(); + + // 1/Pt + m1OverPtNum[i]->Reset(); + m1OverPtDen[i]->Reset(); + + // 3D eta/phi/pt + mEtaPhiPtNum[i]->Reset(); + mEtaPhiPtDen[i]->Reset(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j]->Reset(); + mPtDenVsTrkPID[i][j]->Reset(); + // Phi + mPhiNumVsTrkPID[i][j]->Reset(); + mPhiDenVsTrkPID[i][j]->Reset(); + // Eta + mEtaNumVsTrkPID[i][j]->Reset(); + mEtaDenVsTrkPID[i][j]->Reset(); + } + } + + if (mUseMC) { + mPtPhysPrimNum[i]->Reset(); + mPtPhysPrimDen[i]->Reset(); + + mPhiPhysPrimNum[i]->Reset(); + mPhiPhysPrimDen[i]->Reset(); + + mEtaPhysPrimNum[i]->Reset(); + mEtaPhysPrimDen[i]->Reset(); + + m1OverPtPhysPrimNum[i]->Reset(); + m1OverPtPhysPrimDen[i]->Reset(); + } + } + + // Residuals + mResidualPt->Reset(); + mResidualPhi->Reset(); + mResidualEta->Reset(); + // Others + mChi2Matching->Reset(); + mChi2Refit->Reset(); + mTimeResVsPt->Reset(); + mDCAr->Reset(); + mDCArVsPtNum->Reset(); + mDCArVsPtDen->Reset(); + + // K0 + if (mDoK0QC) { + mK0MassVsPtVsOccpp->Reset(); + mK0MassVsPtVsOccPbPb->Reset(); + } +} + +//__________________________________________________________ +bool MatchITSTPCQC::init() +{ + LOGP(debug, "Creating Variable Binning"); + std::array title{"TPC", "ITS"}; + std::array etaSel{Form(", |#eta| < %.1f", mEtaTPCCut), Form(", |#eta| < %.1f", mEtaCut)}; + std::array maxNCls{156, 7}; + // log binning for pT + const Int_t nbinsPt = mPtBins; + const Double_t xminPt = (mPtCut > 0) ? mPtCut : 0.01; + const Double_t xmaxPt = mPtMaxCut; + Double_t* xbinsPt = new Double_t[nbinsPt + 1]; + Double_t xlogminPt = TMath::Log10(xminPt); + Double_t xlogmaxPt = TMath::Log10(xmaxPt); + Double_t dlogxPt = (xlogmaxPt - xlogminPt) / nbinsPt; + for (int i = 0; i <= nbinsPt; i++) { + Double_t xlogPt = xlogminPt + i * dlogxPt; + xbinsPt[i] = TMath::Exp(TMath::Log(10) * xlogPt); + } + + LOGP(debug, "Creating Histograms"); + // Data and MC + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i] = new TH1D(Form("mPtNum_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum[i]->Sumw2(); + mPtNum[i]->SetOption("logy"); + mPtNum[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen[i] = new TH1D(Form("mPtDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtDen[i]->Sumw2(); + mPtDen[i]->SetOption("logy"); + mPtDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch[i] = new TEfficiency(Form("mFractionITSTPCmatch_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum_noEta0[i] = new TH1D(Form("mPtNum_noEta0_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks without |eta| < %.2f, wrt %s tracks %s; Pt [GeV/c]; dNdPt", mEtaNo0Cut, title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtNum_noEta0[i]->Sumw2(); + mPtNum_noEta0[i]->SetOption("logy"); + mPtNum_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen_noEta0[i] = new TH1D(Form("mPtDen_noEta0_%s", title[i].c_str()), Form("Pt distribution of %s tracks without |eta| < %.2f %s; Pt [GeV/c]; dNdPt", title[i].c_str(), mEtaNo0Cut, etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + mPtDen_noEta0[i]->Sumw2(); + mPtDen_noEta0[i]->SetOption("logy"); + mPtDen_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch_noEta0[i] = new TEfficiency(Form("mFractionITSTPCmatch_noEta0_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt without |eta| < %.2f %s; Pt [GeV/c]; Eff", title[i].c_str(), mEtaNo0Cut, etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut); + + // Phi + mPhiNum[i] = new TH1F(Form("mPhiNum_%s", title[i].c_str()), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiNum[i]->Sumw2(); + mPhiDen[i] = new TH1F(Form("mPhiDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiDen[i]->Sumw2(); + mFractionITSTPCmatchPhi[i] = new TEfficiency(Form("mFractionITSTPCmatchPhi_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i] = new TH2F(Form("mPhiVsPtNum_%s", title[i].c_str()), Form("Phi vs Pt distribution of ITSTPC matched tracks wrt %s %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i]->Sumw2(); + mPhiVsPtDen[i] = new TH2F(Form("mPhiVsPtDen_%s", title[i].c_str()), Form("Phi vs Pt distribution of %s tracks %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchPhiVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks %s, Phi vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNum[i] = new TH1F(Form("mEtaNum_%s", title[i].c_str()), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaNum[i]->Sumw2(); + mEtaNum[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaDen[i] = new TH1F(Form("mEtaDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaDen[i]->Sumw2(); + mEtaDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatchEta[i] = new TEfficiency(Form("mFractionITSTPCmatchEta_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks , wrt %s tracks, vs Eta; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + mEtaVsPtNum[i] = new TH2F(Form("mEtaVsPtNum_%s", title[i].c_str()), Form("Eta vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + mEtaVsPtNum[i]->Sumw2(); + mEtaVsPtDen[i] = new TH2F(Form("mEtaVsPtDen_%s", title[i].c_str()), Form("Eta vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + mEtaVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchEtaVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Eta vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 100, -2.f, 2.f); + + // Clusters + mClsVsPtNum[i] = new TH2F(Form("mClsVsPtNum_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + mClsVsPtNum[i]->Sumw2(); + mClsVsPtDen[i] = new TH2F(Form("mClsVsPtDen_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + mClsVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchClsVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchClsVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, #Clusters vs Pt; #it{p}_{T} [GeV#it{c}]; #Clusters; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, maxNCls[i], 0, maxNCls[i]); + + // Chi2 + mChi2VsPtNum[i] = new TH2F(Form("mChi2VsPtNum_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + mChi2VsPtNum[i]->Sumw2(); + mChi2VsPtDen[i] = new TH2F(Form("mChi2VsPtDen_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + mChi2VsPtDen[i]->Sumw2(); + mFractionITSTPCmatchChi2VsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchChi2VsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Chi2 vs Pt; #it{p}_{T} [GeV#it{c}]; Chi2; Eff", title[i].c_str()), mPtBins, mPtCut, mPtMaxCut, 200, 0, 300); + + // 1/pt + m1OverPtNum[i] = new TH1D(Form("m1OverPtNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks, wrt %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtNum[i]->Sumw2(); + m1OverPtDen[i] = new TH1D(Form("m1OverPtDen_%s", title[i].c_str()), Form("1/Pt distribution of %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtDen[i]->Sumw2(); + mFractionITSTPCmatch1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatch1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt, wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + // 3d eta/phi/pt + mEtaPhiPtNum[i] = new TH3F(Form("mEtaPhiPtNum_%s", title[i].c_str()), Form("Numerator #eta vs #varphi vs #it{p}_{T}, wrt %s;#eta %s;#varphi;#it{p}_{T} [GeV#it{c}];Entries", title[i].c_str(), etaSel[i].c_str()), 100, -2., 2., 100, 0., 2 * TMath::Pi(), 100, 0.01, 20.); + mEtaPhiPtNum[i]->Sumw2(); + mEtaPhiPtDen[i] = new TH3F(Form("mEtaPhiPtDen_%s", title[i].c_str()), Form("Denominator #eta vs #varphi vs #it{p}_{T}, wrt %s;#eta %s;#varphi;#it{p}_{T} [GeV#it{c}];Entries", title[i].c_str(), etaSel[i].c_str()), 100, -2., 2., 100, 0., 2 * TMath::Pi(), 100, 0.01, 20.); + mEtaPhiPtDen[i]->Sumw2(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j] = new TH1D(Form("mPtNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + mPtNumVsTrkPID[i][j]->Sumw2(); + mPtDenVsTrkPID[i][j] = new TH1D(Form("mPtDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + mPtDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPtVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPtVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s, TrkPID %i; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str(), j), mPtBins, mPtCut, mPtMaxCut); + + // Phi + mPhiNumVsTrkPID[i][j] = new TH1D(Form("mPhiNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiNumVsTrkPID[i][j]->Sumw2(); + mPhiDenVsTrkPID[i][j] = new TH1D(Form("mPhiDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPhiVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Phi %s, TrkPID %i; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNumVsTrkPID[i][j] = new TH1D(Form("mEtaNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaNumVsTrkPID[i][j]->Sumw2(); + mEtaDenVsTrkPID[i][j] = new TH1D(Form("mEtaDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchEtaVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Eta %s, TrkPID %i; Eta; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + } + } + } + + mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", mPtBins, mPtCut, mPtMaxCut, 100, -1.f, 1.f); + mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); + mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); + mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); + mChi2Matching->SetOption("logy"); + mChi2Matching->GetYaxis()->SetTitleOffset(1.4); + mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); + mChi2Refit->SetOption("logy"); + mChi2Refit->GetYaxis()->SetTitleOffset(1.4); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtNum->Sumw2(); + mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtDen->Sumw2(); + mFractionITSTPCmatchDCArVsPt = new TEfficiency("mFractionITSTPCmatchDCArVsPt", "Fraction of ITSTPC matched tracks wrt TPC vs DCAr; #it{p}_{T} [GeV#it{c}]; DCAr; Eff", 100, 0, 20., 200, -30, 30); + + mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); + mTimeResVsPt->SetOption("colz logz logy logx"); + mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); + + if (mUseMC) { + mcReader.initFromDigitContext("collisioncontext.root"); + + for (int i = 0; i < matchType::SIZE; ++i) { + mPtPhysPrimNum[i] = new TH1F(Form("mPtPhysPrimNum_%s", title[i].c_str()), Form("Pt distribution of matched tracks (physical primary), wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimNum[i]->Sumw2(); + mPtPhysPrimDen[i] = new TH1F(Form("mPtPhysPrimDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks (physical primary) %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhiPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi (physical primary), wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + + mEtaPhysPrimNum[i] = new TH1F(Form("mEtaPhysPrimNum_%s", title[i].c_str()), Form("Eta distribution of matched tracks (physical primary), wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimNum[i]->Sumw2(); + mEtaPhysPrimDen[i] = new TH1F(Form("mEtaPhysPrimDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks (physical primary); Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchEtaPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Eta (physical primary), wrt %s tracks; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + + mPhiPhysPrimNum[i] = new TH1F(Form("mPhiPhysPrimNum_%s", title[i].c_str()), Form("Phi distribution of matched tracks (physical primary), wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimNum[i]->Sumw2(); + mPhiPhysPrimDen[i] = new TH1F(Form("mPhiPhysPrimDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks (physical primary) %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Pt (physical primary), wrt %s tracks %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + + m1OverPtPhysPrimNum[i] = new TH1D(Form("m1OverPtPhysPrimNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + m1OverPtPhysPrimNum[i]->Sumw2(); + m1OverPtPhysPrimDen[i] = new TH1D(Form("m1OverPtPhysPrimDen_%s", title[i].c_str()), Form("1/PtPt distribution of %s tracks (physical primary) %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + m1OverPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 2 * mPtBins, -20., 20.); + } + } + + // log binning for pT for K0s + const Int_t nbinsPtK0 = 10; + const Double_t xminPtK0 = 0.01; + const Double_t xmaxPtK0 = 20; + Double_t* xbinsPtK0 = new Double_t[nbinsPtK0 + 1]; + Double_t xlogminPtK0 = TMath::Log10(xminPtK0); + Double_t xlogmaxPtK0 = TMath::Log10(xmaxPtK0); + Double_t dlogxPtK0 = (xlogmaxPtK0 - xlogminPtK0) / nbinsPtK0; + for (int i = 0; i <= nbinsPtK0; i++) { + Double_t xlogPtK0 = xlogminPtK0 + i * dlogxPtK0; + xbinsPtK0[i] = TMath::Exp(TMath::Log(10) * xlogPtK0); + } + // the other bins + const Int_t nbinsMassK0 = 100; + Double_t* ybinsMassK0 = new Double_t[nbinsMassK0 + 1]; + Double_t yminMassK0 = 0.4; + Double_t ymaxMassK0 = 0.6; + Double_t dyMassK0 = (ymaxMassK0 - yminMassK0) / nbinsMassK0; + for (int i = 0; i <= nbinsMassK0; i++) { + ybinsMassK0[i] = yminMassK0 + i * dyMassK0; + } + const Int_t nbinsMultK0pp = mNBinsTPCOccpp; + Double_t* zbinsMultK0pp = new Double_t[nbinsMultK0pp + 1]; + Double_t zminMultK0pp = mMinTPCOccpp; + Double_t zmaxMultK0pp = mMaxTPCOccpp; + Double_t dzMultK0pp = (zmaxMultK0pp - zminMultK0pp) / nbinsMultK0pp; + for (int i = 0; i <= nbinsMultK0pp; i++) { + zbinsMultK0pp[i] = zminMultK0pp + i * dzMultK0pp; + } + + const Int_t nbinsMultK0PbPb = mNBinsTPCOccPbPb; + Double_t* zbinsMultK0PbPb = new Double_t[nbinsMultK0PbPb + 1]; + Double_t zminMultK0PbPb = mMinTPCOccPbPb; + Double_t zmaxMultK0PbPb = mMaxTPCOccPbPb; + Double_t dzMultK0PbPb = (zmaxMultK0PbPb - zminMultK0PbPb) / nbinsMultK0PbPb; + for (int i = 0; i <= nbinsMultK0PbPb; i++) { + zbinsMultK0PbPb[i] = zminMultK0PbPb + i * dzMultK0PbPb; + } + + if (mDoK0QC) { + // V0s + mK0MassVsPtVsOccpp = new TH3F("mK0MassVsPtVsOccpp", "K0 invariant mass vs Pt vs TPC occupancy; Pt [GeV/c]; K0s mass [GeV/c^2]; TPC occ.", nbinsPtK0, xbinsPtK0, nbinsMassK0, ybinsMassK0, nbinsMultK0pp, zbinsMultK0pp); + + mK0MassVsPtVsOccPbPb = new TH3F("mK0MassVsPtVsOccPbPb", "K0 invariant mass vs Pt vs TPC occupancy; Pt [GeV/c]; K0s mass [GeV/c^2]; TPC occ", nbinsPtK0, xbinsPtK0, nbinsMassK0, ybinsMassK0, nbinsMultK0PbPb, zbinsMultK0PbPb); + } + + LOG(info) << "Printing configuration cuts"; + printParams(); + + delete[] xbinsPt; + delete[] xbinsPtK0; + delete[] ybinsMassK0; + delete[] zbinsMultK0pp; + delete[] zbinsMultK0PbPb; + + return true; +} + +//__________________________________________________________ + +void MatchITSTPCQC::initDataRequest() +{ + + // initialize data request, if it was not already done + + mSrc &= mAllowedSources; + + if (!mSrc[GID::Source::ITSTPC] || !mSrc[GID::Source::TPC] || !mSrc[GID::Source::ITS]) { + LOG(fatal) << "We cannot do ITSTPC QC, some sources are missing, check sources in " << mSrc; + } + + mDataRequest = std::make_shared(); + mDataRequest->requestTracks(mSrc, mUseMC); + if (mDoK0QC) { + mDataRequest->requestPrimaryVertices(mUseMC); + mDataRequest->requestSecondaryVertices(mUseMC); + mDataRequest->requestTPCOccMap(); + } +} + +//__________________________________________________________ + +void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) +{ + // Getting the B field + mBz = o2::base::Propagator::Instance()->getNominalBz(); + + // Getting the SVertexer config params + if (mTimestamp == -1 && mDoK0QC) { + // we have not yet initialized the SVertexer params; let's do it + ctx.inputs().get("SVParam"); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + + mTimestamp = ctx.services().get().creation; + auto grplhcif = o2::base::GRPGeomHelper::instance().getGRPLHCIF(); + if (grplhcif->getBeamZ(0) != 1 || grplhcif->getBeamZ(1) != 1) { + LOG(info) << "We are in Heavy Ion: Z for beam 0 = " << grplhcif->getBeamZ(0) << " ; Z for beam 1 = " << grplhcif->getBeamZ(1); + mIsHI = true; + } else { + LOG(info) << "We are not in Heavy Ion: Z for beam 0 = " << grplhcif->getBeamZ(0) << " ; Z for beam 1 = " << grplhcif->getBeamZ(1); + } + } + + static int evCount = 0; + mRecoCont.collectData(ctx, *mDataRequest); + mTPCTracks = mRecoCont.getTPCTracks(); + mITSTracks = mRecoCont.getITSTracks(); + mITSTPCTracks = mRecoCont.getTPCITSTracks(); + + LOG(info) << "****** Number of found ITSTPC tracks = " << mITSTPCTracks.size(); + LOG(info) << "****** Number of found TPC tracks = " << mTPCTracks.size(); + LOG(info) << "****** Number of found ITS tracks = " << mITSTracks.size(); + + // cache selection for TPC and ITS tracks + std::vector isTPCTrackSelectedEntry(mTPCTracks.size(), false); + std::vector isITSTrackSelectedEntry(mITSTracks.size(), false); + TrackCuts cuts; + // ITS track + cuts.setMinPtITSCut(mPtITSCut); + cuts.setEtaITSCut(mEtaITSCut); + cuts.setMinNClustersITS(mMinNClustersITS); + cuts.setMaxChi2PerClusterITS(mMaxChi2PerClusterITS); + for (auto it = mRequiredITSHits.begin(); it != mRequiredITSHits.end(); it++) { + cuts.setRequireHitsInITSLayers((*it).first, (*it).second); + } + // TPC track + cuts.setMinPtTPCCut(mPtTPCCut); + cuts.setEtaTPCCut(mEtaTPCCut); + cuts.setMinNTPCClustersCut(mNTPCClustersCut); + cuts.setMaxDCATPCCut(mDCATPCCut); + cuts.setMaxDCATPCCutY(mDCATPCCutY); + // ITS-TPC track kinematics + cuts.setMinPtCut(mPtCut); + cuts.setMaxPtCut(mPtMaxCut); + cuts.setEtaCut(-mEtaCut, mEtaCut); + + for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { + auto const& trkTpc = mTPCTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::TPC); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isTPCTrackSelectedEntry[itrk] = true; + } + } + + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trkIts = mITSTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::ITS); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isITSTrackSelectedEntry[itrk] = true; + } + } + + // numerator + eta, chi2... + if (mUseMC) { + for (int i = 0; i < matchType::SIZE; ++i) { + mMapLabels[i].clear(); + } + for (int itrk = 0; itrk < static_cast(mITSTPCTracks.size()); ++itrk) { + auto const& trk = mITSTPCTracks[itrk]; + auto idxTrkTpc = trk.getRefTPC().getIndex(); + if (trk.getRefITS().getSource() != GID::ITS) { + continue; + } + if (isTPCTrackSelectedEntry[idxTrkTpc]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) == mMapLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); + } else { + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::TPC].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + auto idxTrkIts = trk.getRefITS().getIndex(); + if (isITSTrackSelectedEntry[idxTrkIts]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) == mMapLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); + } else { + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::ITS].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::ITS].at(lbl).mIdx = itrk; + } + } + } + } + LOG(debug) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); + // now we use only the tracks in the map to fill the histograms (--> tracks have passed the + // track selection and there are no duplicated tracks wrt the same MC label) + for (int i = 0; i < matchType::SIZE; ++i) { + for (auto const& el : mMapLabels[i]) { + auto const& trk = mITSTPCTracks[el.second.mIdx]; + o2::track::TrackParCov trkDen; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkDen = mTPCTracks[trk.getRefTPC()]; + } else { + trkDen = mITSTracks[trk.getRefITS()]; + if (std::abs(trkDen.getEta()) > mEtaITSCut) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + } + } + if (isEtaITSOk) { + mPtNum[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > mEtaNo0Cut) { + mPtNum_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiNum[i]->Fill(trkDen.getPhi()); + mPhiVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + mEtaPhiPtNum[i]->Fill(trkDen.getEta(), trkDen.getPhi(), trkDen.getPt()); + // we fill also the denominator + mPtDen[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiDen[i]->Fill(trkDen.getPhi()); + mPhiVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + mEtaPhiPtDen[i]->Fill(trkDen.getEta(), trkDen.getPhi(), trkDen.getPt()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + // we fill also the denominator + mPtDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + } + } + mEtaNum[i]->Fill(trkDen.getEta()); + mEtaVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getEta()); + // we fill also the denominator + mEtaDen[i]->Fill(trkDen.getEta()); + mEtaVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getEta()); + if (i == matchType::TPC) { + auto tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + mClsVsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (tpcTrk.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtNum->Fill(tpcTrk.getPt(), dca[0]); + mDCArVsPtDen->Fill(tpcTrk.getPt(), dca[0]); + } + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + mClsVsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + } + if (el.second.mIsPhysicalPrimary) { + if (isEtaITSOk) { + mPtPhysPrimNum[i]->Fill(trkDen.getPt()); + mPhiPhysPrimNum[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtPhysPrimDen[i]->Fill(trkDen.getPt()); + mPhiPhysPrimDen[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaPhysPrimNum[i]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaPhysPrimDen[i]->Fill(trkDen.getEta()); + } + ++mNITSTPCSelectedTracks[i]; + } + } + } + int iITSTPC = 0; + for (auto const& trk : mITSTPCTracks) { + if (trk.getRefTPC().getIndex() >= mTPCTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for TPC track associated to matched track: idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; + } + std::array title{"TPC", "ITS"}; + for (int i = 0; i < matchType::SIZE; ++i) { + o2::track::TrackParCov trkRef; + unsigned int idxTrkRef{0}; + bool fillHisto = false; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkRef = mTPCTracks[trk.getRefTPC()]; + idxTrkRef = trk.getRefTPC().getIndex(); + if (isTPCTrackSelectedEntry[idxTrkRef]) { + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } + } else { + idxTrkRef = trk.getRefITS().getIndex(); + if (trk.getRefITS().getSource() == GID::ITSAB) { + // do not use afterburner tracks + LOG(debug) << "Track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " is from afterburner"; + continue; + } + if (idxTrkRef >= mITSTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for ITS track associated to matched track (NOT from AB): idx = " << trk.getRefITS().getIndex() << ", size of container = " << mITSTracks.size() << " in TF " << evCount; + } + trkRef = mITSTracks[trk.getRefITS()]; + LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); + if (isITSTrackSelectedEntry[idxTrkRef]) { + LOG(debug) << "Track was selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we keep it in the numerator, pt = " << trkRef.getPt(); + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } else { + LOG(debug) << "Track was not selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + if (std::abs(trkRef.getEta()) > mEtaITSCut) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + LOG(debug) << "Track (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " will be discarded when filling pt of phi related histograms, since eta = " << trkRef.getEta() << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + } + if (fillHisto) { + if (!mUseMC) { + LOG(debug) << "Filling num (" << title[i] << ") with track with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " with pt = " << trkRef.getPt(); + if (isEtaITSOk) { + mPtNum[i]->Fill(trkRef.getPt()); + if (std::abs(trkRef.getEta()) > mEtaNo0Cut) { + mPtNum_noEta0[i]->Fill(trkRef.getPt()); + } + mPhiNum[i]->Fill(trkRef.getPhi()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPt()); + mPhiNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPhi()); + } + mPhiVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getPhi()); + m1OverPtNum[i]->Fill(trkRef.getSign() * trkRef.getPtInv()); + mEtaPhiPtNum[i]->Fill(trkRef.getEta(), trkRef.getPhi(), trkRef.getPt()); + } + mEtaNum[i]->Fill(trkRef.getEta()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getEta()); + } + mEtaVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getEta()); + if (i == matchType::TPC) { + const auto& tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + } + if (i == matchType::TPC) { + mResidualPt->Fill(trk.getPt(), trk.getPt() - trkRef.getPt()); + mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkRef.getPhi()); + mResidualEta->Fill(trk.getEta(), trk.getEta() - trkRef.getEta()); + mChi2Matching->Fill(trk.getChi2Match()); + mChi2Refit->Fill(trk.getChi2Refit()); + mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); + math_utils::Point3D v{}; + std::array dca{-999, -999}; + if (trkRef.propagateParamToDCA(v, mBz, &dca)) { + mDCAr->Fill(dca[0]); + if (!mUseMC) { + mDCArVsPtNum->Fill(trkRef.getPt(), dca[0]); + } + } + LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); + } + } else { + LOG(debug) << "Not filling num (" << title[i] << ") for ITSTPC track " << iITSTPC << " for track with pt " << trkRef.getPt(); + } + } + ++iITSTPC; + } + + // now filling the denominator for the efficiency calculation + if (mUseMC) { + for (int i = 0; i < matchType::SIZE; ++i) { + mMapRefLabels[i].clear(); + } + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of TPC clusters + for (int itrk = 0; itrk < static_cast(mTPCTracks.size()); ++itrk) { + auto const& trk = mTPCTracks[itrk]; + if (isTPCTrackSelectedEntry[itrk]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) != mMapLabels[matchType::TPC].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::TPC].find(lbl) == mMapRefLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of TPC clusters + if (mTPCTracks[mMapRefLabels[matchType::TPC].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + } + // same for ITS + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of ITS clusters + for (int itrk = 0; itrk < static_cast(mITSTracks.size()); ++itrk) { + auto const& trk = mITSTracks[itrk]; + if (isITSTrackSelectedEntry[itrk]) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) != mMapLabels[matchType::ITS].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::ITS].find(lbl) == mMapRefLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of ITS clusters + if (mITSTracks[mMapRefLabels[matchType::ITS].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::ITS].at(lbl).mIdx = itrk; + } + } + } + } + LOG(debug) << "number of entries in map for denominator of TPC tracks (without duplicates) = " << mMapRefLabels[matchType::TPC].size() + mMapLabels[matchType::TPC].size(); + LOG(debug) << "number of entries in map for denominator of ITS tracks (without duplicates) = " << mMapRefLabels[matchType::ITS].size() + mMapLabels[matchType::ITS].size(); + // now we use only the tracks in the map to fill the histograms (--> tracks have passed the + // track selection and there are no duplicated tracks wrt the same MC label) + for (auto const& el : mMapRefLabels[matchType::TPC]) { + auto const& trk = mTPCTracks[el.second.mIdx]; + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::TPC]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } + if (el.second.mIsPhysicalPrimary) { + mPtPhysPrimDen[matchType::TPC]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::TPC]->Fill(trk.getPhi()); + mEtaPhysPrimDen[matchType::TPC]->Fill(trk.getEta()); + m1OverPtPhysPrimDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + } + ++mNTPCSelectedTracks; + } + for (auto const& el : mMapRefLabels[matchType::ITS]) { + auto const& trk = mITSTracks[el.second.mIdx]; + if (std::abs(trk.getEta()) < mEtaITSCut) { + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::ITS]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + if (el.second.mIsPhysicalPrimary) { + if (std::abs(trk.getEta()) < mEtaITSCut) { + mPtPhysPrimDen[matchType::ITS]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::ITS]->Fill(trk.getPhi()); + m1OverPtPhysPrimDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaPhysPrimDen[matchType::ITS]->Fill(trk.getEta()); + } + ++mNITSSelectedTracks; + } + } else { + // if we are in data, we loop over all tracks (no check on the label) + for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { + auto const& trk = mTPCTracks[itrk]; + if (isTPCTrackSelectedEntry[itrk]) { + LOG(debug) << "Filling den (TPC) with track with pt = " << trk.getPt(); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for den pt, phi, phi vs pt, 1.pt histos"; + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::TPC]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } + ++mNTPCSelectedTracks; + } + } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trk = mITSTracks[itrk]; + LOG(debug) << "Checking den for track (ITS) " << itrk << " with pt " << trk.getPt() << " and eta = " << trk.getEta(); + if (isITSTrackSelectedEntry[itrk]) { + if (std::abs(trk.getEta()) < mEtaITSCut) { + LOG(debug) << "Filling den for track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta(); + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > mEtaNo0Cut) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + mEtaPhiPtDen[matchType::ITS]->Fill(trk.getEta(), trk.getPhi(), trk.getPt()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for num pt, phi, phi vs pt, 1.pt histos"; + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + ++mNITSSelectedTracks; + } else { + LOG(debug) << "Not filling for this track (ITS) " << itrk << " with pt = " << trk.getPt(); + } + } + } + + if (mDoK0QC && mRecoCont.getPrimaryVertices().size() > 0) { + // now doing K0S + mFitterV0.setBz(mBz); + const auto pvertices = mRecoCont.getPrimaryVertices(); + LOG(info) << "****** Number of PVs = " << pvertices.size(); + + // getting occupancy estimator + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + if (!mParam) { + // for occupancy estimator + mParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); + } + size_t occupancyMapSizeBytes = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mParam.get()); + LOG(debug) << "occupancyMapSizeBytes = " << occupancyMapSizeBytes; + mTPCRefitterOccMap = mRecoCont.occupancyMapTPC; + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mParam.get(), mNHBPerTF, mTPCRefitterOccMap.data(), occupancyMapSizeBytes); + + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i * mNTPCOccBinLength + mTBinClOcc.clear(); + int mNTPCOccBinLength = mParam->rec.tpc.occupancyMapTimeBins; + LOG(debug) << "mNTPCOccBinLength = " << mNTPCOccBinLength; + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { + int nTPCBinsInTF = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8; // number of TPC time bins in 1 TF, considering that 1 TPC time bin is 8 bunches + int ninteg = 0; + int nTPCOccBinsInTF = nTPCBinsInTF * mNTPCOccBinLengthInv; // how many occupancy bins in 1 TF; mNTPCOccBinLengthInv is the inverse of the length of an occupancy bin + int sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); // we will integrate occupancy at max for this number of bins: the max between 1 and the number of occupancy bins in 1 orbit + LOG(debug) << "number of TPC TB in 1 TF = nTPCBinsInTF = " << nTPCBinsInTF << " ; number of occupancy bins in 1 TF = nTPCOccBinsInTF = " << nTPCOccBinsInTF; + LOG(debug) << "bins to integrate = sumBins = " << sumBins; + mTBinClOcc.resize(nTPCOccBinsInTF); + std::vector mltHistTB(nTPCOccBinsInTF); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + bool foundNotZero = false; + for (int i = 0; i < nTPCOccBinsInTF; i++) { // for every occupancy bin in the TF + mltHistTB[i] = mParam->GetUnscaledMult(tb); + if (mParam->GetUnscaledMult(tb) != 0) { + LOG(debug) << "i = " << i << " tb = " << tb << " mltHistTB[" << i << "] = " << mltHistTB[i]; + foundNotZero = true; + } + tb += mNTPCOccBinLength; + } + if (!foundNotZero) { + LOG(debug) << "No mult bin was found different from 0!"; + } + foundNotZero = false; + // now we fill the occupancy map; we integrate the sumBins after the current one, but when we are at the last 27 bins of the TF, where we integrate what we have left till the end of the TF; for practical reasons, we start from the end, adding all the time, and then also removing the last bin, when we have enough, so that we always add together sumBins bins (except, as said, for the last part of the TF) + for (int i = nTPCOccBinsInTF; i--;) { + if (mltHistTB[i] != 0) { + foundNotZero = true; + } + LOG(debug) << "i = " << i << " sm before = " << sm; + sm += mltHistTB[i]; + LOG(debug) << "i = " << i << " sm after = " << sm; + if (i + sumBins < nTPCOccBinsInTF) { + LOG(debug) << "i = " << i << " sumBins = " << sumBins << " nTPCOccBinsInTF = " << nTPCOccBinsInTF << " we have to decrease sm by = " << mltHistTB[i + sumBins]; + sm -= mltHistTB[i + sumBins]; + LOG(debug) << "i = " << i << " sm after 2 = " << sm; + } + mTBinClOcc[i] = sm; + LOG(debug) << "i = " << i << " mTBinClOcc[" << i << "] = " << mTBinClOcc[i]; + } + if (!foundNotZero) { + LOG(debug) << "No mult bin was found different from 0! sm = " << sm; + } + } else { + mTBinClOcc.resize(1); + } + auto v0IDs = mRecoCont.getV0sIdx(); + auto nv0 = v0IDs.size(); + if (nv0 > mRecoCont.getV0s().size()) { + mRefit = true; + } + LOG(debug) << "Found " << mRecoCont.getV0s().size() << " V0s in reco container"; + LOG(debug) << "Found " << nv0 << " V0s ids"; + // associating sec vtxs to prim vtx + std::map> pv2sv; + static int tfID = 0; + for (int iv = 0; iv < nv0; iv++) { + const auto v0id = v0IDs[iv]; + o2::dataformats::GlobalTrackID id0 = v0id.getProngID(0), id1 = v0id.getProngID(1); + if (id0.getSourceDetectorsMask()[DetID::ITS] && id1.getSourceDetectorsMask()[DetID::ITS] && + mRecoCont.isTrackSourceLoaded(id0.getSource()) && mRecoCont.isTrackSourceLoaded(id1.getSource())) { // taking only tracks for which there is the ITS in the track sources + pv2sv[v0id.getVertexID()].push_back(iv); + } else { + LOG(debug) << "Skipping this V0: the track sources do not contain ITS"; + } + } + int nV0sOk = 0; + // processing every sec vtx for each prim vtx + int myCount = 0; + for (auto it : pv2sv) { + int pvID = it.first; + auto& vv = it.second; + if (pvID < 0 || vv.size() == 0) { + continue; + } + const auto& pv = mRecoCont.getPrimaryVertex(pvID); + float pvTime = pv.getTimeStamp().getTimeStamp(); // in \mus + for (int iv0 : vv) { + nV0sOk += processV0(iv0, mRecoCont, mTBinClOcc, pvTime) ? 1 : 0; + } + ++myCount; + } + + LOG(debug) << "Processed " << nV0sOk << " V0s"; + } + evCount++; +} + +//__________________________________________________________ +bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoData, std::vector& mTBinClOcc, float pvTime) +{ + o2::dataformats::V0 v0; + auto v0s = recoData.getV0s(); + auto v0IDs = recoData.getV0sIdx(); + static int tfID = 0; + + const auto& v0id = v0IDs[iv]; + ++mNK0; + if (mNK0 % int(1 / mK0Scaling) == 0) { + LOG(debug) << "Checking " << mNK0 << "th V0: refitting it, since we keep " << mK0Scaling * 100 << "% of all V0s"; + } else { + LOG(debug) << "Checking " << mNK0 << "th K0: NOT refitting it, but skipping it, since we keep " << mK0Scaling * 100 << "% of all V0s"; + return false; + } + if (mRefit && !refitV0(v0id, v0, recoData)) { + return false; + } + const auto& v0sel = mRefit ? v0 : v0s[iv]; + if (mMaxEtaK0 < std::abs(v0sel.getEta())) { + return false; + } + + if (mCutK0Mass > 0 && std::abs(std::sqrt(v0sel.calcMass2AsK0()) - 0.497) > mCutK0Mass || v0sel.getDCA() > mK0MaxDCA || v0sel.getCosPA() < mK0MinCosPA) { + if (v0sel.getDCA() > mK0MaxDCA && v0sel.getCosPA() < mK0MinCosPA) { + LOG(debug) << "v0sel.getDCA() = " << v0sel.getDCA() << " max is " << mK0MaxDCA << " returning ... "; + } + if (v0sel.getCosPA() > mK0MinCosPA) { + LOG(debug) << "v0sel.getCosPA() = " << v0sel.getCosPA() << " min is " << mK0MinCosPA << " returning ... "; + } + return false; + } + // get the corresponding PV + int tb = pvTime / (8 * o2::constants::lhc::LHCBunchSpacingMUS) * mNTPCOccBinLengthInv; // V0 time in TPC time bins + LOG(debug) << "pvTime = " << pvTime << " tb = " << tb; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + ++mNK0; + LOG(debug) << "Filling K0 histogram with pt = " << v0sel.getPt() << " mass = " << std::sqrt(v0sel.calcMass2AsK0()) << " mult TPC = " << mltTPC; + if (!mIsHI) { + mK0MassVsPtVsOccpp->Fill(v0sel.getPt(), std::sqrt(v0sel.calcMass2AsK0()), mltTPC); + } else { + mK0MassVsPtVsOccPbPb->Fill(v0sel.getPt(), std::sqrt(v0sel.calcMass2AsK0()), mltTPC); + } + return true; +} + +//__________________________________________________________ +bool MatchITSTPCQC::refitV0(const o2::dataformats::V0Index& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData) +{ + LOG(debug) << "Refitting V0"; + if (!recoData.isTrackSourceLoaded(id.getProngID(0).getSource()) || !recoData.isTrackSourceLoaded(id.getProngID(1).getSource())) { + return false; + } + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == o2::dataformats::GlobalTrackID::TPC) || (id.getProngID(1).getSource() == o2::dataformats::GlobalTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +//__________________________________________________________ +void MatchITSTPCQC::finalize() +{ + + std::array title{"TPC", "ITS"}; + + // first we use denominators and nominators to set the TEfficiency; later they are scaled + + // filling the efficiency + for (int ti = 0; ti < matchType::SIZE; ++ti) { + setEfficiency(mFractionITSTPCmatch[ti], mPtNum[ti], mPtDen[ti]); + setEfficiency(mFractionITSTPCmatch_noEta0[ti], mPtNum_noEta0[ti], mPtDen_noEta0[ti]); + setEfficiency(mFractionITSTPCmatchPhi[ti], mPhiNum[ti], mPhiDen[ti]); + setEfficiency(mFractionITSTPCmatchEta[ti], mEtaNum[ti], mEtaDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchPhiVsPt[ti], mPhiVsPtNum[ti], mPhiVsPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchEtaVsPt[ti], mEtaVsPtNum[ti], mEtaVsPtDen[ti]); + setEfficiency(mFractionITSTPCmatch1OverPt[ti], m1OverPtNum[ti], m1OverPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchClsVsPt[ti], mClsVsPtNum[ti], mClsVsPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchChi2VsPt[ti], mChi2VsPtNum[ti], mChi2VsPtDen[ti]); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + setEfficiency(mFractionITSTPCmatchPtVsTrkPID[ti][j], mPtNumVsTrkPID[ti][j], mPtDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchPhiVsTrkPID[ti][j], mPhiNumVsTrkPID[ti][j], mPhiDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchEtaVsTrkPID[ti][j], mEtaNumVsTrkPID[ti][j], mEtaDenVsTrkPID[ti][j]); + } + } + if (mUseMC) { + setEfficiency(mFractionITSTPCmatchPhysPrim[ti], mPtPhysPrimNum[ti], mPtPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiPhysPrim[ti], mPhiPhysPrimNum[ti], mPhiPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchEtaPhysPrim[ti], mEtaPhysPrimNum[ti], mEtaPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[ti], m1OverPtPhysPrimNum[ti], m1OverPtPhysPrimDen[ti]); + } + } + setEfficiency<2>(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen); + /* + mPtTPC->Scale(scaleFactTPC); + mPt->Scale(scaleFactITSTPC); + mPhiTPC->Scale(scaleFactTPC); + mPhi->Scale(scaleFactITSTPC); + if (mUseMC) { + mPtTPCPhysPrim->Scale(scaleFactTPC); + mPtPhysPrim->Scale(scaleFactITSTPC); + mPhiTPCPhysPrim->Scale(scaleFactTPC); + mPhiPhysPrim->Scale(scaleFactITSTPC); + } + mEta->Scale(scaleFactITSTPC); + mChi2Matching->Scale(scaleFactITSTPC); + mChi2Refit->Scale(scaleFactITSTPC); + //mTimeResVsPt->Scale(scaleFactITSTPC); // if to few entries, one sees nothing after normalization --> let's not normalize + */ +} + +//__________________________________________________________ +template +void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden) +{ + // Trivial check if we initalized + if (eff == nullptr) { + LOG(fatal) << "Cannot get TEfficiency object "; + } + if (hnum == nullptr) { + LOG(fatal) << "Cannot get numerator histogram for TEfficiency object " << eff->GetName(); + } + if (hden == nullptr) { + LOG(fatal) << "Cannot get denominator histogram for TEfficiency object " << eff->GetName(); + } + + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if constexpr (DEBUG) { // checking + bool bad{false}; + LOG(info) << "Setting efficiency " << eff->GetName() << " from " << hnum->GetName() << " and " << hden->GetName(); + LOG(info) << "Num " << hnum->GetName() << " " << hnum->GetNbinsX() << " " << hnum->GetNbinsY() << " with " << hnum->GetEntries() << " entries"; + LOG(info) << "Den " << hden->GetName() << " " << hden->GetNbinsX() << " " << hden->GetNbinsY() << " with " << hden->GetEntries() << " entries"; + if (hnum->GetDimension() != hden->GetDimension()) { + LOGP(warning, "Histograms have different dimensions (num={} to den={})", hnum->GetDimension(), hden->GetDimension()); + bad = true; + } + if (!TEfficiency::CheckBinning(*hnum, *hden)) { + LOGP(warning, "Histograms do not have a compatible binning"); + bad = true; + } + if constexpr (DIM == 3) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + for (int k = 1; k <= hden->GetNbinsZ(); k++) { + if (hden->GetBinContent(i, j, k) < hnum->GetBinContent(i, j, k)) { + LOGP(warning, "bin {}/{}/{} -> den: {} < num: {}", i, j, k, hden->GetBinContent(i, j, k), hnum->GetBinContent(i, j, k)); + bad = true; + } + } + } + } + } else if constexpr (DIM == 2) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + if (hden->GetBinContent(i, j) < hnum->GetBinContent(i, j)) { + LOGP(warning, "bin {}/{} -> den: {} < num: {}", i, j, hden->GetBinContent(i, j), hnum->GetBinContent(i, j)); + bad = true; + } + } + } + } else { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + if (hden->GetBinContent(i) < hnum->GetBinContent(i)) { + LOG(warning) << "bin " << i << " den: " << hden->GetBinContent(i) << " < num: " << hnum->GetBinContent(i) << " should be the opposite"; + bad = true; + } + } + } + if (bad) { + LOG(info) << " `--> Histogram is bad!"; + return; + } else { + LOG(info) << " `--> Histogram is good!"; + } + } + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents + if (!eff->SetTotalHistogram(*hden, "f")) { + LOG(fatal) << "Something went wrong when defining the efficiency denominator " << eff->GetName() << " from " << hnum->GetName(); + } + if (!eff->SetPassedHistogram(*hnum, "")) { + LOG(fatal) << "Something went wrong when defining the efficiency numerator " << eff->GetName() << " from " << hnum->GetName(); + } + if constexpr (DIM == 3) { + eff->SetTitle(Form("%s;%s;%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), hnum->GetYaxis()->GetTitle(), hnum->GetZaxis()->GetTitle(), "Efficiency")); + } else if constexpr (DIM == 2) { + eff->SetTitle(Form("%s;%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), hnum->GetYaxis()->GetTitle(), "Efficiency")); + } else { + eff->SetTitle(Form("%s;%s;%s", eff->GetTitle(), hnum->GetXaxis()->GetTitle(), "Efficiency")); + } +} + +//__________________________________________________________ + +void MatchITSTPCQC::getHistos(TObjArray& objar) +{ + + for (int i = 0; i < matchType::SIZE; ++i) { + objar.Add(mPtNum[i]); + objar.Add(mPtDen[i]); + objar.Add(mFractionITSTPCmatch[i]); + + objar.Add(mPtNum_noEta0[i]); + objar.Add(mPtDen_noEta0[i]); + objar.Add(mFractionITSTPCmatch_noEta0[i]); + + objar.Add(mPtPhysPrimNum[i]); + objar.Add(mPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim[i]); + + objar.Add(mPhiNum[i]); + objar.Add(mPhiDen[i]); + objar.Add(mFractionITSTPCmatchPhi[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + objar.Add(mPtNumVsTrkPID[i][j]); + objar.Add(mPtDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPtVsTrkPID[i][j]); + // Phi + objar.Add(mPhiNumVsTrkPID[i][j]); + objar.Add(mPhiDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + // Eta + objar.Add(mEtaNumVsTrkPID[i][j]); + objar.Add(mEtaDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + objar.Add(mPhiPhysPrimNum[i]); + objar.Add(mPhiPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhiPhysPrim[i]); + + objar.Add(mPhiVsPtNum[i]); + objar.Add(mPhiVsPtDen[i]); + objar.Add(mFractionITSTPCmatchPhiVsPt[i]); + + objar.Add(mEtaNum[i]); + objar.Add(mEtaDen[i]); + objar.Add(mFractionITSTPCmatchEta[i]); + + objar.Add(mEtaPhysPrimNum[i]); + objar.Add(mEtaPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchEtaPhysPrim[i]); + + objar.Add(mEtaVsPtNum[i]); + objar.Add(mEtaVsPtDen[i]); + objar.Add(mFractionITSTPCmatchEtaVsPt[i]); + + objar.Add(mClsVsPtNum[i]); + objar.Add(mClsVsPtDen[i]); + objar.Add(mFractionITSTPCmatchClsVsPt[i]); + + objar.Add(mChi2VsPtNum[i]); + objar.Add(mChi2VsPtDen[i]); + objar.Add(mFractionITSTPCmatchChi2VsPt[i]); + + objar.Add(m1OverPtNum[i]); + objar.Add(m1OverPtDen[i]); + objar.Add(mFractionITSTPCmatch1OverPt[i]); + + objar.Add(m1OverPtPhysPrimNum[i]); + objar.Add(m1OverPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim1OverPt[i]); + + objar.Add(mEtaPhiPtNum[i]); + objar.Add(mEtaPhiPtDen[i]); + } + objar.Add(mChi2Matching); + objar.Add(mChi2Refit); + objar.Add(mTimeResVsPt); + objar.Add(mResidualPt); + objar.Add(mResidualPhi); + objar.Add(mResidualEta); + objar.Add(mDCAr); + objar.Add(mDCArVsPtNum); + objar.Add(mDCArVsPtDen); + objar.Add(mFractionITSTPCmatchDCArVsPt); + + // V0 + objar.Add(mK0MassVsPtVsOccpp); + objar.Add(mK0MassVsPtVsOccPbPb); +} + +void MatchITSTPCQC::printParams() const +{ + LOG(info) << "MatchITSTPCQC parameters:"; + LOG(info) << " - minPtBins = " << mPtBins; + LOG(info) << " - minPtITSCut = " << mPtITSCut; + LOG(info) << " - etaITSCut = " << mEtaITSCut; + LOG(info) << " - minNITSClustersCut = " << mMinNClustersITS; + LOG(info) << " - maxChi2PerClusterITS = " << mMaxChi2PerClusterITS; + LOG(info) << " - minPtTPCCut = " << mPtTPCCut; + LOG(info) << " - etaTPCCut = " << mEtaTPCCut; + LOG(info) << " - minNTPCClustersCut = " << mNTPCClustersCut; + LOG(info) << " - mEtaNo0Cut = " << mEtaNo0Cut; + LOG(info) << " - minDCACut = " << mDCATPCCut; + LOG(info) << " - minDCACutY = " << mDCATPCCutY; + LOG(info) << " - minPtCut = " << mPtCut; + LOG(info) << " - maxPtCut = " << mPtMaxCut; + LOG(info) << " - etaCut = " << mEtaCut; + LOG(info) << " - cutK0Mass = " << mCutK0Mass; + LOG(info) << " - maxEtaK0 = " << mMaxEtaK0; + LOG(info) << " - minTPCOccpp = " << mMinTPCOccpp; + LOG(info) << " - maxTPCOccpp = " << mMaxTPCOccpp; + LOG(info) << " - nBinsTPCOccpp = " << mNBinsTPCOccpp; + LOG(info) << " - minTPCOccPbPb = " << mMinTPCOccPbPb; + LOG(info) << " - maxTPCOccPbPb = " << mMaxTPCOccPbPb; + LOG(info) << " - nBinsTPCOccPbPb = " << mNBinsTPCOccPbPb; +} diff --git a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h index 0270136abbf77..5043f36ef1433 100644 --- a/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h +++ b/Detectors/GRP/calibration/include/GRPCalibration/GRPDCSDPsProcessor.h @@ -16,6 +16,7 @@ #include #include #include "Framework/Logger.h" +#include "Framework/O2LongInt.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" @@ -37,12 +38,19 @@ using DPID = o2::dcs::DataPointIdentifier; using DPVAL = o2::dcs::DataPointValue; using DPCOM = o2::dcs::DataPointCompositeObject; -inline unsigned long llu2lu(std::uint64_t v) { return (unsigned long)v; } +inline O2LongUInt llu2lu(std::uint64_t v) { return (O2LongUInt)v; } struct GRPEnvVariables { - std::unordered_map>> mEnvVars; - + std::unordered_map>> mEnvVars; + size_t totalEntries() const + { + size_t s = 0; + for (const auto& el : mEnvVars) { + s += el.second.size(); + } + return s; + } void print() { for (const auto& el : mEnvVars) { @@ -53,7 +61,7 @@ struct GRPEnvVariables { } } - ClassDefNV(GRPEnvVariables, 1); + ClassDefNV(GRPEnvVariables, 2); }; struct MagFieldHelper { @@ -64,10 +72,12 @@ struct MagFieldHelper { bool negDip = 0; bool updated = false; bool verbose = false; + static constexpr float ZeroCurrent = 70.f; // current below this threshold considered to be produce 0 field + static constexpr float DeltaThreshold = 0.2f; // update field on delta's exceeding this value void updateCurL3(float v) { - if (!(isSet & 0x1) || std::abs(v - curL3) > 0.1) { + if (!(isSet & 0x1) || (std::abs(v - curL3) > DeltaThreshold && (std::abs(v) > ZeroCurrent || std::abs(curL3) > ZeroCurrent))) { if (verbose) { LOG(info) << "L3 current will be updated from " << curL3 << " to " << v; } @@ -78,7 +88,7 @@ struct MagFieldHelper { } void updateCurDip(float v) { - if (!(isSet & 0x2) || std::abs(v - curDip) > 0.1) { + if (!(isSet & 0x2) || (std::abs(v - curDip) > DeltaThreshold && (std::abs(v) > ZeroCurrent || std::abs(curDip) > ZeroCurrent))) { if (verbose) { LOG(info) << "Dipole current will be updated from " << curDip << " to " << v; } @@ -113,11 +123,17 @@ struct MagFieldHelper { struct GRPCollimators { - std::unordered_map>> mCollimators; - + std::unordered_map>> mCollimators; + size_t totalEntries() const + { + size_t s = 0; + for (const auto& el : mCollimators) { + s += el.second.size(); + } + return s; + } void print() { - for (const auto& el : mCollimators) { std::printf("%-60s\n", el.first.c_str()); for (const auto& it : el.second) { @@ -126,7 +142,7 @@ struct GRPCollimators { } } - ClassDefNV(GRPCollimators, 1); + ClassDefNV(GRPCollimators, 2); }; struct GRPLHCInfo { @@ -174,21 +190,21 @@ struct GRPLHCInfo { static constexpr std::string_view bptxPhaseShiftAliases[NBPTXPhaseShiftAliases] = {"BPTX_Phase_Shift_B1", "BPTX_Phase_Shift_B2"}; static constexpr std::string_view lumiAliases[NLumiAliases] = {"ALI_Lumi_Total_Inst"}; static constexpr std::string_view lhcStringAliases[NLHCStringAliases] = {"ALI_Lumi_Source_Name", "BEAM_MODE", "MACHINE_MODE"}; - static constexpr int nAliasesLHC = NCollimatorAliases + NBeamAliases + NBkgAliases + NBPTXAliases + NBPTXPhaseAliases + NBPTXPhaseRMSAliases + NBPTXPhaseShiftAliases + NLumiAliases + NLHCStringAliases; - - std::array>, 2> mIntensityBeam; - std::array>, 3> mBackground; - std::vector> mInstLumi; - std::vector> mBPTXdeltaT; - std::vector> mBPTXdeltaTRMS; - std::array>, 2> mBPTXPhase; - std::array>, 2> mBPTXPhaseRMS; - std::array>, 2> mBPTXPhaseShift; - std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored - std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored - std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored - - void resetAndKeepLastVector(std::vector>& vect) + static constexpr int nAliasesLHC = (int)NCollimatorAliases + (int)NBeamAliases + (int)NBkgAliases + (int)NBPTXAliases + (int)NBPTXPhaseAliases + (int)NBPTXPhaseRMSAliases + (int)NBPTXPhaseShiftAliases + (int)NLumiAliases + (int)NLHCStringAliases; + + std::array>, 2> mIntensityBeam; + std::array>, 3> mBackground; + std::vector> mInstLumi; + std::vector> mBPTXdeltaT; + std::vector> mBPTXdeltaTRMS; + std::array>, 2> mBPTXPhase; + std::array>, 2> mBPTXPhaseRMS; + std::array>, 2> mBPTXPhaseShift; + std::pair mLumiSource; // only one value per object: when there is a change, a new object is stored + std::pair mMachineMode; // only one value per object: when there is a change, a new object is stored + std::pair mBeamMode; // only one value per object: when there is a change, a new object is stored + + void resetAndKeepLastVector(std::vector>& vect) { // always check that the size is > 0 (--> begin != end) for all vectors if (vect.begin() != vect.end()) { @@ -276,20 +292,22 @@ class GRPDCSDPsProcessor void init(const std::vector& pids); int process(const gsl::span dps); int processDP(const DPCOM& dpcom); - uint64_t processFlags(uint64_t flag, const char* pid) { return 0; } // for now it is not really implemented + O2LongUInt processFlags(O2LongUInt flag, const char* pid) { return 0; } // for now it is not really implemented bool processCollimators(const DPCOM& dpcom); bool processEnvVar(const DPCOM& dpcom); - bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); - bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); - bool compareToLatest(std::pair& p, double val); + bool processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate); + bool processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag); + bool compareToLatest(std::pair& p, double val); bool processLHCIFDPs(const DPCOM& dpcom); void resetAndKeepLastLHCIFDPs() { mLHCInfo.resetAndKeepLast(); } - void resetAndKeepLast(std::unordered_map>>& mapToReset) + void resetAndKeepLast(std::unordered_map>>& mapToReset) { // keep only the latest measurement for (auto& el : mapToReset) { - el.second.erase(el.second.begin(), el.second.end() - 1); + if (el.second.begin() != el.second.end()) { + el.second.erase(el.second.begin(), el.second.end() - 1); + } } } @@ -349,8 +367,8 @@ class GRPDCSDPsProcessor void useVerboseMode() { mVerbose = true; } void clearVectors() { mClearVectors = true; } - void printVectorInfo(const std::vector>& vect, bool afterUpdate); - void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, uint64_t timestamp, double val); + void printVectorInfo(const std::vector>& vect, bool afterUpdate); + void updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val); private: std::unordered_map mPids; // contains all PIDs for the processor, the bool diff --git a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx index 2700deed92fe8..aec4241f4f8db 100644 --- a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx +++ b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx @@ -175,7 +175,6 @@ bool GRPDCSDPsProcessor::processCollimators(const DPCOM& dpcom) bool GRPDCSDPsProcessor::processEnvVar(const DPCOM& dpcom) { - // function to process Data Points that are related to env variables bool match = processPairD(dpcom, "CavernTemperature", mEnvVars.mEnvVars) || processPairD(dpcom, "CavernAtmosPressure", mEnvVars.mEnvVars) || @@ -186,7 +185,7 @@ bool GRPDCSDPsProcessor::processEnvVar(const DPCOM& dpcom) } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) +bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& alias, std::unordered_map>>& mapToUpdate) { // function to process Data Points that is stored in a pair @@ -208,7 +207,7 @@ bool GRPDCSDPsProcessor::processPairD(const DPCOM& dpcom, const std::string& ali } //______________________________________________________________________ -bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) +bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& alias, std::pair& p, bool& flag) { // function to process string Data Points that is stored in a pair @@ -238,7 +237,7 @@ bool GRPDCSDPsProcessor::processPairS(const DPCOM& dpcom, const std::string& ali //______________________________________________________________________ -bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) +bool GRPDCSDPsProcessor::compareToLatest(std::pair& p, double val) { // check if the content of the pair should be updated @@ -278,13 +277,13 @@ bool GRPDCSDPsProcessor::processLHCIFDPs(const DPCOM& dpcom) } for (int ibeam = 0; ibeam < GRPLHCInfo::BeamAliases::NBeamAliases; ++ibeam) { - if (aliasStr.find(static_cast(GRPLHCInfo::beamAliases[ibeam])) != string::npos) { + if (aliasStr.find(static_cast(GRPLHCInfo::beamAliases[ibeam])) != std::string::npos) { updateVector(dpid, mLHCInfo.mIntensityBeam[ibeam], aliasStr, dpcomdata.get_epoch_time(), val); return true; } } - if (aliasStr.find("BPTX") != string::npos) { + if (aliasStr.find("BPTX") != std::string::npos) { if (aliasStr == static_cast(GRPLHCInfo::bptxAliases[GRPLHCInfo::BPTXAliases::BPTX_deltaT_B1_B2])) { updateVector(dpid, mLHCInfo.mBPTXdeltaT, aliasStr, dpcomdata.get_epoch_time(), val); return true; @@ -319,7 +318,7 @@ bool GRPDCSDPsProcessor::processLHCIFDPs(const DPCOM& dpcom) } for (int ibkg = 0; ibkg < 3; ++ibkg) { - if (aliasStr.find(static_cast(GRPLHCInfo::bkgAliases[ibkg])) != string::npos) { + if (aliasStr.find(static_cast(GRPLHCInfo::bkgAliases[ibkg])) != std::string::npos) { updateVector(dpid, mLHCInfo.mBackground[ibkg], aliasStr, dpcomdata.get_epoch_time(), val); return true; } @@ -396,20 +395,20 @@ void GRPDCSDPsProcessor::updateEnvVarsCCDB() void GRPDCSDPsProcessor::updateCollimatorsCCDB() { - // we need to update a CCDB for the Env Variables DPs --> let's prepare the CCDBInfo + // we need to update a CCDB for the Collimators Variables DPs --> let's prepare the CCDBInfo if (mVerbose) { LOG(info) << "Entry related to Collimators needs to be updated with startTime " << mStartValidityColli; } std::map md; md["responsible"] = "Chiara Zampolli"; - o2::calibration::Utils::prepareCCDBobjectInfo(mEnvVars, mccdbCollimatorsInfo, "GLO/Config/Collimators", md, mStartValidityColli, mStartValidityColli + 3 * o2::ccdb::CcdbObjectInfo::DAY); // valid for 3 days + o2::calibration::Utils::prepareCCDBobjectInfo(mCollimators, mccdbCollimatorsInfo, "GLO/Config/Collimators", md, mStartValidityColli, mStartValidityColli + 3 * o2::ccdb::CcdbObjectInfo::DAY); // valid for 3 days return; } //______________________________________________________________________ -void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) +void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, bool afterUpdate) { std::string stage = afterUpdate ? "after update" : "before update"; @@ -423,7 +422,7 @@ void GRPDCSDPsProcessor::printVectorInfo(const std::vector>& vect, std::string alias, uint64_t timestamp, double val) +void GRPDCSDPsProcessor::updateVector(const DPID& dpid, std::vector>& vect, std::string alias, O2LongUInt timestamp, double val) { printVectorInfo(vect, 0); bool updateFlag = false; diff --git a/Detectors/GRP/workflows/CMakeLists.txt b/Detectors/GRP/workflows/CMakeLists.txt index 87957000ab08b..1097855a5d579 100644 --- a/Detectors/GRP/workflows/CMakeLists.txt +++ b/Detectors/GRP/workflows/CMakeLists.txt @@ -31,11 +31,21 @@ o2_add_executable(grp-lhc-if-file-workflow O2::GRPCalibration O2::DetectorsCalibration) +o2_add_executable(workflow + COMPONENT_NAME rct-updater + SOURCES src/rct-updater-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::CCDB + O2::DetectorsBase + O2::DetectorsCalibration + O2::DataFormatsParameters) + o2_add_executable(grp-create COMPONENT_NAME ecs SOURCES src/create-grp-ecs.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats O2::DataFormatsParameters + O2::DataFormatsCTP O2::CommonUtils O2::CCDB Boost::program_options) @@ -47,6 +57,8 @@ o2_add_executable(workflow O2::CommonUtils O2::CCDB O2::DetectorsRaw + O2::ITSBase + O2::MFTBase Boost::program_options) o2_add_executable(grp-dcs-dps-sim-workflow diff --git a/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h b/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h index 52f6be37a226a..6a9b946251eb1 100644 --- a/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h +++ b/Detectors/GRP/workflows/include/GRPWorkflows/GRPDCSDPsSpec.h @@ -45,6 +45,9 @@ class GRPDCSDPsDataProcessor : public Task int64_t mDPsUpdateInterval; bool mReportTiming = false; bool mLHCIFupdated = false; + size_t mEmptyCyclesEnvVars = 0; + size_t mEmptyCyclesCollimators = 0; + size_t mWarnEmptyCycles = 1; }; } // namespace grp diff --git a/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx b/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx index 24ac29aed05ad..6f214cf7c0b42 100644 --- a/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx +++ b/Detectors/GRP/workflows/src/GRPDCSDPsSpec.cxx @@ -46,6 +46,7 @@ void GRPDCSDPsDataProcessor::init(o2::framework::InitContext& ic) std::vector vect; mDPsUpdateInterval = ic.options().get("DPs-update-interval"); + mWarnEmptyCycles = ic.options().get("warn-empty-cycles"); if (mDPsUpdateInterval == 0) { LOG(error) << "GRP DPs update interval set to zero seconds --> changed to 60"; mDPsUpdateInterval = 60; @@ -248,6 +249,14 @@ void GRPDCSDPsDataProcessor::sendCollimatorsDPsoutput(DataAllocator& output) // filling CCDB with Collimators object const auto& payload = mProcessor->getCollimatorsObj(); + if (payload.totalEntries() == 0) { + if ((mEmptyCyclesCollimators % size_t(mWarnEmptyCycles)) == 0) { + LOGP(alarm, "No Collimator DPs were received after {} {}-s cycles", mEmptyCyclesCollimators, mDPsUpdateInterval); + } + mEmptyCyclesCollimators++; + } else { + mEmptyCyclesCollimators = 0; + } auto& info = mProcessor->getccdbCollimatorsInfo(); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() @@ -264,6 +273,14 @@ void GRPDCSDPsDataProcessor::sendEnvVarsDPsoutput(DataAllocator& output) // filling CCDB with EnvVars object const auto& payload = mProcessor->getEnvVarsObj(); + if (payload.totalEntries() == 0) { + if ((mEmptyCyclesEnvVars % size_t(mWarnEmptyCycles)) == 0) { + LOGP(alarm, "No EnvVar DPs were received after {} {}-s cycles", mEmptyCyclesEnvVars, mDPsUpdateInterval); + } + mEmptyCyclesEnvVars++; + } else { + mEmptyCyclesEnvVars = 0; + } auto& info = mProcessor->getccdbEnvVarsInfo(); auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() @@ -305,6 +322,7 @@ DataProcessorSpec getGRPDCSDPsDataProcessorSpec() {"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}, {"report-timing", VariantType::Bool, false, {"Report timing for every slice"}}, {"DPs-update-interval", VariantType::Int64, 600ll, {"Interval (in s) after which to update the DPs CCDB entry"}}, + {"warn-empty-cycles", VariantType::Int, 1, {"Warn about empty object after this number of cycles"}}, {"clear-vectors", VariantType::Bool, false, {"Clear vectors when starting processing for a new CCDB entry (latest value will not be kept)"}}}}; } diff --git a/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx b/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx index 37608f1bd34b1..5e5eba87dafe2 100644 --- a/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx +++ b/Detectors/GRP/workflows/src/GRPLHCIFfileSpec.cxx @@ -245,8 +245,8 @@ DataProcessorSpec getGRPLHCIFfileSpec() outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "GRP_LHCIFData"}, Lifetime::Sporadic); return DataProcessorSpec{ "grp-lhc-if-file-processor", - Inputs{{"inputConfig", "GRP", "DCS_CONFIG_FILE", Lifetime::Timeframe}, - {"inputConfigFileName", "GRP", "DCS_CONFIG_NAME", Lifetime::Timeframe}}, + Inputs{{"inputConfig", "GRP", "DCS_CONFIG_FILE", Lifetime::Sporadic}, + {"inputConfigFileName", "GRP", "DCS_CONFIG_NAME", Lifetime::Sporadic}}, outputs, AlgorithmSpec{adaptFromTask()}, Options{{"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}}}; diff --git a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx index 69c3b9a3561d3..d738976ff4ffd 100644 --- a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx +++ b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx @@ -18,6 +18,8 @@ #include #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "MFTBase/GeometryTGeo.h" +#include "ITSBase/GeometryTGeo.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" @@ -96,7 +98,33 @@ class AlignerTask : public Task gGeoManager->SetName(std::string(o2::base::NameConf::CCDBOBJECT).c_str()); auto fnm = o2::base::NameConf::getAlignedGeomFileName(); gGeoManager->Export(fnm.c_str()); - LOG(info) << "Stored to local file " << fnm; + LOGP(info, "Stored aligned geometry to local file {}", fnm); + + // create GeometryTGeo for detectors which support it + if (mDetsMask[DetID::ITS] +#ifdef ENABLE_UPGRADES + || mDetsMask[DetID::IT3] +#endif + ) { + auto itsTGeo = o2::its::GeometryTGeo::Instance(); + itsTGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2GRot)); + TFile outF("its_GeometryTGeo.root", "recreate"); + outF.WriteObjectAny(itsTGeo, "o2::its::GeometryTGeo", "ccdb_object"); + LOGP(info, "Stored ITS geometry to {}", outF.GetName()); + outF.Close(); + itsTGeo->destroy(); + } + if (mDetsMask[DetID::MFT]) { + auto mftTGeo = o2::mft::GeometryTGeo::Instance(); + mftTGeo->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + TFile outF("mft_GeometryTGeo.root", "recreate"); + outF.WriteObjectAny(mftTGeo, "o2::mft::GeometryTGeo", "ccdb_object"); + LOGP(info, "Stored MFT geometry to {}", outF.GetName()); + outF.Close(); + mftTGeo->destroy(); + } + // consider upgrades? + pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } @@ -122,7 +150,10 @@ DataProcessorSpec getAlignerSpec(DetID::mask_t dets) false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::Alignments, // geometry - inputs); + inputs, // inputs + false, // ask-once + false, // dprop + DetID::getNames(dets)); // only selected detectors return DataProcessorSpec{ "geometry-aligned-producer", diff --git a/Detectors/GRP/workflows/src/create-grp-ecs.cxx b/Detectors/GRP/workflows/src/create-grp-ecs.cxx index 252ab3845e146..d9a73f0737799 100644 --- a/Detectors/GRP/workflows/src/create-grp-ecs.cxx +++ b/Detectors/GRP/workflows/src/create-grp-ecs.cxx @@ -15,8 +15,10 @@ #include #include #include "DataFormatsParameters/GRPECSObject.h" +#include "DataFormatsCTP/Configuration.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" #include "CommonUtils/NameConf.h" #include "CommonUtils/StringUtils.h" @@ -29,24 +31,30 @@ enum CCDBRefreshMode { NONE, ASYNC, SYNC }; -void createGRPECSObject(const std::string& dataPeriod, - int run, - int runTypeI, - int nHBPerTF, - const std::string& _detsReadout, - const std::string& _detsContinuousRO, - const std::string& _detsTrigger, - const std::string& flpList, - long tstart, - long tend, - long tstartCTP, - long tendCTP, - long marginAtSOR, - long marginAtEOR, - const std::string& ccdbServer = "", - const std::string& metaDataStr = "", - CCDBRefreshMode refresh = CCDBRefreshMode::NONE) +int createGRPECSObject(const std::string& dataPeriod, + int run, + int runOrig, // in case of replay + int runTypeI, + int nHBPerTF, + const std::string& _detsReadout, + const std::string& _detsContinuousRO, + const std::string& _detsTrigger, + const std::string& flpList, + long tstart, + long tend, + long tstartCTP, + long tendCTP, + long marginAtSOR, + long marginAtEOR, + const std::string& ccdbServer = "", + std::string ccdbServerInp = "", + const std::string& metaDataStr = "", + CCDBRefreshMode refresh = CCDBRefreshMode::NONE) { + int retValGLO = 0; + int retValRCT = 0; + int retValGLOmd = 0; + int retValCTP = 0; // substitute TRG by CTP std::regex regCTP(R"((^\s*|,\s*)(TRG)(\s*,|\s*$))"); std::string detsReadout{std::regex_replace(_detsReadout, regCTP, "$1CTP$3")}; @@ -74,6 +82,8 @@ void createGRPECSObject(const std::string& dataPeriod, tendVal = tend + marginAtEOR; } GRPECSObject grpecs; + o2::ctp::CTPConfiguration* ctpConfig = nullptr; + o2::ctp::CTPConfiguration ctpConfigNew; grpecs.setTimeStart(tstart); grpecs.setTimeEnd(tend); grpecs.setTimeStartCTP(tstartCTP); @@ -115,17 +125,59 @@ void createGRPECSObject(const std::string& dataPeriod, } }; + if (ccdbServerInp.empty()) { + ccdbServerInp = ccdbServer; + } + if (runOrig > 0 && runOrig != run && tend <= tstart && !ccdbServerInp.empty()) { // create CTP config + try { + auto& bcm = o2::ccdb::BasicCCDBManager::instance(); + bcm.setURL(ccdbServerInp); + bcm.setFatalWhenNull(false); + ctpConfig = bcm.getForRun("CTP/Config/Config", runOrig); + if (!ctpConfig) { + throw std::runtime_error(fmt::format("Failed to access CTP/Config/Config for original run {}", runOrig)); + } + std::string cfstr = ctpConfig->getConfigString(), srun{fmt::format("run {}", run)}, srunOrig{fmt::format("run {}", runOrig)}; + o2::utils::Str::replaceAll(cfstr, srunOrig, srun); + ctpConfigNew.loadConfigurationRun3(cfstr); + ctpConfigNew.setRunNumber(run); + } catch (std::exception e) { + LOGP(error, "Failed to create CTP/Config/Config from the original run {}, reason: {}", runOrig, e.what()); + } + } + toKeyValPairs(metaDataStr); if (!ccdbServer.empty()) { CcdbApi api; + const std::string objPath{"GLO/Config/GRPECS"}; api.init(ccdbServer); metadata["responsible"] = "ECS"; metadata[o2::base::NameConf::CCDBRunTag.data()] = std::to_string(run); metadata["EOR"] = fmt::format("{}", tend); - api.storeAsTFileAny(&grpecs, objPath, metadata, tstart, tendVal); // making it 1-year valid to be sure we have something - LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}", ccdbServer, objPath, tstart, tendVal, tstart, tend); + retValGLO = api.storeAsTFileAny(&grpecs, objPath, metadata, tstart, tendVal); // making it 1-year valid to be sure we have something + if (retValGLO == 0) { + LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}", ccdbServer, objPath, tstart, tendVal, tstart, tend); + } else { + LOGP(alarm, "Upload to {}/{} with validity {}:{} for SOR:{}/EOR:{} FAILED, returned with code {}", ccdbServer, objPath, tstart, tendVal, tstart, tend, retValGLO); + } + if ((runType == GRPECSObject::RunType::PHYSICS || runType == GRPECSObject::RunType::COSMICS) && tstart >= tend) { // also create the RCT/Info/RunInformation entry in case the run type is PHYSICS, to be finalized at EOR + char tempChar{}; + std::map mdRCT; + mdRCT["SOR"] = std::to_string(tstart); + mdRCT["EOR"] = std::to_string(tend); + mdRCT["SOX"] = std::to_string(tstartCTP); + mdRCT["EOX"] = std::to_string(tendCTP); + long startValRCT = (long)run; + long endValRCT = (long)(run + 1); + retValRCT = api.storeAsBinaryFile(&tempChar, sizeof(tempChar), "tmp.dat", "char", "RCT/Info/RunInformation", mdRCT, startValRCT, endValRCT); + if (retValRCT == 0) { + LOGP(info, "Uploaded initial RCT object to {}/{} with validity {}:{}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT); + } else { + LOGP(alarm, "Upload of initial RCT object to {}/{} with validity {}:{} FAILED, returned with code {}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT, retValRCT); + } + } if (tend > tstart) { // override SOR version to the same limits metadata.erase("EOR"); @@ -135,27 +187,55 @@ void createGRPECSObject(const std::string& dataPeriod, std::string etag = itETag->second; etag.erase(remove(etag.begin(), etag.end(), '\"'), etag.end()); LOGP(info, "Overriding run {} SOR-only version {}{}{}/{} validity to match complete SOR/EOR version validity", run, ccdbServer, ccdbServer.back() == '/' ? "" : "/", prevHeader["Valid-From"], etag); - api.updateMetadata(objPath, {}, std::max(tstart, tendVal - 1), etag, tendVal); + retValGLOmd = api.updateMetadata(objPath, {}, std::max(tstart, tendVal - 1), etag, tendVal); + if (retValGLOmd != 0) { + LOGP(alarm, "Overriding run {} SOR-only version {}{}{}/{} validity to match complete SOR/EOR version validity FAILED", run, ccdbServer, ccdbServer.back() == '/' ? "" : "/", prevHeader["Valid-From"], etag); + } } - if (runType == GRPECSObject::RunType::PHYSICS) { // also storing the RCT/Info/RunInformation entry in case the run type is PHYSICS and if we are at the end of run + if (runType == GRPECSObject::RunType::PHYSICS || runType == GRPECSObject::RunType::COSMICS) { // also storing the RCT/Info/RunInformation entry in case the run type is PHYSICS and if we are at the end of run char tempChar{}; std::map mdRCT; mdRCT["SOR"] = std::to_string(tstart); mdRCT["EOR"] = std::to_string(tend); + mdRCT["SOX"] = std::to_string(tstartCTP); + mdRCT["EOX"] = std::to_string(tendCTP); long startValRCT = (long)run; long endValRCT = (long)(run + 1); - api.storeAsBinaryFile(&tempChar, sizeof(tempChar), "tmp.dat", "char", "RCT/Info/RunInformation", mdRCT, startValRCT, endValRCT); - LOGP(info, "Uploaded RCT object to {}/{} with validity {}:{}", ccdbServer, "RCT/Info/RunInformation", startValRCT, endValRCT); + retValRCT = api.updateMetadata("RCT/Info/RunInformation", mdRCT, startValRCT); + if (retValRCT == 0) { + LOGP(info, "Updated RCT object to SOR:{}/EOR:{} SOX:{}/EOX:{}", tstart, tend, tstartCTP, tendCTP); + } else { + LOGP(alarm, "Update of RCT object to SOR:{}/EOR:{} SOX:{}/EOX:{} FAILED, returned with code {}", tstart, tend, tstartCTP, tendCTP, retValRCT); + } } } + if (ctpConfig && ctpConfigNew.getRunNumber() == run) { // create CTP config + std::map metadataCTP; + metadataCTP["runNumber"] = fmt::format("{}", run); + metadataCTP["comment"] = fmt::format("cloned from run {}", runOrig); + retValCTP = api.storeAsTFileAny(&ctpConfigNew, "CTP/Config/Config", metadataCTP, tstart, tendVal); + if (retValCTP == 0) { + LOGP(info, "Uploaded to {}/{} with validity {}:{} for SOR:{}/EOR:{}, cloned from run {}", ccdbServer, "CTP/Config/Config", tstart, tendVal, tstart, tend, runOrig); + } else { + LOGP(alarm, "Upload to {}/{} with validity {}:{} for SOR:{}/EOR:{} (cloned from run {}) FAILED, returned with code {}", ccdbServer, "CTP/Config/Config", tstart, tendVal, tstart, tend, runOrig, retValCTP); + } + } } else { // write a local file auto fname = o2::base::NameConf::getGRPECSFileName(); TFile grpF(fname.c_str(), "recreate"); grpF.WriteObjectAny(&grpecs, grpecs.Class(), o2::base::NameConf::CCDBOBJECT.data()); - LOG(info) << "Stored to local file " << fname; + grpF.Close(); + LOGP(info, "Stored GRPECS to local file {}", fname); + if (ctpConfig && ctpConfigNew.getRunNumber() == run) { + std::string ctnpfname = fmt::format("CTPConfig_{}_from_{}.root", run, runOrig); + TFile ctpF(ctnpfname.c_str(), "recreate"); + ctpF.WriteObjectAny(&ctpConfigNew, ctpConfigNew.Class(), o2::base::NameConf::CCDBOBJECT.data()); + ctpF.Close(); + LOGP(info, "Stored CTPConfig to local file {}", ctnpfname); + } } - // + if (refresh != CCDBRefreshMode::NONE && !ccdbServer.empty()) { auto cmd = fmt::format("curl -I -i -s \"{}{}latest/%5Cw%7B3%7D/.*/`date +%s000`/?prepare={}\"", ccdbServer, ccdbServer.back() == '/' ? "" : "/", refresh == CCDBRefreshMode::SYNC ? "sync" : "true"); auto t0 = std::chrono::high_resolution_clock::now(); @@ -163,6 +243,10 @@ void createGRPECSObject(const std::string& dataPeriod, auto t1 = std::chrono::high_resolution_clock::now(); LOGP(info, "Executed [{}] -> {} in {:.3f} s", cmd, res, std::chrono::duration_cast(t1 - t0).count() / 1000.f); } + if (retValGLO != 0 || retValRCT != 0 || retValGLOmd != 0 || retValCTP != 0) { + return 4; + } + return 0; } int main(int argc, char** argv) @@ -184,19 +268,21 @@ int main(int argc, char** argv) add_option("run,r", bpo::value(), "run number"); add_option("run-type,t", bpo::value()->default_value(int(GRPECSObject::RunType::NONE)), "run type"); add_option("hbf-per-tf,n", bpo::value()->default_value(128), "number of HBFs per TF"); - add_option("detectors,d", bpo::value()->default_value("all"), "comma separated list of detectors"); - add_option("continuous,c", bpo::value()->default_value("ITS,TPC,TOF,MFT,MCH,MID,ZDC,FT0,FV0,FDD,CTP"), "comma separated list of detectors in continuous readout mode"); - add_option("triggering,g", bpo::value()->default_value("FT0,FV0"), "comma separated list of detectors providing a trigger"); - add_option("flps,f", bpo::value()->default_value(""), "comma separated list of FLPs in the data taking"); + add_option("detectors,d", bpo::value()->default_value("all"), "comma separated list of detectors"); + add_option("continuous,c", bpo::value()->default_value("ITS,TPC,TOF,MFT,MCH,MID,ZDC,FT0,FV0,FDD,CTP"), "comma separated list of detectors in continuous readout mode"); + add_option("triggering,g", bpo::value()->default_value("FT0,FV0"), "comma separated list of detectors providing a trigger"); + add_option("flps,f", bpo::value()->default_value(""), "comma separated list of FLPs in the data taking"); add_option("start-time,s", bpo::value()->default_value(0), "ECS run start time in ms, now() if 0"); add_option("end-time,e", bpo::value()->default_value(0), "ECS run end time in ms, start-time+3days is used if 0"); add_option("start-time-ctp", bpo::value()->default_value(0), "run start CTP time in ms, same as ECS if not set or 0"); add_option("end-time-ctp", bpo::value()->default_value(0), "run end CTP time in ms, same as ECS if not set or 0"); add_option("ccdb-server", bpo::value()->default_value("http://alice-ccdb.cern.ch"), "CCDB server for upload, local file if empty"); + add_option("ccdb-server-input", bpo::value()->default_value(""), "CCDB server for inputs (if needed, e.g. CTPConfig), dy default ccdb-server is used"); add_option("meta-data,m", bpo::value()->default_value("")->implicit_value(""), "metadata as key1=value1;key2=value2;.."); - add_option("refresh", bpo::value()->default_value("")->implicit_value("async"), R"(refresh server cache after upload: "none" (or ""), "async" (non-blocking) and "sync" (blocking))"); + add_option("refresh", bpo::value()->default_value("")->implicit_value("async"), R"(refresh server cache after upload: "none" (or ""), "async" (non-blocking) and "sync" (blocking))"); add_option("marginSOR", bpo::value()->default_value(4 * o2::ccdb::CcdbObjectInfo::DAY), "validity at SOR"); add_option("marginEOR", bpo::value()->default_value(10 * o2::ccdb::CcdbObjectInfo::MINUTE), "validity margin to add after EOR"); + add_option("original-run,o", bpo::value()->default_value(0), "if >0, use as the source run to create CTP/Config/Config object"); opt_all.add(opt_general).add(opt_hidden); bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); @@ -217,17 +303,17 @@ int main(int argc, char** argv) } if (vm.count("run") == 0) { std::cerr << "ERROR: " - << "obligator run number is missing" << std::endl; + << "obligatory run number is missing" << std::endl; std::cerr << opt_general << std::endl; exit(3); } if (vm.count("period") == 0) { std::cerr << "ERROR: " - << "obligator data taking period name is missing" << std::endl; + << "obligatory data taking period name is missing" << std::endl; std::cerr << opt_general << std::endl; exit(3); } - std::string refreshStr = vm["refresh"].as(); + std::string refreshStr = vm["refresh"].as(); CCDBRefreshMode refresh = CCDBRefreshMode::NONE; if (!refreshStr.empty() && refreshStr != "none") { if (refreshStr == "async") { @@ -239,9 +325,10 @@ int main(int argc, char** argv) } } - createGRPECSObject( + int retVal = createGRPECSObject( vm["period"].as(), vm["run"].as(), + vm["original-run"].as(), vm["run-type"].as(), vm["hbf-per-tf"].as(), vm["detectors"].as(), @@ -255,6 +342,14 @@ int main(int argc, char** argv) vm["marginSOR"].as(), vm["marginEOR"].as(), vm["ccdb-server"].as(), + vm["ccdb-server-input"].as(), vm["meta-data"].as(), refresh); + + if (retVal != 0) { + std::cerr << "ERROR: " + << "Either GLO/Config/GRPECS or RCT/Info/RunInformation could not be stored correctly to CCDB" << std::endl; + std::cerr << opt_general << std::endl; + exit(retVal); + } } diff --git a/Detectors/GRP/workflows/src/rct-updater-workflow.cxx b/Detectors/GRP/workflows/src/rct-updater-workflow.cxx new file mode 100644 index 0000000000000..624e89ec4076d --- /dev/null +++ b/Detectors/GRP/workflows/src/rct-updater-workflow.cxx @@ -0,0 +1,204 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/Logger.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputSpec.h" +#include "Framework/Task.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsCalibration/Utils.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector options{{"configKeyValues", o2::framework::VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + std::swap(workflowOptions, options); +} + +#include "DetectorsBase/GRPGeomHelper.h" +#include "CCDB/CcdbApi.h" +#include "DataFormatsParameters/GRPECSObject.h" + +namespace o2::rct +{ +class RCTUpdaterSpec : public o2::framework::Task +{ + public: + RCTUpdaterSpec(std::shared_ptr gr) : mGGCCDBRequest(gr) {} + ~RCTUpdaterSpec() final = default; + + void init(InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mUpdateInterval = std::max(0.1f, ic.options().get("update-interval")); + auto ccdb = ic.options().get("ccdb-server"); + if (!ccdb.empty() && ccdb != "none") { + mCCDBApi = std::make_unique(); + mCCDBApi->init(ic.options().get("ccdb-server")); + } else { + LOGP(warn, "No ccdb server provided, no RCT update will be done"); + } + mTimeToleranceMS = ic.options().get("max-diff-orbit-creationtime"); + mMaxWarnings = ic.options().get("max-warn-tf-discard"); + } + + void run(ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // do we have RCT object? + const auto* grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + mNHBFPerTF = grp->getNHBFPerTF(); + if (mNHBFPerTF < 1) { + mNHBFPerTF = 32; + } + mRunNumber = tinfo.runNumber; + mUpdateIntervalTF = uint32_t(mUpdateInterval / (mNHBFPerTF * o2::constants::lhc::LHCOrbitMUS * 1e-6)); // convert update interval in seconds to interval in TFs + LOGP(info, "Will update RCT after {} TFs of {} HBFs ({}s was requested)", mUpdateIntervalTF, mNHBFPerTF, mUpdateInterval); + mOrbitReset = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(); + mMinOrbit = 0xffffffff; + mMaxOrbit = 0; + if (grp->getRunType() == o2::parameters::GRPECS::PHYSICS || grp->getRunType() == o2::parameters::GRPECS::COSMICS) { + mEnabled = true; + } else { + LOGP(warning, "Run {} type is {}, disabling RCT update", mRunNumber, o2::parameters::GRPECS::RunTypeNames[grp->getRunType()]); + mEnabled = false; + } + if (mEnabled) { + if (mCCDBApi) { + auto md = mCCDBApi->retrieveHeaders("RCT/Info/RunInformation", {}, grp->getRun()); + if (md.empty()) { + mEnabled = false; + LOGP(alarm, "RCT object is missing for {} run {}, disabling RCT updater", o2::parameters::GRPECS::RunTypeNames[grp->getRunType()], grp->getRun()); + } + } + } + } + if (mEnabled) { + // make sure that the orbit makes sense, since sometimes at the EOR bogus TFs are sent. + long ts = mOrbitReset + long(tinfo.firstTForbit * o2::constants::lhc::LHCOrbitMUS * 1e-3); + if (mTimeToleranceMS > 0 && std::abs(int64_t(tinfo.creation) - ts) < mTimeToleranceMS) { + if (tinfo.firstTForbit < mMinOrbit) { + mMinOrbit = tinfo.firstTForbit; + } + if (tinfo.firstTForbit > mMaxOrbit) { + mMaxOrbit = tinfo.firstTForbit; + } + if (tinfo.tfCounter > mLastTFUpdate + mUpdateIntervalTF) { // need to update + mLastTFUpdate = tinfo.tfCounter; + updateRCT(); + } + } else { + static int nWarn = 0; + if (nWarn < mMaxWarnings) { + nWarn++; + LOGP(warn, "timestamp {} for orbit {} and orbit reset time {} differs by >{} from the TF creation time {}, ignore TF {}", ts, tinfo.firstTForbit, mOrbitReset / 1000, mTimeToleranceMS, tinfo.creation, tinfo.tfCounter); + } + } + } + } + + void endOfStream(framework::EndOfStreamContext& ec) final + { + if (mEnabled) { + updateRCT(); + mEnabled = false; + } + } + + void stop() final + { + if (mEnabled) { + updateRCT(); + mEnabled = false; + } + } + + void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj) final + { + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + } + + void updateRCT() + { + std::map mdRCT; + if (mMinOrbit > mMaxOrbit) { + return; + } + mdRCT["STF"] = std::to_string(long(mMinOrbit * o2::constants::lhc::LHCOrbitMUS * 1e-3) + mOrbitReset); + mdRCT["ETF"] = std::to_string(long((mMaxOrbit + mNHBFPerTF - 1) * o2::constants::lhc::LHCOrbitMUS * 1e-3) + mOrbitReset); + long startValRCT = (long)mRunNumber; + long endValRCT = (long)(mRunNumber + 1); + if (mCCDBApi) { + int retValRCT = mCCDBApi->updateMetadata("RCT/Info/RunInformation", mdRCT, startValRCT); + if (retValRCT == 0) { + LOGP(info, "Updated {}/RCT/Info/RunInformation object for run {} with TF start:{} end:{}", mCCDBApi->getURL(), mRunNumber, mdRCT["STF"], mdRCT["ETF"]); + } else { + LOGP(alarm, "Update of RCT object for run {} with TF start:{} end:{} FAILED, returned with code {}", mRunNumber, mdRCT["STF"], mdRCT["ETF"], retValRCT); + } + } else { + LOGP(info, "CCDB update disabled, TF timestamp range is {}:{}", mdRCT["STF"], mdRCT["ETF"]); + } + } + + private: + bool mEnabled = true; + float mUpdateInterval = 1.; + int mUpdateIntervalTF = 1; + uint32_t mMinOrbit = 0xffffffff; + uint32_t mMaxOrbit = 0; + uint32_t mLastTFUpdate = 0; + long mTimeToleranceMS = 5000; + long mOrbitReset = 0; + int mRunNumber = 0; + int mNHBFPerTF = 32; + int mMaxWarnings = 0; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mCCDBApi; +}; +} // namespace o2::rct + +// ------------------------------------------------------------------ +#include "Framework/runDataProcessing.h" +#include "Framework/DataProcessorSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + std::vector inputs{{"ctfdone", "CTF", "DONE", 0, Lifetime::Timeframe}}; + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "RCTUPD_DUMMY"}, Lifetime::Sporadic); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "RCTUPD_DUMMY"}, Lifetime::Sporadic); + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); // query only once all objects except mag.field + specs.push_back(DataProcessorSpec{ + "rct-updater", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest)}, + Options{ + {"update-interval", VariantType::Float, 1.f, {"update every ... seconds"}}, + {"max-diff-orbit-creationtime", VariantType::Int, -1, {"max difference between TF creation-time and orbit-time to discard TF, do not check if negative"}}, + {"max-warn-tf-discard", VariantType::Int, 10, {"max N warnings about discarding bad TFs"}}, + {"ccdb-server", VariantType::String, "http://ccdb-test.cern.ch:8080", {"CCDB to update"}}}}); + return specs; +} diff --git a/Detectors/GlobalTracking/CMakeLists.txt b/Detectors/GlobalTracking/CMakeLists.txt index 816af91470d52..ecf77ea741e21 100644 --- a/Detectors/GlobalTracking/CMakeLists.txt +++ b/Detectors/GlobalTracking/CMakeLists.txt @@ -8,46 +8,52 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) o2_add_library(GlobalTracking TARGETVARNAME targetName SOURCES src/MatchTPCITS.cxx src/MatchTOF.cxx + src/MatchHMP.cxx src/MatchTPCITSParams.cxx src/MatchCosmics.cxx src/MatchCosmicsParams.cxx src/MatchGlobalFwd.cxx src/MatchGlobalFwdAssessment.cxx - src/MatchITSTPCQC.cxx - src/ITSTPCMatchingQCParams.cxx src/MatchGlobalFwdParam.cxx src/MatchTOFParams.cxx - PUBLIC_LINK_LIBRARIES O2::Framework - O2::DataFormatsTPC - O2::DataFormatsITSMFT - O2::DataFormatsITS - O2::DataFormatsFT0 - O2::DataFormatsTOF - O2::DataFormatsTRD - O2::ITSReconstruction - O2::FT0Reconstruction - O2::TPCFastTransformation - O2::GPUO2Interface - O2::TPCBase - O2::TPCReconstruction - O2::TPCCalibration - O2::TOFBase - O2::TOFCalibration - O2::TOFWorkflowUtils - O2::SimConfig - O2::DataFormatsFT0 - O2::DataFormatsGlobalTracking - O2::ITStracking - O2::MFTTracking - O2::MCHTracking - O2::MathUtils - O2::ReconstructionDataFormats - O2::Steer) + src/MatchITSTPCQC.cxx + src/ITSTPCMatchingQCParams.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsTPC + O2::DataFormatsITSMFT + O2::DataFormatsITS + O2::DataFormatsFT0 + O2::DataFormatsTOF + O2::DataFormatsHMP + O2::DataFormatsTRD + O2::ITSReconstruction + O2::FT0Reconstruction + O2::TPCFastTransformation + O2::GPUO2Interface + O2::GPUTracking + O2::TPCBase + O2::TPCReconstruction + O2::TPCCalibration + O2::TOFBase + O2::HMPIDReconstruction + O2::TOFCalibration + O2::TOFWorkflowUtils + O2::SimConfig + O2::DataFormatsFT0 + O2::DataFormatsGlobalTracking + O2::ITStracking + O2::MFTTracking + O2::MCHTracking + O2::MathUtils + O2::ReconstructionDataFormats + O2::Steer + $<$:O2::ITS3Reconstruction>) o2_target_root_dictionary(GlobalTracking HEADERS include/GlobalTracking/MatchTPCITSParams.h @@ -55,12 +61,13 @@ o2_target_root_dictionary(GlobalTracking include/GlobalTracking/MatchGlobalFwdParam.h include/GlobalTracking/MatchGlobalFwdAssessment.h include/GlobalTracking/MatchTOF.h + include/GlobalTracking/MatchHMP.h include/GlobalTracking/MatchCosmics.h include/GlobalTracking/MatchCosmicsParams.h - include/GlobalTracking/MatchITSTPCQC.h - include/GlobalTracking/ITSTPCMatchingQCParams.h include/GlobalTracking/TrackMethods.h - include/GlobalTracking/TrackCuts.h) + include/GlobalTracking/TrackCuts.h + include/GlobalTracking/MatchITSTPCQC.h + include/GlobalTracking/ITSTPCMatchingQCParams.h) if (OpenMP_CXX_FOUND) diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h index 402b3488193fe..9356232de375c 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchCosmicsParams.h @@ -32,6 +32,7 @@ struct MatchCosmicsParams : public o2::conf::ConfigurableParamHelper& getMatchedFwdTracks() const { return mMatchedTracks; } const std::vector& getMFTMatchingPlaneParams() const { return mMFTMatchPlaneParams; } const std::vector& getMCHMatchingPlaneParams() const { return mMCHMatchPlaneParams; } const std::vector& getMFTMCHMatchInfo() const { return mMatchingInfo; } const std::vector& getMatchLabels() const { return mMatchLabels; } + /// Converts mchTrack parameters to Forward coordinate system + o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam& mchTrack); + /// Converts FwdTrack parameters to MCH coordinate system + o2::mch::TrackParam FwdtoMCH(const o2::dataformats::GlobalFwdTrack& fwdtrack); + private: void updateTimeDependentParams(); void fillBuiltinFunctions(); @@ -277,9 +285,6 @@ class MatchGlobalFwd return true; } - /// Converts mchTrack parameters to Forward coordinate system - o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam& mchTrack); - float mBz = -5.f; ///< nominal Bz in kGauss float mMatchingPlaneZ = sLastMFTPlaneZ; ///< MCH-MFT matching plane Z position Float_t mMFTDiskThicknessInX0 = 0.042 / 5; ///< MFT disk thickness in radiation length @@ -288,6 +293,9 @@ class MatchGlobalFwd int mMFTROFrameLengthInBC = 0; ///< MFT RO frame in BC (for MFT cont. mode only) float mMFTROFrameLengthMUS = -1.; ///< MFT RO frame in \mus float mMFTROFrameLengthMUSInv = -1.; ///< MFT RO frame in \mus inverse + int mMFTROFrameBiasInBC = 0; ///< MFT ROF bias in BC wrt to orbit start + float mMFTROFrameBiasMUS = -1.; ///< MFT ROF bias in \mus + float mMFTROFrameBiasMUSInv = -1.; ///< MFT ROF bias in \mus inverse std::map mMatchingFunctionMap; ///< MFT-MCH Matching function std::map mCutFunctionMap; ///< MFT-MCH Candidate cut function @@ -325,11 +333,15 @@ class MatchGlobalFwd std::vector mMFTMatchPlaneParams; ///< MFT track parameters at matching plane std::vector mMCHMatchPlaneParams; ///< MCH track parameters at matching plane + std::map>> mCandidates; ///< map each MCH track id to vector of best match candidates + const o2::itsmft::TopologyDictionary* mMFTDict{nullptr}; // cluster patterns dictionary o2::itsmft::ChipMappingMFT mMFTMapping; bool mMCTruthON = false; ///< Flag availability of MC truth bool mUseMIDMCHMatch = false; ///< Flag for using MCHMID matches (TrackMCHMID) - int mSaveMode = 0; ///< Output mode [0 = SaveBestMatch; 1 = SaveAllMatches; 2 = SaveTrainingData] + bool mUseTrackTime = false; ///< Flag for using the MCH or MCHMID track time information to select the MFT ROF(s) + int mSaveMode = 0; ///< Output mode [0 = SaveBestMatch; 1 = SaveAllMatches; 2 = SaveTrainingData; 3 = SaveNCandidates] + int mNCandidates = 5; ///< Numbers of matching candidates to save in savemode=3 MatchingType mMatchingType = MATCHINGUNDEFINED; TGeoManager* mGeoManager; }; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h index b64fc7cd0cf16..94afdfcf8bcff 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdAssessment.h @@ -133,7 +133,7 @@ class GloFwdAssessment std::array, 7> mTrackPhiNCls = {nullptr}; std::array, 7> mTrackXYNCls = {nullptr}; std::array, 7> mTrackEtaPhiNCls = {nullptr}; - std::unique_ptr mTrackTanl = nullptr; + std::unique_ptr mTrackCotl = nullptr; // Histos and data for MC analysis std::vector mNameOfTrackTypes = {"Rec", @@ -176,7 +176,7 @@ class GloFwdAssessment kTH3GMTrackXPullPtEta, kTH3GMTrackYPullPtEta, kTH3GMTrackPhiPullPtEta, - kTH3GMTrackTanlPullPtEta, + kTH3GMTrackCotlPullPtEta, kTH3GMTrackInvQPtPullPtEta, kTH3GMTrackReducedChi2PtEta, kTH3GMTrackPtEtaChi2, @@ -200,7 +200,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, "TH3GMTrackXPullPtEta"}, {kTH3GMTrackYPullPtEta, "TH3GMTrackYPullPtEta"}, {kTH3GMTrackPhiPullPtEta, "TH3GMTrackPhiPullPtEta"}, - {kTH3GMTrackTanlPullPtEta, "TH3GMTrackTanlPullPtEta"}, + {kTH3GMTrackCotlPullPtEta, "TH3GMTrackCotlPullPtEta"}, {kTH3GMTrackInvQPtPullPtEta, "TH3GMTrackInvQPtPullPtEta"}, {kTH3GMTrackReducedChi2PtEta, "TH3GMTrackReducedChi2PtEta"}, {kTH3GMCloseMatchPtEtaChi2, "TH3GMCloseMatchPtEtaChi2"}, @@ -222,7 +222,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, "TH3GMTrackXPullPtEta"}, {kTH3GMTrackYPullPtEta, "TH3GMTrackYPullPtEta"}, {kTH3GMTrackPhiPullPtEta, "TH3GMTrackPhiPullPtEta"}, - {kTH3GMTrackTanlPullPtEta, "TH3GMTrackTanlPullPtEta"}, + {kTH3GMTrackCotlPullPtEta, "TH3GMTrackCotlPullPtEta"}, {kTH3GMTrackInvQPtPullPtEta, "TH3GMTrackInvQPtPullPtEta"}, {kTH3GMTrackReducedChi2PtEta, "TH3GMTrackReducedChi2PtEta"}, {kTH3GMCloseMatchPtEtaChi2, "TH3GMCloseMatchPtEtaChi2"}, @@ -244,7 +244,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackYPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackPhiPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, - {kTH3GMTrackTanlPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, + {kTH3GMTrackCotlPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -10, 10}}, {kTH3GMTrackInvQPtPullPtEta, {40, 0, 20, 16, 2.2, 3.8, 200, -50, 50}}, {kTH3GMTrackReducedChi2PtEta, {40, 0, 20, 16, 2.2, 3.8, 1000, 0, 100}}, {kTH3GMCloseMatchPtEtaChi2, {40, 0, 20, 16, 2.2, 3.8, 1000, 0, 100}}, @@ -266,7 +266,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackYPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackPhiPullPtEta, R"(p_{t}_{MC})"}, - {kTH3GMTrackTanlPullPtEta, R"(p_{t}_{MC})"}, + {kTH3GMTrackCotlPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackInvQPtPullPtEta, R"(p_{t}_{MC})"}, {kTH3GMTrackReducedChi2PtEta, R"(p_{t}_{MC})"}, {kTH3GMCloseMatchPtEtaChi2, R"(p_{t}_{Fit})"}, @@ -288,7 +288,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackYPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackPhiPullPtEta, R"(\eta_{MC})"}, - {kTH3GMTrackTanlPullPtEta, R"(\eta_{MC})"}, + {kTH3GMTrackCotlPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackInvQPtPullPtEta, R"(\eta_{MC})"}, {kTH3GMTrackReducedChi2PtEta, R"(\eta_{MC})"}, {kTH3GMCloseMatchPtEtaChi2, R"(\eta_{Fit})"}, @@ -310,7 +310,7 @@ class GloFwdAssessment {kTH3GMTrackXPullPtEta, R"(\Delta X/\sigma_{X})"}, {kTH3GMTrackYPullPtEta, R"(\Delta Y/\sigma_{Y})"}, {kTH3GMTrackPhiPullPtEta, R"(\Delta \phi/\sigma_{\phi})"}, - {kTH3GMTrackTanlPullPtEta, R"(\Delta \tan(\lambda)/\sigma_{tan(\lambda)})"}, + {kTH3GMTrackCotlPullPtEta, R"(\Delta \cot(\lambda)/\sigma_{cot(\lambda)})"}, {kTH3GMTrackInvQPtPullPtEta, R"((\Delta q/p_t)/\sigma_{q/p_{t}})"}, {kTH3GMTrackReducedChi2PtEta, R"(\chi^2/d.f.)"}, {kTH3GMCloseMatchPtEtaChi2, R"(Match \chi^2)"}, @@ -337,8 +337,8 @@ class GloFwdAssessment kInvQPtResMCHVsPt, kPhiPullVsEta, kPhiPullVsPt, - kTanlPullVsEta, - kTanlPullVsPt, + kCotlPullVsEta, + kCotlPullVsPt, kInvQPtPullVsEta, kInvQPtPullVsPt, kNSlicedTH3 @@ -359,8 +359,8 @@ class GloFwdAssessment {kInvQPtResMCHVsPt, "InvQPtResMCHVsPt"}, {kPhiPullVsEta, "PhiPullVsEta"}, {kPhiPullVsPt, "PhiPullVsPt"}, - {kTanlPullVsEta, "TanlPullVsEta"}, - {kTanlPullVsPt, "TanlPullVsPt"}, + {kCotlPullVsEta, "CotlPullVsEta"}, + {kCotlPullVsPt, "CotlPullVsPt"}, {kInvQPtPullVsEta, "InvQPtPullVsEta"}, {kInvQPtPullVsPt, "InvQPtPullVsPt"}}; @@ -379,8 +379,8 @@ class GloFwdAssessment {kInvQPtResMCHVsPt, kTH3GMTrackInvQPtResMCHPtEta}, {kPhiPullVsEta, kTH3GMTrackPhiPullPtEta}, {kPhiPullVsPt, kTH3GMTrackPhiPullPtEta}, - {kTanlPullVsEta, kTH3GMTrackTanlPullPtEta}, - {kTanlPullVsPt, kTH3GMTrackTanlPullPtEta}, + {kCotlPullVsEta, kTH3GMTrackCotlPullPtEta}, + {kCotlPullVsPt, kTH3GMTrackCotlPullPtEta}, {kInvQPtPullVsEta, kTH3GMTrackInvQPtPullPtEta}, {kInvQPtPullVsPt, kTH3GMTrackInvQPtPullPtEta}}; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h index 9ba6563cec334..757d74ff40780 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwdParam.h @@ -26,7 +26,8 @@ namespace globaltracking enum SaveMode { kBestMatch = 0, kSaveAll, - kSaveTrainingData }; + kSaveTrainingData, + kSaveNCandidates }; struct GlobalFwdMatchingParam : public o2::conf::ConfigurableParamHelper { @@ -38,9 +39,11 @@ struct GlobalFwdMatchingParam : public o2::conf::ConfigurableParamHelper +#include +#include +#include +#include +#include +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" +#include "ReconstructionDataFormats/MatchInfoTOFReco.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "CommonDataFormat/EvIndex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsBase/Propagator.h" +#include "MathUtils/Cartesian.h" +#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "CommonConstants/GeomConstants.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsHMP/Cluster.h" +#include "GlobalTracking/MatchTPCITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ReconstructionDataFormats/PID.h" +#include "TPCFastTransform.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/TrackHMP.h" + +#include "HMPIDBase/Geo.h" +#include "DataFormatsHMP/Cluster.h" +#include "DataFormatsHMP/Trigger.h" + +namespace o2 +{ + +namespace globaltracking +{ +class RecoContainer; +} + +namespace dataformats +{ +template +class MCTruthContainer; +} + +namespace globaltracking +{ + +class MatchHMP +{ + + using Geo = o2::hmpid::Geo; + using Cluster = o2::hmpid::Cluster; + using Trigger = o2::hmpid::Trigger; + using evGIdx = o2::dataformats::EvIndex; + using evIdx = o2::dataformats::EvIndex; + using timeEst = o2::dataformats::TimeStampWithError; + using matchTrack = std::pair; + + public: + ///< perform matching for provided input + void run(const o2::globaltracking::RecoContainer& inp); + + ///< print settings + void print() const; + void printCandidatesHMP() const; + + ///< set extra time tolerance + void setExtraTimeToleranceTRD(float val) { mExtraTimeToleranceTRD = val; } + ///< get extra tolerance + float getExtraTimeToleranceTRD() const { return mExtraTimeToleranceTRD; } + + ///< set extra time tolerance + void setExtraTimeToleranceTOF(float val) { mExtraTimeToleranceTOF = val; } + ///< get extra tolerance + float getExtraTimeToleranceTOF() const { return mExtraTimeToleranceTOF; } + + /* enum DebugFlagTypes : UInt_t { + MatchTreeAll = 0x1 << 1, // ///< produce matching candidates tree for all candidates + }; + */ + enum trackType : int8_t { UNCONS = 0, + CONSTR, + SIZE, + TPC = 0, + ITSTPC, + TPCTRD, + ITSTPCTRD, + SIZEALL }; + + std::vector& getMatchedTrackVector(o2::globaltracking::MatchHMP::trackType index) { return mMatchedTracks[index]; } + + std::vector& getMatchedHMPLabelsVector(o2::globaltracking::MatchHMP::trackType index) { return mOutHMPLabels[index]; } ///< get vector of HMP label of matched tracks + + void setTS(unsigned long creationTime) + { + mTimestamp = creationTime; + } + unsigned long getTS() const { return mTimestamp; } + + private: + // bool prepareFITData(); + int prepareInteractionTimes(); + bool prepareTracks(); + bool prepareHMPClusters(); + void doFastMatching(); + void doMatching(); + + static int intTrkCha(o2::track::TrackParCov* pTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz); // find track-PC intersection, retuns chamber ID + static int intTrkCha(int ch, o2::dataformats::TrackHMP* pHmpTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz); // find track-PC intersection, retuns chamber ID + + bool intersect(Double_t pnt[3], Double_t norm[3]) const; + + void addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addTPCTOFSeed(const o2::dataformats::TrackTPCTOF& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); + void addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, timeEst timeMUS); + + // Data members + const o2::globaltracking::RecoContainer* mRecoCont = nullptr; + + o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + + // for derived class + int mCurrTracksTreeEntry = 0; ///< current tracks tree entry loaded to memory + + bool mMCTruthON = false; ///< flag availability of MC truth + + ///========== Parameters to be set externally, e.g. from CCDB ==================== + float mBz = 0; ///< nominal Bz + float mMaxInvPt = 999.; ///< derived from nominal Bz + + // to be done later + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds + float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z + // float mTimeTolerance = 1e3; ///< tolerance in ns for track-TOF time bracket matching + float mExtraTimeToleranceTRD = 0.; ///< extra tolerance in ns for track-TOF time bracket matching + float mExtraTimeToleranceTOF = 0.; ///< extra tolerance in ns for track-TOF time bracket matching + float mSigmaTimeCut = 3.; ///< number of sigmas to cut on time when matching the track to the TOF cluster + + static constexpr Double_t BC_TIME = o2::constants::lhc::LHCBunchSpacingNS; // bunch crossing in ns + static constexpr Double_t BC_TIME_INV = 1. / BC_TIME; // inv bunch crossing in ns + static constexpr Double_t BC_TIME_INPS = BC_TIME * 1000; // bunch crossing in ps + static constexpr Double_t BC_TIME_INPS_INV = 1. / BC_TIME_INPS; // inv bunch crossing in ps + + bool mIsFIT = false; + bool mIsTPCused = false; + bool mIsITSTPCused = false; + bool mIsTPCTOFused = false; + bool mIsTPCTRDused = false; + bool mIsITSTPCTOFused = false; + bool mIsTPCTRDTOFused = false; + bool mIsITSTPCTRDused = false; + bool mIsITSTPCTRDTOFused = false; + bool mSetHighPurity = false; + + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + + unsigned long mTimestamp = 0; ///< in ms + + ///>>>------ these are input arrays which should not be modified by the matching code + // since this info is provided by external device + gsl::span mHMPClustersArray; ///< input HMPID clusters + gsl::span mHMPTriggersArray; ///< input HMPID triggers + + const o2::dataformats::MCTruthContainer* mHMPClusLabels; ///< input HMP clusters MC labels (pointer to read from tree) + + ///< working copy of the input tracks + std::vector mTracksWork[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< track params prepared for matching + time value + std::vector mHMPTriggersWork; + std::vector mTracksLblWork[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< track labels + + std::vector mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< indices of track entry in mTracksWork + std::vector mHMPTriggersIndexCache; ///< indices of track entry in mHMPTriggersWork + + ///< array of matched HMPCluster with matching information + std::vector mMatchedTracks[o2::globaltracking::MatchHMP::trackType::SIZE]; // this is the output of the matching -> UNCONS, CONSTR + std::vector mOutHMPLabels[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< HMP label of matched tracks + + std::vector mTrackGid[o2::globaltracking::MatchHMP::trackType::SIZE]; ///< expected times and others + std::vector mMatchedTracksIndex[o2::globaltracking::MatchHMP::trackType::SIZE]; // vector of indexes of the tracks to be matched + + int mNumOfTriggers; // number of HMP triggers + + ///----------- aux stuff --------------/// + static constexpr float MAXSNP = 0.85; // max snp of ITS or TPC track at xRef to be matched + + TStopwatch mTimerTot; + TStopwatch mTimerMatchITSTPC; + TStopwatch mTimerMatchTPC; + TStopwatch mTimerDBG; + + ClassDef(MatchHMP, 1); +}; +} // namespace globaltracking +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h index 39acc8f93368f..983fadcb0092e 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h @@ -26,8 +26,11 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/PID.h" #include #include +#include +#include namespace o2 { @@ -46,47 +49,63 @@ struct LblInfo { class MatchITSTPCQC { public: + enum matchType : int { TPC = 0, + ITS, + SIZE }; + MatchITSTPCQC() = default; ~MatchITSTPCQC(); bool init(); void initDataRequest(); void run(o2::framework::ProcessingContext& ctx); - void setDataRequest(std::shared_ptr dr) { mDataRequest = dr; } + void setDataRequest(const std::shared_ptr& dr) { mDataRequest = dr; } void finalize(); void reset(); - TH1D* getHistoPt() const { return mPt; } - TH1D* getHistoPtTPC() const { return mPtTPC; } - TEfficiency* getFractionITSTPCmatch() const { return mFractionITSTPCmatch; } + TH1D* getHistoPtNum(matchType m) const { return mPtNum[m]; } + TH1D* getHistoPtDen(matchType m) const { return mPtDen[m]; } + TEfficiency* getFractionITSTPCmatch(matchType m) const { return mFractionITSTPCmatch[m]; } + + TH1D* getHistoPtNumNoEta0(matchType m) const { return mPtNum_noEta0[m]; } + TH1D* getHistoPtDenNoEta0(matchType m) const { return mPtDen_noEta0[m]; } + TEfficiency* getFractionITSTPCmatchNoEta0(matchType m) const { return mFractionITSTPCmatch_noEta0[m]; } + + TH1F* getHistoPhiNum(matchType m) const { return mPhiNum[m]; } + TH1F* getHistoPhiDen(matchType m) const { return mPhiDen[m]; } + TEfficiency* getFractionITSTPCmatchPhi(matchType m) const { return mFractionITSTPCmatchPhi[m]; } + + TH2F* getHistoPhiVsPtNum(matchType m) const { return mPhiVsPtNum[m]; } + TH2F* getHistoPhiVsPtDen(matchType m) const { return mPhiVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiVsPt(matchType m) const { return mFractionITSTPCmatchPhiVsPt[m]; } - TH1F* getHistoPhi() const { return mPhi; } - TH1F* getHistoPhiTPC() const { return mPhiTPC; } - TEfficiency* getFractionITSTPCmatchPhi() const { return mFractionITSTPCmatchPhi; } + TH1F* getHistoEtaNum(matchType m) const { return mEtaNum[m]; } + TH1F* getHistoEtaDen(matchType m) const { return mEtaDen[m]; } + TEfficiency* getFractionITSTPCmatchEta(matchType m) const { return mFractionITSTPCmatchEta[m]; } - TH2F* getHistoPhiVsPt() const { return mPhiVsPt; } - TH2F* getHistoPhiVsPtTPC() const { return mPhiVsPtTPC; } - TEfficiency* getFractionITSTPCmatchPhiVsPt() const { return mFractionITSTPCmatchPhiVsPt; } + TH2F* getHistoEtaVsPtNum(matchType m) const { return mEtaVsPtNum[m]; } + TH2F* getHistoEtaVsPtDen(matchType m) const { return mEtaVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaVsPt(matchType m) const { return mFractionITSTPCmatchEtaVsPt[m]; } - TH1F* getHistoEta() const { return mEta; } - TH1F* getHistoEtaTPC() const { return mEtaTPC; } - TEfficiency* getFractionITSTPCmatchEta() const { return mFractionITSTPCmatchEta; } + TH2F* getHistoClsVsPtNum(matchType m) const { return mClsVsPtNum[m]; } + TH2F* getHistoClsVsPtDen(matchType m) const { return mClsVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchClsVsPt(matchType m) const { return mFractionITSTPCmatchClsVsPt[m]; } - TH2F* getHistoEtaVsPt() const { return mEtaVsPt; } - TH2F* getHistoEtaVsPtTPC() const { return mEtaVsPtTPC; } - TEfficiency* getFractionITSTPCmatchEtaVsPt() const { return mFractionITSTPCmatchEtaVsPt; } + TH2F* getHistoChi2VsPtNum(matchType m) const { return mChi2VsPtNum[m]; } + TH2F* getHistoChi2VsPtDen(matchType m) const { return mChi2VsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchChi2VsPt(matchType m) const { return mFractionITSTPCmatchChi2VsPt[m]; } - TH1F* getHistoPtPhysPrim() const { return mPtPhysPrim; } - TH1F* getHistoPtTPCPhysPrim() const { return mPtTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhysPrim() const { return mFractionITSTPCmatchPhysPrim; } + TH1F* getHistoPtPhysPrimNum(matchType m) const { return mPtPhysPrimNum[m]; } + TH1F* getHistoPtPhysPrimDen(matchType m) const { return mPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim(matchType m) const { return mFractionITSTPCmatchPhysPrim[m]; } - TH1F* getHistoPhiPhysPrim() const { return mPhiPhysPrim; } - TH1F* getHistoPhiTPCPhysPrim() const { return mPhiTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhiPhysPrim() const { return mFractionITSTPCmatchPhiPhysPrim; } + TH1F* getHistoPhiPhysPrimNum(matchType m) const { return mPhiPhysPrimNum[m]; } + TH1F* getHistoPhiPhysPrimDen(matchType m) const { return mPhiPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiPhysPrim(matchType m) const { return mFractionITSTPCmatchPhiPhysPrim[m]; } - TH1F* getHistoEtaPhysPrim() const { return mEtaPhysPrim; } - TH1F* getHistoEtaTPCPhysPrim() const { return mEtaTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchEtaPhysPrim() const { return mFractionITSTPCmatchEtaPhysPrim; } + TH1F* getHistoEtaPhysPrimNum(matchType m) const { return mEtaPhysPrimNum[m]; } + TH1F* getHistoEtaPhysPrimDen(matchType m) const { return mEtaPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaPhysPrim(matchType m) const { return mFractionITSTPCmatchEtaPhysPrim[m]; } TH2F* getHistoResidualPt() const { return mResidualPt; } TH2F* getHistoResidualPhi() const { return mResidualPhi; } @@ -95,72 +114,219 @@ class MatchITSTPCQC TH1F* getHistoChi2Matching() const { return mChi2Matching; } TH1F* getHistoChi2Refit() const { return mChi2Refit; } TH2F* getHistoTimeResVsPt() const { return mTimeResVsPt; } + TH1F* getHistoDCAr() const { return mDCAr; } + TH2F* getHistoDCArVsPtNum() const { return mDCArVsPtNum; } + TH2F* getHistoDCArVsPtDen() const { return mDCArVsPtDen; } + TEfficiency* getFractionITSTPCmatchDCArVsPt() const { return mFractionITSTPCmatchDCArVsPt; } + + TH1D* getHisto1OverPtNum(matchType m) const { return m1OverPtNum[m]; } + TH1D* getHisto1OverPtDen(matchType m) const { return m1OverPtDen[m]; } + TEfficiency* getFractionITSTPCmatch1OverPt(matchType m) const { return mFractionITSTPCmatch1OverPt[m]; } + + TH1D* getHisto1OverPtPhysPrimNum(matchType m) const { return m1OverPtPhysPrimNum[m]; } + TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } + void getHistos(TObjArray& objar); + + /// \brief Publishes the histograms to the publisher e.g. the one provided by the QC task + /// \tparam T type of the publisher + /// \param publisher the publisher e.g. getObjectsManager() + template + void publishHistograms(const std::shared_ptr& publisher) + { + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + publisher->startPublishing(mPtNum[i]); + publisher->startPublishing(mPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch[i]); + + publisher->startPublishing(mPtNum_noEta0[i]); + publisher->startPublishing(mPtDen_noEta0[i]); + publisher->startPublishing(mFractionITSTPCmatch_noEta0[i]); + + // Phi + publisher->startPublishing(mPhiNum[i]); + publisher->startPublishing(mPhiDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhi[i]); + + publisher->startPublishing(mPhiVsPtNum[i]); + publisher->startPublishing(mPhiVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsPt[i]); + + // Eta + publisher->startPublishing(mEtaNum[i]); + publisher->startPublishing(mEtaDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEta[i]); + + publisher->startPublishing(mEtaVsPtNum[i]); + publisher->startPublishing(mEtaVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsPt[i]); + + // Clusters + publisher->startPublishing(mClsVsPtNum[i]); + publisher->startPublishing(mClsVsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchClsVsPt[i]); + + // Chi2 + publisher->startPublishing(mChi2VsPtNum[i]); + publisher->startPublishing(mChi2VsPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatchChi2VsPt[i]); + + // 1/pt + publisher->startPublishing(m1OverPtNum[i]); + publisher->startPublishing(m1OverPtDen[i]); + publisher->startPublishing(mFractionITSTPCmatch1OverPt[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + publisher->startPublishing(mPtNumVsTrkPID[i][j]); + publisher->startPublishing(mPtDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPtVsTrkPID[i][j]); + + // Phi + publisher->startPublishing(mPhiNumVsTrkPID[i][j]); + publisher->startPublishing(mPhiDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + + // Eta + publisher->startPublishing(mEtaNumVsTrkPID[i][j]); + publisher->startPublishing(mEtaDenVsTrkPID[i][j]); + publisher->startPublishing(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + if (mUseMC) { + publisher->startPublishing(mPhiPhysPrimNum[i]); + publisher->startPublishing(mPhiPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhiPhysPrim[i]); + publisher->startPublishing(mPtPhysPrimNum[i]); + publisher->startPublishing(mPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim[i]); + publisher->startPublishing(mEtaPhysPrimNum[i]); + publisher->startPublishing(mEtaPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchEtaPhysPrim[i]); + publisher->startPublishing(m1OverPtPhysPrimNum[i]); + publisher->startPublishing(m1OverPtPhysPrimDen[i]); + publisher->startPublishing(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } + } + publisher->startPublishing(mChi2Matching); + publisher->startPublishing(mChi2Refit); + publisher->startPublishing(mTimeResVsPt); + publisher->startPublishing(mResidualPt); + publisher->startPublishing(mResidualPhi); + publisher->startPublishing(mResidualEta); + publisher->startPublishing(mDCAr); + publisher->startPublishing(mDCArVsPtNum); + publisher->startPublishing(mDCArVsPtDen); + publisher->startPublishing(mFractionITSTPCmatchDCArVsPt); + } + void setSources(GID::mask_t src) { mSrc = src; } + void setUseTrkPID(bool b) { mUseTrkPID = b; } + bool getUseTrkPID() const { return mUseTrkPID; } void setUseMC(bool b) { mUseMC = b; } bool getUseMC() const { return mUseMC; } void deleteHistograms(); void setBz(float bz) { mBz = bz; } - // track selection - bool selectTrack(o2::tpc::TrackTPC const& track); - void setPtCut(float v) { mPtCut = v; } - void setEtaCut(float v) { mEtaCut = v; } + // ITS track + void setMinPtITSCut(float v) { mPtITSCut = v; }; + void setEtaITSCut(float v) { mEtaITSCut = v; }; // TODO: define 2 different values for min and max (**) + void setMinNClustersITS(float v) { mMinNClustersITS = v; } + void setMaxChi2PerClusterITS(float v) { mMaxChi2PerClusterITS = v; } + // TO DO: define an agreed way to implement the setter for ITS matching (min. # layers, which layers) + // [...] --> exploit the method TrackCuts::setRequireHitsInITSLayers(...) + // TPC track + void setMinPtTPCCut(float v) { mPtTPCCut = v; }; + void setEtaTPCCut(float v) { mEtaTPCCut = v; }; // TODO: define 2 different values for min and max (***) void setMinNTPCClustersCut(float v) { mNTPCClustersCut = v; } void setMinDCAtoBeamPipeCut(std::array v) { setMinDCAtoBeamPipeDistanceCut(v[0]); setMinDCAtoBeamPipeYCut(v[1]); } - void setMinDCAtoBeamPipeDistanceCut(float v) { mDCACut = v; } - void setMinDCAtoBeamPipeYCut(float v) { mDCACutY = v; } + void setMinDCAtoBeamPipeDistanceCut(float v) { mDCATPCCut = v; } + void setMinDCAtoBeamPipeYCut(float v) { mDCATPCCutY = v; } + // ITS-TPC kinematics + void setPtCut(float v) { mPtCut = v; } + void setMaxPtCut(float v) { mPtMaxCut = v; } + void setEtaCut(float v) { mEtaCut = v; } // TODO: define 2 different values for min and max (*) private: std::shared_ptr mDataRequest; o2::globaltracking::RecoContainer mRecoCont; - GID::mask_t mSrc = GID::getSourcesMask("TPC,ITS-TPC"); - GID::mask_t mAllowedSources = GID::getSourcesMask("TPC,ITS-TPC"); + GID::mask_t mSrc = GID::getSourcesMask("ITS,TPC,ITS-TPC"); + GID::mask_t mAllowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC"); // TPC gsl::span mTPCTracks; + // ITS + gsl::span mITSTracks; // ITS-TPC gsl::span mITSTPCTracks; - bool mUseMC = false; - float mBz = 0; ///< nominal Bz - std::unordered_map mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, - // value is the LbLinfo with the id of the track with the highest pT found with that label so far, - // and the flag to say if it is a physical primary or not - std::unordered_map mMapTPCLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, - // value is the LblInfo with the id of the track with the highest number of TPC clusters found - // with that label so far, and the flag to say if it is a physical primary or not - o2::steer::MCKinematicsReader mcReader; // reader of MC information + bool mUseMC = false; // Usage of the MC information + bool mUseTrkPID = false; // Usage of the PID hypothesis in tracking + float mBz = 0; ///< nominal Bz + std::array, matchType::SIZE> mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, + // value is the LbLinfo with the id of the track with the highest pT found with that label so far, + // and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapRefLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, + // value is the LblInfo with the id of the track with the highest number of TPC clusters found + // with that label so far, and the flag to say if it is a physical primary or not + o2::steer::MCKinematicsReader mcReader; // reader of MC information // Pt - TH1D* mPt = nullptr; - TH1D* mPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatch = nullptr; - TH1F* mPtPhysPrim = nullptr; - TH1F* mPtTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhysPrim = nullptr; + TH1D* mPtNum[matchType::SIZE] = {}; + TH1D* mPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch[matchType::SIZE] = {}; + TH1D* mPtNum_noEta0[matchType::SIZE] = {}; + TH1D* mPtDen_noEta0[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch_noEta0[matchType::SIZE] = {}; + TH1F* mPtPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim[matchType::SIZE] = {}; + // Pt split per PID hypothesis in tracking + TH1D* mPtNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPtDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPtVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Phi - TH1F* mPhi = nullptr; - TH1F* mPhiTPC = nullptr; - TEfficiency* mFractionITSTPCmatchPhi = nullptr; - TH1F* mPhiPhysPrim = nullptr; - TH1F* mPhiTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhiPhysPrim = nullptr; - TH2F* mPhiVsPt = nullptr; - TH2F* mPhiVsPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatchPhiVsPt = nullptr; + TH1F* mPhiNum[matchType::SIZE] = {}; + TH1F* mPhiDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhi[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiPhysPrim[matchType::SIZE] = {}; + TH2F* mPhiVsPtNum[matchType::SIZE] = {}; + TH2F* mPhiVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsPt[matchType::SIZE] = {}; + // Phi split per PID hypothesis in tracking + TH1D* mPhiNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mPhiDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Eta - TH1F* mEta = nullptr; - TH1F* mEtaTPC = nullptr; - TEfficiency* mFractionITSTPCmatchEta = nullptr; - TH1F* mEtaPhysPrim = nullptr; - TH1F* mEtaTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchEtaPhysPrim = nullptr; - TH2F* mEtaVsPt = nullptr; - TH2F* mEtaVsPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatchEtaVsPt = nullptr; + TH1F* mEtaNum[matchType::SIZE] = {}; + TH1F* mEtaDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEta[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimNum[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaPhysPrim[matchType::SIZE] = {}; + TH2F* mEtaVsPtNum[matchType::SIZE] = {}; + TH2F* mEtaVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsPt[matchType::SIZE] = {}; + // Clusters + TH2F* mClsVsPtNum[matchType::SIZE] = {}; + TH2F* mClsVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchClsVsPt[matchType::SIZE] = {}; + // Chi2 + TH2F* mChi2VsPtNum[matchType::SIZE] = {}; + TH2F* mChi2VsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchChi2VsPt[matchType::SIZE] = {}; + // Eta split per PID hypothesis in tracking + TH1D* mEtaNumVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TH1D* mEtaDenVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsTrkPID[matchType::SIZE][track::PID::NIDs] = {}; // Residuals TH2F* mResidualPt = nullptr; TH2F* mResidualPhi = nullptr; @@ -169,20 +335,44 @@ class MatchITSTPCQC TH1F* mChi2Matching = nullptr; TH1F* mChi2Refit = nullptr; TH2F* mTimeResVsPt = nullptr; + TH1F* mDCAr = nullptr; + TH2F* mDCArVsPtNum = nullptr; + TH2F* mDCArVsPtDen = nullptr; + TEfficiency* mFractionITSTPCmatchDCArVsPt = nullptr; + // 1/Pt + TH1D* m1OverPtNum[matchType::SIZE] = {}; + TH1D* m1OverPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch1OverPt[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimNum[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim1OverPt[matchType::SIZE] = {}; void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D = false); int mNTPCSelectedTracks = 0; - int mNITSTPCSelectedTracks = 0; + int mNITSSelectedTracks = 0; + int mNITSTPCSelectedTracks[matchType::SIZE] = {0, 0}; // cut values + // ITS track + float mPtITSCut = 0.f; // min pT for ITS track + float mEtaITSCut = 1e10f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS = 0; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits{}; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + // TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS-TPC kinematics float mPtCut = 0.1f; - float mEtaCut = 1.4f; - int32_t mNTPCClustersCut = 60; - float mDCACut = 100.f; - float mDCACutY = 10.f; + float mPtMaxCut = 1e10f; + float mEtaCut = 1e10f; // 1e10f as defaults of Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h + // TODO: define 2 different values for min and max (*) - ClassDefNV(MatchITSTPCQC, 1); + ClassDefNV(MatchITSTPCQC, 2); }; } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h index db0abf1233e00..b66e5b143a898 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h @@ -90,7 +90,7 @@ class MatchTOF public: ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); + void run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit = 0); void setCosmics() { @@ -154,43 +154,39 @@ class MatchTOF void setTPCVDrift(const o2::tpc::VDriftCorrFact& v); void setTPCCorrMaps(o2::gpu::CorrectionMapsHelper* maph); - ///< set input TPC tracks cluster indices - void setTPCTrackClusIdxInp(const gsl::span inp) - { - mTPCTrackClusIdx = inp; - } - - ///< set input TPC cluster sharing map - void setTPCClustersSharingMap(const gsl::span inp) - { - mTPCRefitterShMap = inp; - } - - ///< set input TPC clusters - void setTPCClustersInp(const o2::tpc::ClusterNativeAccess* inp) - { - mTPCClusterIdxStruct = inp; - } - void setFIT(bool value = true) { mIsFIT = value; } - static int findFITIndex(int bc, const gsl::span& FITRecPoints); + static int findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit); void checkRefitter(); bool makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCTOF& trConstr); ///< populate externally provided container by TOF-time-constrained TPC tracks - template - void makeConstrainedTPCTracks(V& container) + template + void makeConstrainedTPCTracks(MtcInfo& mtcCont, MCInfo& MCCont, CTrack& trcCont) { + auto nmatch = getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC).size(); // preliminary matches + mtcCont.reserve(nmatch); + trcCont.reserve(nmatch); + if (mMCTruthON) { + MCCont.reserve(nmatch); + } checkRefitter(); - int nmatched = mMatchedTracks[trkType::TPC].size(), nconstrained = 0; - container.resize(nmatched); - for (unsigned i = 0; i < nmatched; i++) { - if (makeConstrainedTPCTrack(i, container[nconstrained])) { + + auto& mclabs = getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC); + auto& info = getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC); + int nconstrained = 0; + for (unsigned i = 0; i < nmatch; i++) { + auto& ctr = trcCont.emplace_back(); + if (makeConstrainedTPCTrack(i, ctr)) { + mtcCont.push_back(info[i]); + if (mMCTruthON) { + MCCont.push_back(mclabs[i]); + } nconstrained++; + } else { + trcCont.pop_back(); } } - container.resize(nconstrained); } void setTS(unsigned long creationTime) { mTimestamp = creationTime; } @@ -201,10 +197,14 @@ class MatchTOF void storeMatchable(bool val = true) { mStoreMatchable = val; } + void setNlanes(int lanes) { mNlanes = lanes; } + private: bool prepareFITData(); int prepareInteractionTimes(); bool prepareTPCData(); + void propagateTPCTracks(int sec); + void propagateConstrTracks(int sec); void addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); void addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); void addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr); @@ -215,20 +215,27 @@ class MatchTOF void doMatching(int sec); void doMatchingForTPC(int sec); - void selectBestMatches(); - static void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); - static void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); + void selectBestMatches(int sec); + void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); + void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); bool propagateToRefX(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, o2::track::TrackLTIntegral& intLT); - bool propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); + bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); + bool propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz, float pos[3]); + void updateTL(o2::track::TrackLTIntegral& intLT, float deltal); void updateTimeDependentParams(); + static bool mHasFillScheme; + static bool mFillScheme[o2::constants::lhc::LHCMaxBunches]; + //================================================================ // Data members const o2::globaltracking::RecoContainer* mRecoCont = nullptr; o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF + int mNlanes = 3; ///< for multi-threading in matching + // TOF matching params (work in progress) const MatchTOFParams* mMatchParams = nullptr; @@ -269,6 +276,8 @@ class MatchTOF unsigned long mTimestamp = 0; ///< in ms + unsigned long mFirstTForbit = 0; ///< First orbit in TF (needed to align FT0 recpoints) + // from ruben gsl::span mTPCTracksArray; ///< input TPC tracks span @@ -280,6 +289,7 @@ class MatchTOF /// data needed for refit of time-constrained TPC tracks gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; ///< TPC cluster transformation @@ -294,22 +304,23 @@ class MatchTOF /// <<<----- /// mTracksWork[trkType::SIZE]; /// mTracksLblWork[trkType::SIZE]; /// mLTinfos[trkType::SIZE]; /// mTrackGid[trkType::SIZE]; /// mTracksWork[o2::constants::math::NSectors][trkType::SIZE]; ///< track params prepared for matching + time value + std::vector mTracksLblWork[o2::constants::math::NSectors][trkType::SIZE]; ///< TPCITS track labels + std::vector mLTinfos[o2::constants::math::NSectors][trkType::SIZE]; ///< expected times and others + std::vector mTrackGid[o2::constants::math::NSectors][trkType::SIZE]; ///< expected times and others ///< per sector indices of track entry in mTracksWork std::array, o2::constants::math::NSectors> mTracksSectIndexCache[trkType::SIZE]; + std::array, o2::constants::math::NSectors> mTracksSeed[trkType::SIZE]; + std::vector mVZtpcOnly[o2::constants::math::NSectors]; - std::vector mExtraTPCFwdTime; /// mTOFClusWork; /// mSideTPC; /// mExtraTPCFwdTime[o2::constants::math::NSectors]; ///< track extra params for TPC tracks: Fws Max time + std::vector mTOFClusWork; ///< track params prepared for matching + std::vector mSideTPC[o2::constants::math::NSectors]; ///< track side for TPC tracks ///< per sector indices of TOF cluster entry in mTOFClusWork std::array, o2::constants::math::NSectors> mTOFClusSectIndexCache; - /// mMatchedTracksPairs; + ///< array of track-TOFCluster pairs from the matching std::vector mMatchedTracksPairsSec[o2::constants::math::NSectors]; /// mMatchedTracks[trkType::SIZEALL]; // this is the output of the matching -> UNCONS, CONSTR std::vector mOutTOFLabels[trkType::SIZEALL]; ///< TOF label of matched tracks - std::vector mMatchedTracksIndex[trkType::SIZE]; // vector of indexes of the tracks to be matched + std::vector mMatchedTracksIndex[o2::constants::math::NSectors][trkType::SIZE]; // vector of indexes of the tracks to be matched int mNumOfClusters; // number of clusters to be matched int* mMatchedClustersIndex = nullptr; //[mNumOfClusters] @@ -329,6 +340,9 @@ class MatchTOF UInt_t mDBGFlags = 0; std::string mDebugTreeFileName = "dbg_matchTOF.root"; ///< name for the debug tree file + std::array mCovDiagInner{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + std::array mCovDiagOuter{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + ///----------- aux stuff --------------/// static constexpr float MAXSNP = 0.85; // max snp of ITS or TPC track at xRef to be matched @@ -336,7 +350,7 @@ class MatchTOF TStopwatch mTimerMatchITSTPC; TStopwatch mTimerMatchTPC; TStopwatch mTimerDBG; - ClassDefNV(MatchTOF, 5); + ClassDefNV(MatchTOF, 6); }; } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h index 503f96ec36f19..6ed36a6e18450 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOFParams.h @@ -23,8 +23,12 @@ namespace globaltracking { struct MatchTOFParams : public o2::conf::ConfigurableParamHelper { - float calibMaxChi2 = 3.0; - float nsigmaTimeCut = 4.; // number of sigmas for non-TPC track time resolution to consider + float calibMaxChi2 = 3.0; // max value of Chi2 allowed for using matched tracks in calibration + float nsigmaTimeCut = 4.; // number of sigmas for non-TPC track time resolution to consider + float maxResX = 1.; // max value of track resolution (X dir) used in TOF matching (truncation to that in case it is larger) + float maxResZ = 1.; // max value of track resolution (Z dir) used in TOF matching (truncation to that in case it is larger) + float maxChi2 = 10.; // max value of Chi2 accepted for matching to TOF + bool applyPIDcutTPConly = true; // apply PID cut on TPC only tracks O2ParamDef(MatchTOFParams, "MatchTOF"); }; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h index 16c8051f45d5a..00f2fc157a5ec 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h @@ -50,11 +50,18 @@ #include "ITSReconstruction/RecoGeomHelper.h" #include "TPCFastTransform.h" #include "GPUO2InterfaceRefit.h" +#include "GPUTPCGeometry.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsITSMFT/TrkClusRef.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "CorrectionMapsHelper.h" +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#include "MemoryResources/MemoryResources.h" +#endif +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif class TTree; @@ -86,7 +93,7 @@ namespace tpc { class TrackTPC; class VDriftCorrFact; -} +} // namespace tpc namespace gpu { @@ -110,7 +117,8 @@ enum TrackRejFlag : int { RejectOnTgl, RejectOnQ2Pt, RejectOnChi2, - NSigmaShift = 10 + NSigmaShift = 10, + RejectoOnPIDCorr = 20 }; ///< TPC track parameters propagated to reference X, with time bracket and index of @@ -124,7 +132,9 @@ struct TrackLocTPC : public o2::track::TrackParCov { float timeErr = 0.f; ///< time sigma (makes sense for constrained tracks only) int sourceID = 0; ///< TPC track origin in o2::dataformats::GlobalTrackID gid{}; // global track source ID (TPC track may be part of it) - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + uint8_t lowestRow = -1; + uint8_t padFromEdge = -1; Constraint_t constraint{Constrained}; float getCorrectedTime(float dt) const // return time0 corrected for extra drift (to match certain Z) @@ -133,19 +143,28 @@ struct TrackLocTPC : public o2::track::TrackParCov { } float getSignedDT(float dt) const // account for TPC side in time difference for dt=external_time - tpc.time0 { - return constraint == Constrained ? 0. : (constraint == ASide ? dt : -dt); + return constraint == Constrained ? 0.f : (constraint == ASide ? dt : -dt); } - ClassDefNV(TrackLocTPC, 1); + ClassDefNV(TrackLocTPC, 3); }; ///< ITS track outward parameters propagated to reference X, with time bracket and index of ///< original track in the currently loaded ITS reco output struct TrackLocITS : public o2::track::TrackParCov { + enum : uint16_t { CloneBefore = 0x1, + CloneAfter = 0x2 }; o2::math_utils::Bracketf_t tBracket; ///< bracketing time in \mus - int sourceID = 0; ///< track origin id - int roFrame = MinusOne; ///< ITS readout frame assigned to this track - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + int sourceID = 0; ///< track origin id + int roFrame = MinusOne; ///< ITS readout frame assigned to this track + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + float xrho = 0; ///< x*rho seen during propagation to reference X (as pion) + float dL = 0; ///< distance integrated during propagation to reference X (as pion) + bool hasCloneBefore() const { return getUserField() & CloneBefore; } + bool hasCloneAfter() const { return getUserField() & CloneAfter; } + int getCloneShift() const { return hasCloneBefore() ? -1 : (hasCloneAfter() ? 1 : 0); } + void setCloneBefore() { setUserField(getUserField() | CloneBefore); } + void setCloneAfter() { setUserField(getUserField() | CloneAfter); } ClassDefNV(TrackLocITS, 1); }; @@ -188,6 +207,8 @@ struct ABTrackLink : public o2::track::TrackParCov { uint8_t ladderID = 0xff; ///< ladder ID in the layer (used for seeds with 2 hits in the layer) float chi2 = 0.f; ///< chi2 after update + ABTrackLink() = default; + ~ABTrackLink() = default; ABTrackLink(const o2::track::TrackParCov& tr, int cl, int parID, int nextID, int lr, int nc, int ld, float _chi2) : o2::track::TrackParCov(tr), clID(cl), parentID(parID), nextOnLr(nextID), layerID(int8_t(lr)), nContLayers(int8_t(nc)), ladderID(uint8_t(ld)), chi2(_chi2) {} @@ -198,17 +219,25 @@ struct ABTrackLink : public o2::track::TrackParCov { float chi2NormPredict(float chi2cl) const { return (chi2 + chi2cl) / (1 + o2::its::RecoGeomHelper::getNLayers() - layerID); } }; +struct LinksPoolMT { + std::vector> threadPool; +}; + // AB primary seed: TPC track propagated to outermost ITS layer under specific InteractionCandidate hypothesis struct TPCABSeed { static constexpr int8_t NeedAlternative = -3; int tpcWID = MinusOne; ///< TPC track ID int ICCanID = MinusOne; ///< interaction candidate ID (they are sorted in increasing time) int winLinkID = MinusOne; ///< ID of the validated link + uint32_t linksEntry = 0; ///< 1st entry of the link + int nLinks = 0; ///< number of links int8_t lowestLayer = o2::its::RecoGeomHelper::getNLayers(); ///< lowest layer reached int8_t status = MinusOne; ///< status (RS TODO) + uint8_t threadID = 0; ///< thread ID o2::track::TrackParCov track{}; ///< Seed propagated to the outer layer under certain time constraint std::array firstInLr; ///< entry of 1st (best) hypothesis on each layer - std::vector trackLinks{}; ///< links + static LinksPoolMT* gLinksPool; ///< pool of links per thread + TPCABSeed(int id, int ic, const o2::track::TrackParCov& trc) : tpcWID(id), ICCanID(ic), track(trc) { firstInLr.fill(MinusOne); @@ -221,10 +250,17 @@ struct TPCABSeed { winLinkID = lID; status = Validated; } + int8_t getNLayers() const { return o2::its::RecoGeomHelper::getNLayers() - lowestLayer; } bool needAlteranative() const { return status == NeedAlternative; } void setNeedAlternative() { status = NeedAlternative; } - ABTrackLink& getLink(int i) { return trackLinks[i]; } - const ABTrackLink& getLink(int i) const { return trackLinks[i]; } + ABTrackLink& getLink(int i) { return gLinksPool->threadPool[threadID][i + linksEntry]; } + const ABTrackLink& getLink(int i) const { return gLinksPool->threadPool[threadID][i + linksEntry]; } + void addLink(const o2::track::TrackParCov& trc, int clID, int parentID, int nextID, int lr, int nc, int laddID, float chi2) + { + gLinksPool->threadPool[threadID].push_back({trc, clID, parentID, nextID, lr, nc, laddID, chi2}); + nLinks++; + } + int getNLinks() const { return nLinks; } auto getBestLinkID() const { return lowestLayer < o2::its::RecoGeomHelper::getNLayers() ? firstInLr[lowestLayer] : -1; @@ -233,7 +269,7 @@ struct TPCABSeed { { // check if some clusters used by the link or its parents are forbidden (already used by validatet track) while (linkID > MinusOne) { - const auto& link = trackLinks[linkID]; + const auto& link = getLink(linkID); if (link.clID > MinusOne && clStatus[link.clID] != MinusOne) { return true; } @@ -245,13 +281,15 @@ struct TPCABSeed { { // check if some clusters used by the link or its parents are forbidden (already used by validated track) while (linkID > MinusOne) { - const auto& link = trackLinks[linkID]; + const auto& link = getLink(linkID); if (link.clID > MinusOne) { clStatus[link.clID] = MinusTen; } linkID = link.parentID; } } + size_t sizeInternal() const { return sizeof(ABTrackLink) * getNLinks(); } + size_t capInternal() const { return sizeof(ABTrackLink) * getNLinks(); } }; struct InteractionCandidate : public o2::InteractionRecord { @@ -259,7 +297,6 @@ struct InteractionCandidate : public o2::InteractionRecord { int rofITS; // corresponding ITS ROF entry (in the ROFRecord vectors) uint32_t flag; // origin, etc. o2::dataformats::RangeReference seedsRef; // references to AB seeds - InteractionCandidate() = default; InteractionCandidate(const o2::InteractionRecord& ir, float t, float dt, int rof, uint32_t f = 0) : o2::InteractionRecord(ir), tBracket(t - dt, t + dt), rofITS(rof), flag(f) {} }; @@ -267,17 +304,30 @@ struct ITSChipClustersRefs { ///< contaner for sorted cluster indices for certain time window (usually ROF) and reference on the start and N clusters ///< for every chip using ClusRange = o2::dataformats::RangeReference; - std::vector clusterID; // indices of sorted clusters + std::vector clusterID; // indices of sorted clusters + +#ifndef ENABLE_UPGRADES std::array chipRefs; // offset and number of clusters in each chip ITSChipClustersRefs(int nclIni = 50000) { clusterID.reserve(nclIni); } +#else + std::vector chipRefs; // offset and number of clusters in each chip + ITSChipClustersRefs(int nchips = o2::its::RecoGeomHelper::getNChips(), int nclIni = 50000) + { + clusterID.reserve(nclIni); + chipRefs.resize(nchips, ClusRange()); + } +#endif + void clear() { clusterID.clear(); std::memset(chipRefs.data(), 0, chipRefs.size() * sizeof(ClusRange)); // reset chip->cluster references } + size_t sizeInternal() const { return sizeof(int) * clusterID.size(); } + size_t capInternal() const { return sizeof(int) * clusterID.capacity(); } }; class MatchTPCITS @@ -285,9 +335,6 @@ class MatchTPCITS public: using ITSCluster = o2::BaseCluster; using ClusRange = o2::dataformats::RangeReference; - using MCLabContCl = o2::dataformats::MCTruthContainer; - using MCLabContTr = std::vector; - using MCLabSpan = gsl::span; using TPCTransform = o2::gpu::TPCFastTransform; using BracketF = o2::math_utils::Bracket; using BracketIR = o2::math_utils::Bracket; @@ -303,9 +350,33 @@ class MatchTPCITS static constexpr int MaxSeedsPerLayer = 50; // TODO static constexpr int NITSLayers = o2::its::RecoGeomHelper::getNLayers(); ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); - - void setSkipTPCOnly(bool v) { mSkipTPCOnly = v; } +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) + void run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + void refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + bool refitTrackTPCITS(int slot, int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + void fillCalibDebug(int ifit, int iTPC, const o2::dataformats::TrackTPCITS& match, pmr::vector>& calib); + void reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + bool runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + void refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + bool refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs); +#endif // CLING + void setSkipTPCOnly(bool v) + { + mSkipTPCOnly = v; + } void setCosmics(bool v) { mCosmics = v; } bool isCosmics() const { return mCosmics; } void setNThreads(int n); @@ -313,6 +384,7 @@ class MatchTPCITS ///< perform all initializations void init(); void end(); + void reportTiming(); ///< clear results of previous event reco void clear(); @@ -320,6 +392,8 @@ class MatchTPCITS ///< set Bunch filling and init helpers for validation by BCs void setBunchFilling(const o2::BunchFilling& bf); + void setNHBPerTF(int n) { mNHBPerTF = n; } + ///< ITS readout mode void setITSTriggered(bool v) { mITSTriggered = v; } bool isITSTriggered() const { return mITSTriggered; } @@ -341,6 +415,12 @@ class MatchTPCITS // ==================== >> DPL-driven input >> ======================= void setITSDictionary(const o2::itsmft::TopologyDictionary* d) { mITSDict = d; } +#ifdef ENABLE_UPGRADES + void setIT3Dictionary(const o2::its3::TopologyDictionary* d) + { + mIT3Dict = d; + } +#endif ///< set flag to use MC truth void setMCTruthOn(bool v) @@ -361,13 +441,6 @@ class MatchTPCITS void printCandidatesTPC() const; void printCandidatesITS() const; - const std::vector& getMatchedTracks() const { return mMatchedTracks; } - const MCLabContTr& getMatchLabels() const { return mOutLabels; } - const MCLabContTr& getABTrackletLabels() const { return mABTrackletLabels; } - const std::vector& getABTrackletClusterIDs() const { return mABTrackletClusterIDs; } - const std::vector& getABTrackletRefs() const { return mABTrackletRefs; } - const std::vector& getTglITSTPC() const { return mTglITSTPC; } - //>>> ====================== options =============================>>> void setUseMatCorrFlag(MatCorrType f) { mUseMatCorrFlag = f; } auto getUseMatCorrFlag() const { return mUseMatCorrFlag; } @@ -376,9 +449,10 @@ class MatchTPCITS #ifdef _ALLOW_DEBUG_TREES_ enum DebugFlagTypes : UInt_t { - MatchTreeAll = 0x1 << 1, ///< produce matching candidates tree for all candidates - MatchTreeAccOnly = 0x1 << 2, ///< fill the matching candidates tree only once the cut is passed - WinnerMatchesTree = 0x1 << 3 ///< separate debug tree for winner matches + MatchTreeAll = 0x1 << 1, ///< produce matching candidates tree for all candidates + MatchTreeAccOnly = 0x1 << 2, ///< fill the matching candidates tree only once the cut is passed + WinnerMatchesTree = 0x1 << 3, ///< separate debug tree for winner matches + TPCOrigTree = 0x1 << 4 ///< original TPC tracks with some aux info }; ///< check if partucular flags are set bool isDebugFlag(UInt_t flags) const { return mDBGFlags & flags; } @@ -403,6 +477,7 @@ class MatchTPCITS ///< fill matching debug tree void fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2 = -1., float tCorr = 0.); void dumpWinnerMatches(); + void dumpTPCOrig(bool acc, int tpcIndex); #endif private: @@ -417,7 +492,7 @@ class MatchTPCITS bool prepareFITData(); int prepareInteractionTimes(); int prepareTPCTracksAfterBurner(); - void addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, o2::dataformats::GlobalTrackID srcGID, int tpcID); + int addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, o2::dataformats::GlobalTrackID srcGID, int tpcID); int preselectChipClusters(std::vector& clVecOut, const ClusRange& clRange, const ITSChipClustersRefs& itsChipClRefs, float trackY, float trackZ, float tolerY, float tolerZ) const; @@ -426,8 +501,6 @@ class MatchTPCITS void doMatching(int sec); - void refitWinners(); - bool refitTrackTPCITS(int iTPC, int& iITS); bool refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const; void selectBestMatches(); @@ -441,8 +514,8 @@ class MatchTPCITS int compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& tTPC, float& chi2) const; float getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, const o2::track::TrackParCov& trTPC) const; - bool propagateToRefX(o2::track::TrackParCov& trc); - void addLastTrackCloneForNeighbourSector(int sector); + bool propagateToRefX(o2::track::TrackParCov& trc, o2::track::TrackLTIntegral* lti = nullptr); + void addLastTrackCloneForNeighbourSector(int sector, o2::track::TrackLTIntegral* trackLTInt = nullptr); ///------------------- manipulations with matches records ---------------------- bool registerMatchRecordTPC(int iITS, int iTPC, float chi2, int candIC = MinusOne); @@ -473,7 +546,7 @@ class MatchTPCITS int time2ITSROFrameTrig(float t, int start) const { t -= mITSTimeBiasMUS; - while (start < mITSROFTimes.size()) { + while (start < int(mITSROFTimes.size())) { if (mITSROFTimes[start].getMax() > t) { return start; } @@ -504,14 +577,11 @@ class MatchTPCITS } // ========================= AFTERBURNER ========================= - void runAfterBurner(); int prepareABSeeds(); - void processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs); + void processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID); int followABSeed(const o2::track::TrackParCov& seed, const ITSChipClustersRefs& itsChipClRefs, int seedID, int lrID, TPCABSeed& ABSeed); int registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackParCov& trc, int clID, int parentID, int lr, int laddID, float chi2Cl); bool isBetter(float chi2A, float chi2B) { return chi2A < chi2B; } // RS FIMXE TODO - void refitABWinners(); - bool refitABTrack(int iITSAB, const TPCABSeed& seed); void accountForOverlapsAB(int lrSeed); float correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTPC& tTPC, const InteractionCandidate& cand) const; // RS FIXME will be needed for refit //================================================================ @@ -523,6 +593,9 @@ class MatchTPCITS float mBz = 0; ///< nominal Bz int mTFCount = 0; ///< internal TF counter for debugger int mNThreads = 1; ///< number of OMP threads + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv; o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF ///========== Parameters to be set externally, e.g. from CCDB ==================== @@ -540,33 +613,33 @@ class MatchTPCITS float YMaxAtXMatchingRef = 999.; ///< max Y in the sector at reference X - float mSectEdgeMargin2 = 0.; ///< crude check if ITS track should be matched also in neighbouring sector + float mSectEdgeMargin = 0.; ///< crude check if ITS track should be matched also in neighbouring sector ///< safety margin in TPC time bins when estimating TPC track tMin and tMax from ///< assigned time0 and its track Z position (converted from mTPCTimeEdgeZSafeMargin) float mTPCTimeEdgeTSafeMargin = 0.f; float mTPCExtConstrainedNSigmaInv = 0.f; // inverse for NSigmas for TPC time-interval from external constraint time sigma - int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) - float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus - float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF - float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse - int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS - float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS - float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds - float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds - float mTPCDriftTimeOffset = 0; ///< drift time offset in mus - float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds - float mTPCTBinNS = 0.; ///< TPC time bin duration in ns - float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds - float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin - float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z - float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift - float mTPCZMax = 0.; ///< max drift length - float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 + int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) + float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus + float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF + float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse + int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS + float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds + float mTPCDriftTimeOffset = 0; ///< drift time offset in mus + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mTPCTBinNS = 0.; ///< TPC time bin duration in ns + float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds + float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin + float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z + float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift + float mTPCZMax = 0.; ///< max drift length + float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 float mMinTPCTrackPtInv = 999.; ///< cutoff on TPC track inverse pT float mMinITSTrackPtInv = 999.; ///< cutoff on ITS track inverse pT - bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data + bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data o2::tpc::VDriftCorrFact mTPCDrift{}; o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; @@ -576,58 +649,70 @@ class MatchTPCITS std::array mClosestBunchAbove; // closest filled bunch from above std::array mClosestBunchBelow; // closest filled bunch from below + std::array mCovDiagInner{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + std::array mCovDiagOuter{}; ///< total cov.matrix extra diagonal error from TrackTuneParams + const o2::itsmft::ChipMappingITS ITSChMap{}; const o2::globaltracking::RecoContainer* mRecoCont = nullptr; ///>>>------ these are input arrays which should not be modified by the matching code // since this info is provided by external device - gsl::span mTPCTracksArray; ///< input TPC tracks span - gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span - gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span - gsl::span mITSTracksArray; ///< input ITS tracks span - gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span - std::vector mITSClustersArray; ///< ITS clusters created in loadInput + gsl::span mTPCTracksArray; ///< input TPC tracks span + gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span + gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span + gsl::span mITSTracksArray; ///< input ITS tracks span + gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span + std::vector mITSClustersArray; ///< ITS clusters created in loadInput + std::vector mITSClusterSizes; ///< ITS cluster sizes created in loadInput + gsl::span mITSClusterROFRec; ///< input ITS clusters ROFRecord span gsl::span mFITInfo; ///< optional input FIT info span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; // cluster patterns dictionary +#ifdef ENABLE_UPGRADES + const o2::its3::TopologyDictionary* mIT3Dict{nullptr}; // cluster patterns dictionary +#endif const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - const MCLabContCl* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels - MCLabSpan mITSTrkLabels; ///< input ITS Track MC labels - MCLabSpan mTPCTrkLabels; ///< input TPC Track MC labels + const o2::dataformats::MCTruthContainer* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels + gsl::span mITSTrkLabels; ///< input ITS Track MC labels + gsl::span mTPCTrkLabels; ///< input TPC Track MC labels /// <<<----- + size_t mNMatches = 0; + size_t mNCalibPrelim = 0; size_t mNMatchesControl = 0; - std::vector mInteractions; ///< possible interaction times - std::vector mInteractionMUSLUT; ///< LUT for interactions in 1MUS bins + + size_t mNABRefsClus = 0; + float mAB2MatchGuess = 0.2; // heuristic guess about fraction of AB matches in total matches + std::vector mInteractions; ///< possible interaction times + std::vector mInteractionMUSLUT; ///< LUT for interactions in 1MUS bins ///< container for record the match of TPC track to single ITS track - std::vector mMatchRecordsTPC; + std::vector mMatchRecordsTPC; // RSS DEQ ///< container for reference to MatchRecord involving particular ITS track - std::vector mMatchRecordsITS; + std::vector mMatchRecordsITS; // RSS DEQ //// std::vector mITSROFofTPCBin; ///< aux structure for mapping of TPC time-bins on ITS ROFs - std::vector mITSROFTimes; ///< min/max times of ITS ROFs in \mus - std::vector mTPCWork; ///< TPC track params prepared for matching - std::vector mITSWork; ///< ITS track params prepared for matching - MCLabContTr mTPCLblWork; ///< TPC track labels - MCLabContTr mITSLblWork; ///< ITS track labels - std::vector mWinnerChi2Refit; ///< vector of refitChi2 for winners + std::vector mITSROFTimes; ///< min/max times of ITS ROFs in \mus + std::vector mTPCWork; ///< TPC track params prepared for matching + std::vector mITSWork; ///< ITS track params prepared for matching + std::vector mTPCLblWork; ///< TPC track labels + std::vector mITSLblWork; ///< ITS track labels + std::vector mWinnerChi2Refit; ///< vector of refitChi2 for winners + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength // ------------------------------ std::vector mTPCABSeeds; ///< pool of primary TPC seeds for AB ///< indices of selected track entries in mTPCWork (for tracks selected by AfterBurner) std::vector mTPCABIndexCache; std::vector mABWinnersIDs; - std::vector mABTrackletClusterIDs; ///< IDs of ITS clusters for AfterBurner winners - std::vector mABTrackletRefs; ///< references on AfterBurner winners clusters - std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks - MCLabContTr mABTrackletLabels; - // ------------------------------ + std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks + LinksPoolMT mABLinksPool; ///< per sector indices of TPC track entry in mTPCWork std::array, o2::constants::math::NSectors> mTPCSectIndexCache; @@ -642,13 +727,6 @@ class MatchTPCITS /// mapping for tracks' continuos ROF cycle to actual continuous readout ROFs with eventual gaps std::vector mITSTrackROFContMapping; - ///< outputs tracks container - std::vector mMatchedTracks; - MCLabContTr mOutLabels; ///< Labels: = TPC labels with flag isFake set in case of fake matching - - ///< container for for vdrift calibration - std::vector mTglITSTPC; - o2::its::RecoGeomHelper mRGHelper; ///< helper for cluster and geometry access #ifdef _ALLOW_DEBUG_TREES_ @@ -663,6 +741,7 @@ class MatchTPCITS static constexpr float MaxSnp = 0.9; // max snp of ITS or TPC track at xRef to be matched static constexpr float MaxTgp = 2.064; // max tg corresponting to MaxSnp = MaxSnp/std::sqrt(1.-MaxSnp^2) static constexpr float MinTBToCleanCache = 600.; // keep in AB ITS cluster refs cache at most this number of TPC bins + static const o2::gpu::GPUTPCGeometry TPCGeometry; enum TimerIDs { SWTot, SWPrepITS, @@ -680,7 +759,6 @@ class MatchTPCITS static constexpr std::string_view TimerName[] = {"Total", "PrepareITS", "PrepareTPC", "DoMatching", "SelectBest", "Refit", "ABSeeds", "ABMatching", "ABWinners", "ABRefit", "IO", "Debug"}; TStopwatch mTimer[NStopWatches]; - }; //______________________________________________ @@ -701,7 +779,6 @@ inline bool MatchTPCITS::isDisabledITS(const TrackLocITS& t) const { return t.ma //______________________________________________ inline bool MatchTPCITS::isDisabledTPC(const TrackLocTPC& t) const { return t.matchID < 0; } - } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h index 7bdd883336859..3ec189deff54b 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITSParams.h @@ -29,18 +29,27 @@ struct MatchTPCITSParams : public o2::conf::ConfigurableParamHelper +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + } // end namespace o2 #endif diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h index 1c277b24ecdca..eaafcca527d7d 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h @@ -52,6 +52,32 @@ using GIndex = o2::dataformats::VtxTrackIndex; class TrackCuts { public: + //////////////////////// Selection setters /////////////////////// + /// ITS + void setMinPtITSCut(float value) { mPtITSCut = value; } + void setEtaITSCut(float value) { mEtaITSCut = value; } + void setMinNClustersITS(int32_t value) { mMinNClustersITS = value; } + void setMaxChi2PerClusterITS(float value) { mMaxChi2PerClusterITS = value; } + void setRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + { + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + LOG(info) << "Track selection, set require hits in ITS layers: " << static_cast(minNRequiredHits); + } + /// TPC + void setMinPtTPCCut(float value) { mPtTPCCut = value; } + void setEtaTPCCut(float value) { mEtaTPCCut = value; } + void setMinNTPCClustersCut(int32_t value) { mNTPCClustersCut = value; } + void setMaxDCATPCCut(float value) { mDCATPCCut = value; } + void setMaxDCATPCCutY(float value) { mDCATPCCutY = value; } + /// ITS+TPC kinematics + void setMinPtCut(float value) { mMinPt = value; } + void setMaxPtCut(float value) { mMaxPt = value; } + void setEtaCut(float valueMin, float valueMax) + { + mMinEta = valueMin; + mMaxEta = valueMax; + } + //////////////////////////////// O2 //////////////////////////////// bool isSelected(GID trackIndex, o2::globaltracking::RecoContainer& data) { @@ -60,13 +86,14 @@ class TrackCuts auto src = trackIndex.getSource(); // make selections depending on track source // ITS tracks if (contributorsGID[GIndex::Source::ITS].isIndexSet()) { // ITS tracks selection - isBarrelTrack = true; const auto& itsTrk = data.getITSTrack(contributorsGID[GID::ITS]); int ITSnClusters = itsTrk.getNClusters(); float ITSchi2 = itsTrk.getChi2(); float itsChi2NCl = ITSnClusters != 0 ? ITSchi2 / (float)ITSnClusters : 0; uint8_t itsClusterMap = itsTrk.getPattern(); - if (ITSnClusters <= mMinNClustersITS || + if (itsTrk.getPt() <= mPtITSCut || + std::abs(itsTrk.getEta()) > mEtaITSCut || // TODO: define 2 different values for min and max (**) + ITSnClusters <= mMinNClustersITS || itsChi2NCl >= mMaxChi2PerClusterITS || TrackMethods::FulfillsITSHitRequirements(itsClusterMap, mRequiredITSHits) == false) { return false; @@ -74,21 +101,26 @@ class TrackCuts } // TPC tracks if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { - isBarrelTrack = true; const auto& tpcTrk = data.getTPCTrack(contributorsGID[GID::TPC]); - math_utils::Point3D v{}; + math_utils::Point3D v{}; // vertex not defined?! std::array dca; - if (tpcTrk.getPt() < mPtTPCCut || - std::abs(tpcTrk.getEta()) > mEtaTPCCut || - tpcTrk.getNClusters() < mNTPCClustersCut || - (!(const_cast(tpcTrk).propagateParamToDCA(v, mBz, &dca, mDCACut)) || - std::abs(dca[0]) > mDCACutY)) { + if (tpcTrk.getPt() < mPtTPCCut || std::abs(tpcTrk.getEta()) > mEtaTPCCut || tpcTrk.getNClusters() < mNTPCClustersCut) { // TODO: define 2 different values for min and max (***) + return false; + } + o2::track::TrackPar trTmp(tpcTrk); + if (!trTmp.propagateParamToDCA(v, mBz, &dca, mDCATPCCut) || std::abs(dca[0]) > mDCATPCCutY || std::hypot(dca[0], dca[1]) > mDCATPCCut) { return false; } } - if (isBarrelTrack) { // track selection for barrel tracks + // ITS-TPC matched cuts + // --> currently inactive in MatchITSTPCQC, since either GID::TPC or GID::ITS + if (src == o2::dataformats::GlobalTrackID::ITSTPC || + src == o2::dataformats::GlobalTrackID::ITSTPCTRD || + src == o2::dataformats::GlobalTrackID::ITSTPCTOF || + src == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { // track selection for barrel tracks (ITS-TPC matched) trk = data.getTrackParam(trackIndex); - if (trk.getPt() < mMinPt && trk.getPt() > mMaxPt && trk.getEta() < mMinEta && trk.getEta() > mMaxEta) { + float trkEta = trk.getEta(); + if (trk.getPt() < mMinPt || trk.getPt() > mMaxPt || trkEta < mMinEta || trkEta > mMaxEta) { return false; } } @@ -96,26 +128,26 @@ class TrackCuts } private: - bool isBarrelTrack = false; // all barrel tracks must have either ITS or TPC contribution -> true if ITS || TPC track source condition is passed - // cut values - float mPtTPCCut = 0.1f; - float mEtaTPCCut = 1.4f; - int32_t mNTPCClustersCut = 60; - float mDCACut = 100.f; - float mDCACutY = 10.f; - // kinematic cuts - float mMinPt{0.f}, - mMaxPt{1e10f}; // range in pT + ////////////////////// cut values ////////////////////////////// + /// ITS track + float mPtITSCut = 0.f; // min pT for ITS track + float mEtaITSCut = 1e10f; // eta window for ITS track --> TODO: define 2 different values for min and max (**) + int mMinNClustersITS{0}; // min number of ITS clusters + float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster + std::vector>> mRequiredITSHits{}; // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + /// TPC track + float mPtTPCCut = 0.1f; // min pT for TPC track + float mEtaTPCCut = 1.4f; // eta window for TPC track --> TODO: define 2 different values for min and max (***) + int32_t mNTPCClustersCut = 60; // minimum number of TPC clusters for TPC track + float mDCATPCCut = 100.f; // max DCA 3D to PV for TPC track + float mDCATPCCutY = 10.f; // max DCA xy to PV for TPC track + // ITS+TPC track kinematics + float mMinPt{0.f}, mMaxPt{1e10f}; // range in pT float mMinEta{-1e10f}, mMaxEta{1e10f}; // range in eta - float mBz = o2::base::Propagator::Instance()->getNominalBz(); - float mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster - int mMinNClustersITS{0}; // min number of ITS clusters - - // vector of ITS requirements (minNRequiredHits in specific requiredLayers) - std::vector>> mRequiredITSHits{}; + float mBz = o2::base::Propagator::Instance()->getNominalBz(); - ClassDefNV(TrackCuts, 1); + ClassDefNV(TrackCuts, 2); }; } // namespace o2 diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h index 2d67a9595008e..f65d9ffd260e6 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h @@ -33,9 +33,9 @@ class TrackMethods const o2::tpc::ClusterNativeAccess& tpcClusAcc, uint8_t& shared, uint8_t& found, uint8_t& crossed) { - LOGP(info, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); - LOGP(info, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); - LOGP(info, " tpcClusAcc{}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); + LOGP(debug, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); + LOGP(debug, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); + LOGP(debug, "tpcClusAcc {}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); constexpr int maxRows = 152; constexpr int neighbour = 2; std::array clMap{}, shMap{}; @@ -47,7 +47,7 @@ class TrackMethods o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; clMap[rowIndex] = true; - if (tpcClusShMap[absoluteIndex] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { if (!shMap[rowIndex]) { shared++; } @@ -93,4 +93,4 @@ class TrackMethods }; } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h index 1b76ad0456ac7..8125147add297 100644 --- a/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h +++ b/Detectors/GlobalTracking/src/GlobalTrackingLinkDef.h @@ -16,6 +16,7 @@ #pragma link off all functions; #pragma link C++ class o2::globaltracking::MatchTOF + ; +#pragma link C++ class o2::globaltracking::MatchHMP + ; #pragma link C++ class o2::globaltracking::TrackLocTPC + ; #pragma link C++ class o2::globaltracking::TrackLocITS + ; #pragma link C++ class o2::globaltracking::MatchTPCITSParams + ; @@ -28,8 +29,6 @@ #pragma link C++ class o2::globaltracking::MatchTOFParams + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::MatchTOFParams> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::ITSTPCMatchingQCParams> + ; - #pragma link C++ class o2::globaltracking::GloFwdAssessment + ; #pragma link C++ class o2::globaltracking::GlobalFwdMatchingParam + ; @@ -44,4 +43,6 @@ #pragma link C++ class std::vector < std::tuple < float, float, float>> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::globaltracking::ITSTPCMatchingQCParams> + ; + #endif diff --git a/Detectors/GlobalTracking/src/MatchCosmics.cxx b/Detectors/GlobalTracking/src/MatchCosmics.cxx index 697f9ed0175b6..90964fb1c05fa 100644 --- a/Detectors/GlobalTracking/src/MatchCosmics.cxx +++ b/Detectors/GlobalTracking/src/MatchCosmics.cxx @@ -89,12 +89,14 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) auto tpcTBinMUSInv = 1. / mTPCTBinMUS; const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); const auto& tpcClusShMap = data.clusterShMapTPC; + const auto& tpcClusOccMap = data.occupancyMapTPC; std::unique_ptr tpcRefitter; if (data.inputsTPCclusters) { tpcRefitter = std::make_unique(&data.inputsTPCclusters->clusterIndex, mTPCCorrMapsHelper, mBz, - tpcClusRefs.data(), tpcClusShMap.data(), - nullptr, o2::base::Propagator::Instance()); + tpcClusRefs.data(), 0, tpcClusShMap.data(), + tpcClusOccMap.data(), tpcClusOccMap.size(), nullptr, o2::base::Propagator::Instance()); + tpcRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } const auto& itsClusters = prepareITSClusters(data); @@ -170,7 +172,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) if (!mFieldON) { trCosm.setQ2Pt(-o2::track::kMostProbablePt); } - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2, false, false); // inward refit, reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2, false, false); // inward refit, reset if (retVal < 0) { // refit failed LOG(debug) << "Inward refit of btm TPC track failed."; continue; @@ -228,7 +230,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) } } const auto& tpcTrOrig = data.getTPCTrack(gidxListTop[GTrackID::TPC]); - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2, true, false); // outward refit, no reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosm, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2, true, false); // outward refit, no reset if (retVal < 0) { // refit failed LOG(debug) << "Outward refit of top TPC track failed."; continue; @@ -242,7 +244,7 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) auto trCosmTop = outerLegs[top]; if (gidxListTop[GTrackID::TPC].isIndexSet()) { // inward refit in TPC const auto& tpcTrOrig = data.getTPCTrack(gidxListTop[GTrackID::TPC]); - int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosmTop, tpcTrOrig.getClusterRef(), (t0 + mTPCDriftTimeOffset) * tpcTBinMUSInv, &chi2Dummy, false, true); // inward refit, reset + int retVal = tpcRefitter->RefitTrackAsTrackParCov(trCosmTop, tpcTrOrig.getClusterRef(), t0 * tpcTBinMUSInv, &chi2Dummy, false, true); // inward refit, reset if (retVal < 0) { // refit failed LOG(debug) << "Outward refit of top TPC track failed."; continue; @@ -498,9 +500,11 @@ void MatchCosmics::createSeeds(const o2::globaltracking::RecoContainer& data) return true; } if constexpr (isTPCTrack()) { + if (!this->mMatchParams->allowTPCOnly) { + return true; + } // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins t0 *= this->mTPCTBinMUS; - t0 -= this->mTPCDriftTimeOffset; terr *= this->mTPCTBinMUS; } else if (isITSTrack()) { t0 += 0.5 * this->mITSROFrameLengthMUS; // time 0 is supplied as beginning of ROF in \mus diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx index a7bf2fcbf1001..7ed50ce7400ae 100644 --- a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx +++ b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "GlobalTracking/MatchGlobalFwd.h" +#include using namespace o2::globaltracking; @@ -64,8 +65,13 @@ void MatchGlobalFwd::init() mUseMIDMCHMatch = matchingParam.useMIDMatch; LOG(info) << "UseMIDMCH Matching = " << (mUseMIDMCHMatch ? "true" : "false"); + mUseTrackTime = matchingParam.useTrackTime; + LOG(info) << "Use track time = " << (mUseTrackTime ? "true" : "false"); + mSaveMode = matchingParam.saveMode; LOG(info) << "Save mode MFTMCH candidates = " << mSaveMode; + + mNCandidates = matchingParam.nCandidates; } //_________________________________________________________ @@ -98,6 +104,9 @@ void MatchGlobalFwd::run(const o2::globaltracking::RecoContainer& inp) case kSaveTrainingData: doMatching(); break; + case kSaveNCandidates: + doMatching(); + break; default: LOG(fatal) << "Invalid MFTMCH save mode"; } @@ -132,6 +141,7 @@ void MatchGlobalFwd::clear() mMatchLabels.clear(); mMFTTrackROFContMapping.clear(); mMatchingInfo.clear(); + mCandidates.clear(); } //_________________________________________________________ @@ -209,8 +219,8 @@ bool MatchGlobalFwd::processMCHMIDMatches() LOG(debug) << " MCHId: " << MCHId << " --> mMCHID2Work[MCHId]:" << mMCHID2Work[MCHId]; const auto& IR = MIDMatch.getIR(); int nBC = IR.differenceInBC(mStartIR); - float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS; - float tMax = (nBC + 1) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMin = (nBC - 1) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMax = (nBC + 2) * o2::constants::lhc::LHCBunchSpacingMUS; thisMuonTrack.setMIDTrackID(MIDId); thisMuonTrack.setTimeMUS(MIDMatch.getTimeMUS(mStartIR).first); thisMuonTrack.tBracket.set(tMin, tMax); @@ -262,10 +272,10 @@ bool MatchGlobalFwd::prepareMFTData() } return false; } - float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS; - float tMax = (nBC + mMFTROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMin = (nBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMax = (nBC + mMFTROFrameLengthInBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; if (!mMFTTriggered) { - auto irofCont = nBC / mMFTROFrameLengthInBC; + auto irofCont = (nBC + mMFTROFrameBiasInBC) / mMFTROFrameLengthInBC; if (mMFTTrackROFContMapping.size() <= irofCont) { // there might be gaps in the non-empty rofs, this will map continuous ROFs index to non empty ones mMFTTrackROFContMapping.resize((1 + irofCont / 128) * 128, 0); } @@ -285,6 +295,9 @@ bool MatchGlobalFwd::prepareMFTData() trc.setZ(trcOrig.getOutParam().getZ()); trc.setCovariances(trcOrig.getOutParam().getCovariances()); trc.setTrackChi2(trcOrig.getOutParam().getTrackChi2()); + // Extrapolate MFT track parameters and covariances matrix to "mMatchingPlaneZ" + // Parameters: helix track model; Error propagation: Quadratic + // If "mBz" is zero: linear track model trc.propagateToZ(mMatchingPlaneZ, mBz); } } @@ -397,6 +410,25 @@ void MatchGlobalFwd::doMatching() if (mMCTruthON) { LOG(info) << " MFT-MCH Matching: nFakes = " << nFakes << " nTrue = " << nTrue; } + } else if constexpr (saveAllMode == SaveMode::kSaveNCandidates) { + int nFakes = 0, nTrue = 0; + auto& matchAllChi2 = mMatchingFunctionMap["matchALL"]; + for (auto MCHId = 0; MCHId < mMCHWork.size(); MCHId++) { + auto& thisMCHTrack = mMCHWork[MCHId]; + for (auto& pairCandidate : mCandidates[MCHId]) { + thisMCHTrack.setMFTTrackID(pairCandidate.second); + auto& thisMFTTrack = mMFTWork[pairCandidate.second]; + auto chi2 = matchAllChi2(thisMCHTrack, thisMFTTrack); // Matching chi2 is stored independently + thisMCHTrack.setMFTMCHMatchingScore(pairCandidate.first); + thisMCHTrack.setMFTMCHMatchingChi2(chi2); + mMatchedTracks.emplace_back(thisMCHTrack); + mMatchingInfo.emplace_back(thisMCHTrack); + if (mMCTruthON) { + mMatchLabels.push_back(computeLabel(MCHId, pairCandidate.second)); + mMatchLabels.back().isFake() ? nFakes++ : nTrue++; + } + } + } } } @@ -406,10 +438,15 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) { /// Matches MFT tracks on a given ROF with MCH tracks in a range of ROFs const auto& thisMFTROF = mMFTTrackROFRec[MFTROFId]; + const auto& thisMFTBracket = mMFTROFTimes[MFTROFId]; const auto& firstMCHROF = mMCHTrackROFRec[firstMCHROFId]; const auto& lastMCHROF = mMCHTrackROFRec[lastMCHROFId]; int nFakes = 0, nTrue = 0; + auto compare = [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }; + auto firstMFTTrackID = thisMFTROF.getFirstEntry(); auto lastMFTTrackID = firstMFTTrackID + thisMFTROF.getNEntries() - 1; @@ -431,6 +468,12 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) // loop over all MCH tracks for (auto MCHId = firstMCHTrackID; MCHId <= lastMCHTrackID; MCHId++) { auto& thisMCHTrack = mMCHWork[MCHId]; + + // If enabled, use the muon track time to check if the track is correlated with the MFT ROF + if (mUseTrackTime && (thisMFTBracket.isOutside(thisMCHTrack.tBracket))) { + continue; + } + o2::MCCompLabel matchLabel; for (auto MFTId = firstMFTTrackID; MFTId <= lastMFTTrackID; MFTId++) { auto& thisMFTTrack = mMFTWork[MFTId]; @@ -461,12 +504,21 @@ void MatchGlobalFwd::ROFMatch(int MFTROFId, int firstMCHROFId, int lastMCHROFId) } } + if constexpr (saveAllMode == SaveMode::kSaveNCandidates) { // Save best N matching candidates + auto score = mMatchFunc(thisMCHTrack, thisMFTTrack); + std::pair scoreID = {score, MFTId}; + mCandidates[MCHId].push_back(scoreID); + std::sort(mCandidates[MCHId].begin(), mCandidates[MCHId].end(), compare); + if (mCandidates[MCHId].size() > mNCandidates) { + mCandidates[MCHId].pop_back(); + } + } + if constexpr (saveAllMode == SaveMode::kSaveTrainingData) { // In save training data mode store track parameters at matching plane thisMCHTrack.setMFTTrackID(MFTId); mMatchingInfo.emplace_back(thisMCHTrack); mMCHMatchPlaneParams.emplace_back(thisMCHTrack); mMFTMatchPlaneParams.emplace_back(static_cast(thisMFTTrack)); - if (mMCTruthON) { mMatchLabels.push_back(matchLabel); mMatchLabels.back().isFake() ? nFakes++ : nTrue++; @@ -648,6 +700,14 @@ void MatchGlobalFwd::setMFTROFrameLengthInBC(int nbc) mMFTROFrameLengthMUSInv = 1. / mMFTROFrameLengthMUS; } +//_________________________________________________________ +void MatchGlobalFwd::setMFTROFrameBiasInBC(int nbc) +{ + mMFTROFrameBiasInBC = nbc; + mMFTROFrameBiasMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + mMFTROFrameBiasMUSInv = 1. / mMFTROFrameBiasMUS; +} + //_________________________________________________________ void MatchGlobalFwd::setBunchFilling(const o2::BunchFilling& bf) { @@ -759,6 +819,76 @@ o2::dataformats::GlobalFwdTrack MatchGlobalFwd::MCHtoFwd(const o2::mch::TrackPar return convertedTrack; } +//_________________________________________________________________________________________________ +o2::mch::TrackParam MatchGlobalFwd::FwdtoMCH(const o2::dataformats::GlobalFwdTrack& fwdtrack) +{ + // Convert Forward Track parameters and covariances matrix to the + // MCH track format. + + // Parameter conversion + double alpha1, alpha3, alpha4, x2, x3, x4; + + x2 = fwdtrack.getPhi(); + x3 = fwdtrack.getTanl(); + x4 = fwdtrack.getInvQPt(); + + auto sinx2 = TMath::Sin(x2); + auto cosx2 = TMath::Cos(x2); + + alpha1 = cosx2 / x3; + alpha3 = sinx2 / x3; + alpha4 = x4 / TMath::Sqrt(x3 * x3 + sinx2 * sinx2); + + auto K = TMath::Sqrt(x3 * x3 + sinx2 * sinx2); + auto K3 = K * K * K; + + // Covariances matrix conversion + SMatrix55Std jacobian; + SMatrix55Sym covariances; + + covariances(0, 0) = fwdtrack.getCovariances()(0, 0); + covariances(0, 1) = fwdtrack.getCovariances()(0, 1); + covariances(0, 2) = fwdtrack.getCovariances()(0, 2); + covariances(0, 3) = fwdtrack.getCovariances()(0, 3); + covariances(0, 4) = fwdtrack.getCovariances()(0, 4); + + covariances(1, 1) = fwdtrack.getCovariances()(1, 1); + covariances(1, 2) = fwdtrack.getCovariances()(1, 2); + covariances(1, 3) = fwdtrack.getCovariances()(1, 3); + covariances(1, 4) = fwdtrack.getCovariances()(1, 4); + + covariances(2, 2) = fwdtrack.getCovariances()(2, 2); + covariances(2, 3) = fwdtrack.getCovariances()(2, 3); + covariances(2, 4) = fwdtrack.getCovariances()(2, 4); + + covariances(3, 3) = fwdtrack.getCovariances()(3, 3); + covariances(3, 4) = fwdtrack.getCovariances()(3, 4); + + covariances(4, 4) = fwdtrack.getCovariances()(4, 4); + + jacobian(0, 0) = 1; + + jacobian(1, 2) = -sinx2 / x3; + jacobian(1, 3) = -cosx2 / (x3 * x3); + + jacobian(2, 1) = 1; + + jacobian(3, 2) = cosx2 / x3; + jacobian(3, 3) = -sinx2 / (x3 * x3); + + jacobian(4, 2) = -x4 * sinx2 * cosx2 / K3; + jacobian(4, 3) = -x3 * x4 / K3; + jacobian(4, 4) = 1 / K; + // jacobian*covariances*jacobian^T + covariances = ROOT::Math::Similarity(jacobian, covariances); + + double cov[] = {covariances(0, 0), covariances(1, 0), covariances(1, 1), covariances(2, 0), covariances(2, 1), covariances(2, 2), covariances(3, 0), covariances(3, 1), covariances(3, 2), covariances(3, 3), covariances(4, 0), covariances(4, 1), covariances(4, 2), covariances(4, 3), covariances(4, 4)}; + double param[] = {fwdtrack.getX(), alpha1, fwdtrack.getY(), alpha3, alpha4}; + + o2::mch::TrackParam convertedTrack(fwdtrack.getZ(), param, cov); + return o2::mch::TrackParam(convertedTrack); +} + //_________________________________________________________________________________________________ MatchGlobalFwd::MatchGlobalFwd() { diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx index 443a6ad6b5407..fc5fce57b9a9f 100644 --- a/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx +++ b/Detectors/GlobalTracking/src/MatchGlobalFwdAssessment.cxx @@ -46,7 +46,7 @@ void GloFwdAssessment::reset() mTrackEtaPhiNCls[nHisto]->Reset(); } - mTrackTanl->Reset(); + mTrackCotl->Reset(); if (mUseMC) { mPairables.clear(); @@ -86,9 +86,9 @@ void GloFwdAssessment::createHistos() mTrackNumberOfClusters = std::make_unique("mGlobalFwdNumberOfMFTClusters", "Number Of Clusters Per Track; # clusters; # entries", 10, 0.5, 10.5); - mTrackInvQPt = std::make_unique("mGlobalFwdInvQPt", "Track q/p_{T}; q/p_{T} [1/GeV]; # entries", 50, -2, 2); + mTrackInvQPt = std::make_unique("mGlobalFwdInvQPt", "Track q/p_{T}; q/p_{T} [1/GeV]; # entries", 100, -5, 5); - mTrackChi2 = std::make_unique("mGlobalFwdChi2", "Track #chi^{2}; #chi^{2}; # entries", 21, -0.5, 20.5); + mTrackChi2 = std::make_unique("mGlobalFwdChi2", "Track #chi^{2}; #chi^{2}; # entries", 202, -0.5, 100.5); mTrackCharge = std::make_unique("mGlobalFwdCharge", "Track Charge; q; # entries", 3, -1.5, 1.5); @@ -109,7 +109,7 @@ void GloFwdAssessment::createHistos() mTrackEtaPhiNCls[nHisto]->SetOption("COLZ"); } - mTrackTanl = std::make_unique("mGlobalFwdTanl", "Track tan #lambda; tan #lambda; # entries", 100, -25, 0); + mTrackCotl = std::make_unique("mGlobalFwdCotl", "Track cot #lambda; cot #lambda; # entries", 100, -0.25, 0); // Creating MC-based histos if (mUseMC) { @@ -223,7 +223,7 @@ void GloFwdAssessment::runBasicQC(o2::framework::ProcessingContext& ctx) mTrackCharge->Fill(oneTrack.getCharge()); mTrackPhi->Fill(oneTrack.getPhi()); mTrackEta->Fill(oneTrack.getEta()); - mTrackTanl->Fill(oneTrack.getTanl()); + mTrackCotl->Fill(1. / oneTrack.getTanl()); for (auto minNClusters : sMinNClustersList) { if (nClusters >= minNClusters) { @@ -400,7 +400,7 @@ void GloFwdAssessment::processTrueTracks() const auto y_res = fwdTrack.getY() - vyGen; const auto eta_res = fwdTrack.getEta() - etaGen; const auto phi_res = fwdTrack.getPhi() - phiGen; - const auto tanl_res = fwdTrack.getTanl() - tanlGen; + const auto cotl_res = (1. / fwdTrack.getTanl()) - (1. / tanlGen); const auto invQPt_res = invQPt_Rec - invQPtGen; mHistPtVsEta[kRecoTrue]->Fill(eta_Rec, pt_Rec); mHistPhiVsEta[kRecoTrue]->Fill(eta_Rec, phi_Rec); @@ -421,7 +421,7 @@ void GloFwdAssessment::processTrueTracks() mTH3Histos[kTH3GMTrackXPullPtEta]->Fill(ptGen, etaGen, x_res / sqrt(fwdTrack.getCovariances()(0, 0))); mTH3Histos[kTH3GMTrackYPullPtEta]->Fill(ptGen, etaGen, y_res / sqrt(fwdTrack.getCovariances()(1, 1))); mTH3Histos[kTH3GMTrackPhiPullPtEta]->Fill(ptGen, etaGen, phi_res / sqrt(fwdTrack.getCovariances()(2, 2))); - mTH3Histos[kTH3GMTrackTanlPullPtEta]->Fill(ptGen, etaGen, tanl_res / sqrt(fwdTrack.getCovariances()(3, 3))); + mTH3Histos[kTH3GMTrackCotlPullPtEta]->Fill(ptGen, etaGen, cotl_res / sqrt(1. / fwdTrack.getCovariances()(3, 3))); mTH3Histos[kTH3GMTrackInvQPtPullPtEta]->Fill(ptGen, etaGen, invQPt_res / sqrt(fwdTrack.getCovariances()(4, 4))); mTH3Histos[kTH3GMTrackInvQPtResolutionPtEta]->Fill(ptGen, etaGen, (invQPt_Rec - invQPtGen) / invQPtGen); mTH3Histos[kTH3GMTrackInvQPtResMCHPtEta]->Fill(ptGen, etaGen, (invQPt_MCH - invQPtGen) / invQPtGen); @@ -477,7 +477,7 @@ void GloFwdAssessment::getHistos(TObjArray& objar) objar.Add(mTrackXYNCls[nHisto].get()); objar.Add(mTrackEtaPhiNCls[nHisto].get()); } - objar.Add(mTrackTanl.get()); + objar.Add(mTrackCotl.get()); if (mUseMC) { objar.Add(mHistPhiRecVsPhiGen.get()); @@ -552,7 +552,7 @@ void GloFwdAssessment::loadHistos() mTrackEtaPhiNCls[nHisto] = std::unique_ptr((TH2F*)f->Get(Form("mGlobalFwdEtaPhi_%d_MinClusters", minNClusters))); } - mTrackTanl = std::unique_ptr((TH1F*)f->Get("mGlobalFwdTanl")); + mTrackCotl = std::unique_ptr((TH1F*)f->Get("mGlobalFwdCotl")); // Creating MC-based histos if (mUseMC) { diff --git a/Detectors/GlobalTracking/src/MatchHMP.cxx b/Detectors/GlobalTracking/src/MatchHMP.cxx new file mode 100644 index 0000000000000..207bbdb9bfbf6 --- /dev/null +++ b/Detectors/GlobalTracking/src/MatchHMP.cxx @@ -0,0 +1,625 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include +#include + +#include "FairLogger.h" +#include "Field/MagneticField.h" +#include "Field/MagFieldFast.h" +#include "TOFBase/Geo.h" + +#include "SimulationDataFormat/MCTruthContainer.h" + +#include "DetectorsBase/Propagator.h" + +#include "MathUtils/Cartesian.h" +#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" +#include "CommonConstants/PhysicsConstants.h" +#include "CommonConstants/GeomConstants.h" +#include "DetectorsBase/GeometryManager.h" + +#include +#include +#include +#include +#include "DataFormatsParameters/GRPObject.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/TrackLTIntegral.h" +#include "ReconstructionDataFormats/TrackHMP.h" + +#include "GlobalTracking/MatchHMP.h" + +#include "TPCBase/ParameterGas.h" +#include "TPCBase/ParameterElectronics.h" +#include "TPCReconstruction/TPCFastTransformHelperO2.h" + +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "HMPIDBase/Param.h" +#include "HMPIDReconstruction/Recon.h" + +#include "CommonDataFormat/InteractionRecord.h" + +using namespace o2::globaltracking; +using evGIdx = o2::dataformats::EvIndex; +using Cluster = o2::hmpid::Cluster; +using Recon = o2::hmpid::Recon; +using MatchInfo = o2::dataformats::MatchInfoHMP; +using Trigger = o2::hmpid::Trigger; +using GTrackID = o2::dataformats::GlobalTrackID; +using TrackHMP = o2::dataformats::TrackHMP; +using timeEst = o2::dataformats::TimeStampWithError; + +ClassImp(MatchHMP); +//================================================================================================================================================== +void MatchHMP::run(const o2::globaltracking::RecoContainer& inp) +{ + ///< running the matching + + mRecoCont = &inp; + mStartIR = inp.startIR; + + for (int i = 0; i < o2::globaltracking::MatchHMP::trackType::SIZE; i++) { + mTrackGid[i].clear(); + mMatchedTracks[i].clear(); + mOutHMPLabels[i].clear(); + mTracksWork[i].clear(); + mMatchedTracksIndex[i].clear(); + } + + for (int it = 0; it < o2::globaltracking::MatchHMP::trackType::SIZE; it++) { + mTracksIndexCache[it].clear(); + if (mMCTruthON) { + mTracksLblWork[it].clear(); + } + } + + mHMPTriggersIndexCache.clear(); + + bool isPrepareHMPClusters = prepareHMPClusters(); + + if (!isPrepareHMPClusters) { // check cluster before of tracks to see also if MC is required + return; + } + + // mExtraTPCFwdTime.clear(); + + mTimerTot.Start(); + if (!prepareTracks()) { + return; + } + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused || mIsITSTPCTOFused || mIsTPCTOFused || mIsITSTPCTRDTOFused || mIsTPCTRDTOFused) { + doMatching(); + } + + mIsTPCused = false; + mIsITSTPCused = false; + mIsTPCTRDused = false; + mIsITSTPCTRDused = false; + mIsTPCTOFused = false; + mIsITSTPCTRDTOFused = false; + mIsTPCTRDTOFused = false; +} +//================================================================================================================================================== +bool MatchHMP::prepareTracks() +{ + + auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) { + const int nclustersMin = 0; + if constexpr (isTPCTrack()) { + if (trk.getNClusters() < nclustersMin) { + return true; + } + if (std::abs(trk.getQ2Pt()) > mMaxInvPt) { + return true; + } + this->addTPCSeed(trk, gid, time0, terr); + } + if constexpr (isTPCITSTrack()) { + if (trk.getParamOut().getX() < o2::constants::geom::XTPCOuterRef - 1.) { + return true; + } + this->addITSTPCSeed(trk, gid, time0, terr); + } + if constexpr (isTRDTrack()) { + this->addTRDSeed(trk, gid, time0, terr); + } + if constexpr (isTPCTOFTrack()) { + this->addTPCTOFSeed(trk, gid, time0, terr); + } + return true; + }; + + mRecoCont->createTracksVariadic(creator); + + for (int it = 0; it < o2::globaltracking::MatchHMP::trackType::SIZE; it++) { + mMatchedTracksIndex[it].resize(mTracksWork[it].size()); + std::fill(mMatchedTracksIndex[it].begin(), mMatchedTracksIndex[it].end(), -1); // initializing all to -1 + } + + // Unconstrained tracks + /* + if (mIsTPCused) { + + // sort tracks in each sector according to their time (increasing in time) + // for (int sec = o2::constants::math::NSectors; sec--;) { + auto& indexCache = mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::UNCONS]; + LOG(debug) << indexCache.size() << " tracks"; + if (!indexCache.size()) { + return false; + } + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& trcA = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS][a].second; + auto& trcB = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS][b].second; + return ((trcA.getTimeStamp() - trcA.getTimeStampError()) - (trcB.getTimeStamp() - trcB.getTimeStampError()) < 0.); + }); + // } // loop over tracks of single sector + } // unconstrained tracks + */ + // Constrained tracks + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused || mIsITSTPCTOFused || mIsTPCTOFused || mIsITSTPCTRDTOFused || mIsTPCTRDTOFused) { + + auto& indexCache = mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::CONSTR]; + LOG(debug) << indexCache.size() << " tracks"; + if (!indexCache.size()) { + return false; + } + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& trcA = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR][a].second; + auto& trcB = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR][b].second; + return ((trcA.getTimeStamp() - mSigmaTimeCut * trcA.getTimeStampError()) - (trcB.getTimeStamp() - mSigmaTimeCut * trcB.getTimeStampError()) < 0.); + }); + // } // loop over tracks of single sector + } // constrained tracks + + return true; +} +//______________________________________________ +void MatchHMP::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + mIsITSTPCused = true; + + auto trc = _tr.getParamOut(); + o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); + + timeEst ts(time0, terr); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRD) { + mIsTPCTRDused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRD) { + mIsITSTPCTRDused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else { // shouldn't happen + LOG(error) << "MatchHMP::addTRDSee: srcGID.getSource() = " << int(srcGID.getSource()) << " not allowed; expected ones are: " << int(o2::dataformats::GlobalTrackID::TPCTRD) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRD) << " and " << int(o2::dataformats::GlobalTrackID::TPCTRDTOF) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRDTOF); + } + + auto trc = _tr.getOuterParam(); + + o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); + + // o2::dataformats::TimeStampWithError + timeEst ts(time0, terr + mExtraTimeToleranceTRD); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addTPCTOFSeed(const o2::dataformats::TrackTPCTOF& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTOF) { + mIsTPCTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::TPCTRDTOF) { + mIsTPCTRDTOFused = true; + } else if (srcGID.getSource() == o2::dataformats::GlobalTrackID::ITSTPCTRDTOF) { + mIsITSTPCTRDTOFused = true; + } else { // shouldn't happen + LOG(error) << "MatchHMP::addTPCTOFCSeed: srcGID.getSource() = " << int(srcGID.getSource()) << " not allowed; expected ones are: " << int(o2::dataformats::GlobalTrackID::TPCTOF) << " and " << int(o2::dataformats::GlobalTrackID::TPCTRDTOF) << " and " << int(o2::dataformats::GlobalTrackID::ITSTPCTRDTOF); + } + + auto trc = _tr.getParamOut(); + + timeEst ts(time0, terr + mExtraTimeToleranceTOF); + + addConstrainedSeed(trc, srcGID, ts); +} +//______________________________________________ +void MatchHMP::addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, timeEst timeMUS) +{ + std::array globalPos; + + // current track index + int it = mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR].size(); + + auto prop = o2::base::Propagator::Instance(); + float bxyz[3]; + prop->getFieldXYZ(trc.getXYZGlo(), bxyz); + double bz = -bxyz[2]; + + float pCut = 0.; + + if (TMath::Abs(bz - 5.0) < 0.5) { + pCut = 0.450; + } + + if (TMath::Abs(bz - 2.0) < 0.5) { + pCut = 0.200; + } + + if (trc.getP() > pCut && TMath::Abs(trc.getTgl()) < 0.544 && TMath::Abs(trc.getPhi() - TMath::Pi()) > (TMath::Pi() * 0.5)) { + // create working copy of track param + mTracksWork[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); + + mTrackGid[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(srcGID); + + if (mMCTruthON) { + mTracksLblWork[o2::globaltracking::MatchHMP::trackType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); + } + + mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::CONSTR].push_back(it); + } +} +//______________________________________________ +void MatchHMP::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) +{ + mIsTPCused = true; + + std::array globalPos; + + // current track index + int it = mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS].size(); + + // create working copy of track param + timeEst timeInfo; + // set + float extraErr = 0; + + auto trc = _tr.getOuterParam(); + + float trackTime0 = _tr.getTime0() * mTPCTBinMUS; + + timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); + // mExtraTPCFwdTime.push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); + + timeInfo.setTimeStamp(trackTime0); + + trc.getXYZGlo(globalPos); + + mTracksWork[o2::globaltracking::MatchHMP::trackType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); + + if (mMCTruthON) { + mTracksLblWork[o2::globaltracking::MatchHMP::trackType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); + } + + mTracksIndexCache[o2::globaltracking::MatchHMP::trackType::UNCONS].push_back(it); +} +//================================================================================================================================================== +bool MatchHMP::prepareHMPClusters() +{ + mHMPClustersArray = mRecoCont->getHMPClusters(); + mHMPTriggersArray = mRecoCont->getHMPClusterTriggers(); + + mHMPClusLabels = mRecoCont->getHMPClustersMCLabels(); + mMCTruthON = mHMPClusLabels && mHMPClusLabels->getNElements(); + + mNumOfTriggers = 0; + + mHMPTriggersWork.clear(); + + int nTriggersInCurrentChunk = mHMPTriggersArray.size(); + LOG(debug) << "nTriggersInCurrentChunk = " << nTriggersInCurrentChunk; + mNumOfTriggers += nTriggersInCurrentChunk; + mHMPTriggersWork.reserve(mHMPTriggersWork.size() + mNumOfTriggers); + for (int it = 0; it < nTriggersInCurrentChunk; it++) { + const Trigger& clOrig = mHMPTriggersArray[it]; + // create working copy of track param + mHMPTriggersWork.emplace_back(clOrig); + // cache work track index + mHMPTriggersIndexCache.push_back(mHMPTriggersWork.size() - 1); + } + + // sort hmp events according to their time (increasing in time) + auto& indexCache = mHMPTriggersIndexCache; + LOG(debug) << indexCache.size() << " HMP triggers"; + if (!indexCache.size()) { + return false; + } + + std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { + auto& clA = mHMPTriggersWork[a]; + auto& clB = mHMPTriggersWork[b]; + auto timeA = o2::InteractionRecord::bc2ns(clA.getBc(), clA.getOrbit()); + auto timeB = o2::InteractionRecord::bc2ns(clB.getBc(), clB.getOrbit()); + return (timeA - timeB) < 0.; }); + + return true; +} +//================================================================================================================================================== +void MatchHMP::doMatching() +{ + o2::globaltracking::MatchHMP::trackType type = o2::globaltracking::MatchHMP::trackType::CONSTR; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; // material correction method + Recon* recon = new o2::hmpid::Recon(); + o2::hmpid::Param* pParam = o2::hmpid::Param::instance(); + + const float kdRadiator = 10.; // distance between radiator and the plane + + //< do the real matching + auto& cacheTriggerHMP = mHMPTriggersIndexCache; // array of cached HMP triggers indices; reminder: they are ordered in time! + auto& cacheTrk = mTracksIndexCache[type]; // array of cached tracks indices; + int nTracks = cacheTrk.size(), nHMPtriggers = cacheTriggerHMP.size(); + + LOG(debug) << " ************************number of tracks: " << nTracks << ", number of HMP triggers: " << nHMPtriggers; + if (!nTracks || !nHMPtriggers) { + return; + } + + auto prop = o2::base::Propagator::Instance(); + + float bxyz[3]; + + double cluLORS[2] = {0}; + + LOG(debug) << "Trying to match %d tracks" << cacheTrk.size(); + + float timeFromTF = o2::InteractionRecord::bc2ns(mStartIR.bc, mStartIR.orbit); + + for (int ievt = 0; ievt < cacheTriggerHMP.size(); ievt++) { // events loop + + auto& event = mHMPTriggersWork[cacheTriggerHMP[ievt]]; + auto evtTime = event.getIr().differenceInBCMUS(mStartIR); + + int evtTracks = 0; + + for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { // tracks loop + + auto& trackWork = mTracksWork[type][cacheTrk[itrk]]; + auto& trackGid = mTrackGid[type][cacheTrk[itrk]]; + auto& trefTrk = trackWork.first; + + prop->getFieldXYZ(trefTrk.getXYZGlo(), bxyz); + double bz = -bxyz[2]; + + double timeUncert = trackWork.second.getTimeStampError(); + + float minTrkTime = (trackWork.second.getTimeStamp() - mSigmaTimeCut * timeUncert); + float maxTrkTime = (trackWork.second.getTimeStamp() + mSigmaTimeCut * timeUncert); + + // if (evtTime < (maxTrkTime + timeFromTF) && evtTime > (minTrkTime + timeFromTF)) { + if (evtTime < maxTrkTime && evtTime > minTrkTime) { + + evtTracks++; + + MatchInfo matching(999999, mTrackGid[type][cacheTrk[itrk]]); + + matching.setHMPIDtrk(0, 0, 0, 0); // no intersection found + matching.setHMPIDmip(0, 0, 0, 0); // store mip info in any case + matching.setIdxHMPClus(99, 99999); // chamber not found, mip not yet considered + matching.setHMPsignal(Recon::kNotPerformed); // ring reconstruction not yet performed + matching.setIdxTrack(trackGid); + TrackHMP hmpTrk(trefTrk); // create a hmpid track to be used for propagation and matching + + hmpTrk.set(trefTrk.getX(), trefTrk.getAlpha(), trefTrk.getParams(), trefTrk.getCharge(), trefTrk.getPID()); + + double xPc, yPc, xRa, yRa, theta, phi; + + Int_t iCh = intTrkCha(&trefTrk, xPc, yPc, xRa, yRa, theta, phi, bz); // find the intersected chamber for this track + if (iCh < 0) { + continue; + } // no intersection at all, go next track + + matching.setHMPIDtrk(xPc, yPc, theta, phi); // store initial infos + matching.setIdxHMPClus(iCh, 9999); // set chamber, index of cluster + cluster size + + int index = -1; + + double dmin = 999999; //, distCut = 1.; + + bool isOkDcut = kFALSE; + bool isOkQcut = kFALSE; + bool isMatched = kFALSE; + + Cluster* bestHmpCluster = nullptr; // the best matching cluster + std::vector oneEventClusters; + + for (int j = event.getFirstEntry(); j <= event.getLastEntry(); j++) { // event clusters loop + auto& cluster = (o2::hmpid::Cluster&)mHMPClustersArray[j]; + + if (cluster.ch() != iCh) { + continue; + } + oneEventClusters.push_back(cluster); + double qthre = pParam->qCut(); + + if (cluster.q() < 150. || cluster.size() > 10) { + continue; + } + + isOkQcut = kTRUE; + + cluLORS[0] = cluster.x(); + cluLORS[1] = cluster.y(); // get the LORS coordinates of the cluster + + double dist = 0.; + + if (TMath::Abs((xPc - cluLORS[0]) * (xPc - cluLORS[0]) + (yPc - cluLORS[1]) * (yPc - cluLORS[1])) > 0.0001) { + + dist = TMath::Sqrt((xPc - cluLORS[0]) * (xPc - cluLORS[0]) + (yPc - cluLORS[1]) * (yPc - cluLORS[1])); + } + + if (dist < dmin) { + dmin = dist; + index = oneEventClusters.size() - 1; + bestHmpCluster = &cluster; + } + + } // event clusters loop + + // 2. Propagate track to the MIP cluster using the central method + + if (!bestHmpCluster) { + oneEventClusters.clear(); + continue; + } + + TVector3 vG = pParam->lors2Mars(iCh, bestHmpCluster->x(), bestHmpCluster->y()); + float gx = vG.X(); + float gy = vG.Y(); + float gz = vG.Z(); + float alpha = TMath::ATan2(gy, gx); + float radiusH = TMath::Sqrt(gy * gy + gx * gx); + if (!(hmpTrk.rotate(alpha))) { + continue; + } + if (!prop->PropagateToXBxByBz(hmpTrk, radiusH, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, matCorr)) { + oneEventClusters.clear(); + continue; + } + + // 3. Update the track with MIP cluster (Improved angular and position resolution - to be used for Cherenkov angle calculation) + + o2::track::TrackParCov trackC(hmpTrk); + + std::array trkPos{0, gz}; + std::array trkCov{0.1 * 0.1, 0., 0.1 * 0.1}; + + // auto chi2 = trackC.getPredictedChi2(trkPos, trkCov); + trackC.update(trkPos, trkCov); + + // 4. Propagate back the constrained track to the radiator radius + + TrackHMP hmpTrkConstrained(trackC); + hmpTrkConstrained.set(trackC.getX(), trackC.getAlpha(), trackC.getParams(), trackC.getCharge(), trackC.getPID()); + if (!prop->PropagateToXBxByBz(hmpTrkConstrained, radiusH - kdRadiator, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, matCorr)) { + oneEventClusters.clear(); + continue; + } + + float hmpMom = hmpTrkConstrained.getP() * hmpTrkConstrained.getSign(); + + matching.setHmpMom(hmpMom); + + // 5. Propagation in the last 10 cm with the fast method + + double xPc0 = 0., yPc0 = 0.; + intTrkCha(iCh, &hmpTrkConstrained, xPc0, yPc0, xRa, yRa, theta, phi, bz); + + // 6. Set match information + + int cluSize = bestHmpCluster->size(); + matching.setHMPIDmip(bestHmpCluster->x(), bestHmpCluster->y(), (int)bestHmpCluster->q(), 0); // store mip info in any case + matching.setMipClusSize(bestHmpCluster->size()); + matching.setIdxHMPClus(iCh, index + 1000 * cluSize); // set chamber, index of cluster + cluster size + matching.setHMPIDtrk(xPc, yPc, theta, phi); + + if (!isOkQcut) { + matching.setHMPsignal(pParam->kMipQdcCut); + } + + // dmin recalculated + + dmin = TMath::Sqrt((xPc - bestHmpCluster->x()) * (xPc - bestHmpCluster->x()) + (yPc - bestHmpCluster->y()) * (yPc - bestHmpCluster->y())); + + if (dmin < 6.) { + isOkDcut = kTRUE; + } + // isOkDcut = kTRUE; // switch OFF cut + + if (!isOkDcut) { + matching.setHMPsignal(pParam->kMipDistCut); // closest cluster with enough charge is still too far from intersection + } + + if (isOkQcut * isOkDcut) { + isMatched = kTRUE; + } // MIP-Track matched !! + + if (!isMatched) { + mMatchedTracks[type].push_back(matching); + oneEventClusters.clear(); + continue; + } // If matched continue... + + double nmean = pParam->meanIdxRad(); + + // 7. Calculate the Cherenkov angle + + recon->setImpPC(xPc, yPc); // store track impact to PC + recon->ckovAngle(&matching, oneEventClusters, index, nmean, xRa, yRa); // search for Cerenkov angle of this track + + mMatchedTracks[type].push_back(matching); + + oneEventClusters.clear(); + + } // if matching in time + } // tracks loop + } // events loop + + delete recon; + recon = nullptr; +} +//================================================================================================================================================== +int MatchHMP::intTrkCha(o2::track::TrackParCov* pTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz) +{ + // Static method to find intersection in between given track and HMPID chambers + // Arguments: pTrk- ESD track; xPc,yPc- track intersection with PC in LORS [cm] + // Returns: intersected chamber ID or -1 + TrackHMP* hmpTrk = new TrackHMP(*pTrk); // create a hmpid track to be used for propagation and matching + for (Int_t i = o2::hmpid::Param::kMinCh; i <= o2::hmpid::Param::kMaxCh; i++) { // chambers loop + Int_t chInt = intTrkCha(i, hmpTrk, xPc, yPc, xRa, yRa, theta, phi, bz); + if (chInt >= 0) { + delete hmpTrk; + hmpTrk = nullptr; + return chInt; + } + } // chambers loop + delete hmpTrk; + hmpTrk = nullptr; + return -1; // no intersection with HMPID chambers +} // IntTrkCha() +//================================================================================================================================================== +int MatchHMP::intTrkCha(int ch, o2::dataformats::TrackHMP* pHmpTrk, double& xPc, double& yPc, double& xRa, double& yRa, double& theta, double& phi, double bz) +{ + // Static method to find intersection in between given track and HMPID chambers + // Arguments: pTrk- HMPID track; xPc,yPc- track intersection with PC in LORS [cm] + // Returns: intersected chamber ID or -1 + o2::hmpid::Param* pParam = o2::hmpid::Param::instance(); + Double_t p1[3], n1[3]; + pParam->norm(ch, n1); + pParam->point(ch, p1, o2::hmpid::Param::kRad); // point & norm for middle of radiator plane + Double_t p2[3], n2[3]; + pParam->norm(ch, n2); + pParam->point(ch, p2, o2::hmpid::Param::kPc); // point & norm for entrance to PC plane + + if (pHmpTrk->intersect(p1, n1, bz) == kFALSE) { + return -1; + } // try to intersect track with the middle of radiator + if (pHmpTrk->intersect(p2, n2, bz) == kFALSE) { + return -1; + } + pParam->mars2LorsVec(ch, n1, theta, phi); // track angles at RAD + pParam->mars2Lors(ch, p1, xRa, yRa); // TRKxRAD position + pParam->mars2Lors(ch, p2, xPc, yPc); // TRKxPC position + + if (pParam->isInside(xPc, yPc, pParam->distCut()) == kTRUE) { + return ch; + } // return intersected chamber + return -1; // no intersection with HMPID chambers +} // IntTrkCha() +//================================================================================================================================================== diff --git a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx index 2b54cab4c32ac..440bda85813a3 100644 --- a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx +++ b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx @@ -36,33 +36,76 @@ MatchITSTPCQC::~MatchITSTPCQC() void MatchITSTPCQC::deleteHistograms() { - // Pt - delete mPt; - delete mPtTPC; - delete mFractionITSTPCmatch; - delete mPtPhysPrim; - delete mPtTPCPhysPrim; - delete mFractionITSTPCmatchPhysPrim; - // Phi - delete mPhi; - delete mPhiTPC; - delete mFractionITSTPCmatchPhi; - delete mPhiPhysPrim; - delete mPhiTPCPhysPrim; - delete mFractionITSTPCmatchPhiPhysPrim; - delete mPhiVsPt; - delete mPhiVsPtTPC; - delete mFractionITSTPCmatchPhiVsPt; - // Eta - delete mEta; - delete mEtaTPC; - delete mFractionITSTPCmatchEta; - delete mEtaPhysPrim; - delete mEtaTPCPhysPrim; - delete mFractionITSTPCmatchEtaPhysPrim; - delete mEtaVsPt; - delete mEtaVsPtTPC; - delete mFractionITSTPCmatchEtaVsPt; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + delete mPtNum[i]; + delete mPtDen[i]; + delete mFractionITSTPCmatch[i]; + delete mPtNum_noEta0[i]; + delete mPtDen_noEta0[i]; + delete mFractionITSTPCmatch_noEta0[i]; + delete mPtPhysPrimNum[i]; + delete mPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim[i]; + + // Phi + delete mPhiNum[i]; + delete mPhiDen[i]; + delete mFractionITSTPCmatchPhi[i]; + delete mPhiPhysPrimNum[i]; + delete mPhiPhysPrimDen[i]; + delete mFractionITSTPCmatchPhiPhysPrim[i]; + delete mPhiVsPtNum[i]; + delete mPhiVsPtDen[i]; + delete mFractionITSTPCmatchPhiVsPt[i]; + + // Eta + delete mEtaNum[i]; + delete mEtaDen[i]; + delete mFractionITSTPCmatchEta[i]; + delete mEtaPhysPrimNum[i]; + delete mEtaPhysPrimDen[i]; + delete mFractionITSTPCmatchEtaPhysPrim[i]; + delete mEtaVsPtNum[i]; + delete mEtaVsPtDen[i]; + delete mFractionITSTPCmatchEtaVsPt[i]; + + // Clusters + delete mClsVsPtNum[i]; + delete mClsVsPtDen[i]; + delete mFractionITSTPCmatchClsVsPt[i]; + + // Chi2 + delete mChi2VsPtNum[i]; + delete mChi2VsPtDen[i]; + delete mFractionITSTPCmatchChi2VsPt[i]; + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + delete mPtNumVsTrkPID[i][j]; + delete mPtDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPtVsTrkPID[i][j]; + // Phi + delete mPhiNumVsTrkPID[i][j]; + delete mPhiDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchPhiVsTrkPID[i][j]; + // Eta + delete mEtaNumVsTrkPID[i][j]; + delete mEtaDenVsTrkPID[i][j]; + delete mFractionITSTPCmatchEtaVsTrkPID[i][j]; + } + } + + // 1/Pt + delete m1OverPtNum[i]; + delete m1OverPtDen[i]; + delete mFractionITSTPCmatch1OverPt[i]; + delete m1OverPtPhysPrimNum[i]; + delete m1OverPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + } + // Residuals delete mResidualPt; delete mResidualPhi; @@ -71,31 +114,76 @@ void MatchITSTPCQC::deleteHistograms() delete mChi2Matching; delete mChi2Refit; delete mTimeResVsPt; + delete mDCAr; + delete mDCArVsPtNum; + delete mDCArVsPtDen; + delete mFractionITSTPCmatchDCArVsPt; } //__________________________________________________________ void MatchITSTPCQC::reset() { - // Pt - mPt->Reset(); - mPtTPC->Reset(); - mPtPhysPrim->Reset(); - mPtTPCPhysPrim->Reset(); - // Phi - mPhi->Reset(); - mPhiTPC->Reset(); - mPhiPhysPrim->Reset(); - mPhiTPCPhysPrim->Reset(); - mPhiVsPt->Reset(); - mPhiVsPtTPC->Reset(); - // Eta - mEta->Reset(); - mEtaTPC->Reset(); - mEtaPhysPrim->Reset(); - mEtaTPCPhysPrim->Reset(); - mEtaVsPt->Reset(); - mEtaVsPtTPC->Reset(); + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i]->Reset(); + mPtDen[i]->Reset(); + mPtNum_noEta0[i]->Reset(); + mPtDen_noEta0[i]->Reset(); + + // Phi + mPhiNum[i]->Reset(); + mPhiDen[i]->Reset(); + mPhiVsPtNum[i]->Reset(); + mPhiVsPtDen[i]->Reset(); + + // Eta + mEtaNum[i]->Reset(); + mEtaDen[i]->Reset(); + mEtaVsPtNum[i]->Reset(); + mEtaVsPtDen[i]->Reset(); + + // Clusters + mClsVsPtNum[i]->Reset(); + mClsVsPtDen[i]->Reset(); + + // Chi2 + mChi2VsPtNum[i]->Reset(); + mChi2VsPtDen[i]->Reset(); + + // 1/Pt + m1OverPtNum[i]->Reset(); + m1OverPtDen[i]->Reset(); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j]->Reset(); + mPtDenVsTrkPID[i][j]->Reset(); + // Phi + mPhiNumVsTrkPID[i][j]->Reset(); + mPhiDenVsTrkPID[i][j]->Reset(); + // Eta + mEtaNumVsTrkPID[i][j]->Reset(); + mEtaDenVsTrkPID[i][j]->Reset(); + } + } + + if (mUseMC) { + mPtPhysPrimNum[i]->Reset(); + mPtPhysPrimDen[i]->Reset(); + + mPhiPhysPrimNum[i]->Reset(); + mPhiPhysPrimDen[i]->Reset(); + + mEtaPhysPrimNum[i]->Reset(); + mEtaPhysPrimDen[i]->Reset(); + + m1OverPtPhysPrimNum[i]->Reset(); + m1OverPtPhysPrimDen[i]->Reset(); + } + } + // Residuals mResidualPt->Reset(); mResidualPhi->Reset(); @@ -104,44 +192,18 @@ void MatchITSTPCQC::reset() mChi2Matching->Reset(); mChi2Refit->Reset(); mTimeResVsPt->Reset(); + mDCAr->Reset(); + mDCArVsPtNum->Reset(); + mDCArVsPtDen->Reset(); } //__________________________________________________________ bool MatchITSTPCQC::init() { - // Pt - mPtTPC = new TH1D("mPtTPC", "Pt distribution of TPC tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - mFractionITSTPCmatch = new TEfficiency("mFractionITSTPCmatch", "Fraction of ITSTPC matched tracks vs Pt; Pt [GeV/c]; Eff", 100, 0.f, 20.f); - mPt = new TH1D("mPt", "Pt distribution of matched tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - // Phi - mPhiTPC = new TH1F("mPhiTPC", "Phi distribution of TPC tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhi = new TEfficiency("mFractionITSTPCmatchPhi", "Fraction of ITSTPC matched tracks vs Phi; Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mPhi = new TH1F("mPhi", "Phi distribution of matched tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mPhiVsPt = new TH2F("mPhiVsPt", "Phi distribution of matched tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - mPhiVsPtTPC = new TH2F("mPhiVsPtTPC", "Phi distribution of TPC tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhiVsPt = new TEfficiency("mFractionITSTPCmatchPhiVsPt", "Fraction of ITSTPC matched tracks vs Phi; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - // Eta - mEta = new TH1F("mEta", "Eta distribution of matched tracks; Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPC = new TH1F("mEtaTPC", "Eta distribution of TPC tracks; Eta; dNdEta", 100, -2.f, 2.f); - mFractionITSTPCmatchEta = new TEfficiency("mFractionITSTPCmatchEta", "Fraction of ITSTPC matched tracks vs Eta; Eta; Eff", 100, -2.f, 2.f); - mEtaVsPt = new TH2F("mEtaVsPt", "Eta distribution of matched tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; dNdEta", 100, 0.f, 20.f, 100, -2.f, 2.f); - mEtaVsPtTPC = new TH2F("mEtaVsPtTPC", "Eta distribution of TPC tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Eta ; dNdEta", 100, 0.f, 20.f, 100, -2.f, 2.f); - mFractionITSTPCmatchEtaVsPt = new TEfficiency("mFractionITSTPCmatchEtaVsPt", "Fraction of ITSTPC matched tracks vs Eta; #it{p}_{T} [GeV#it{c}]; Eta; Eff", 100, 0.f, 20.f, 100, -2.f, 2.f); - // These will be empty in case of no MC info... - mPhiTPCPhysPrim = new TH1F("mPhiTPCPhysPrim", "Phi distribution of TPC tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhiPhysPrim = new TEfficiency("mFractionITSTPCmatchPhiPhysPrim", "Fraction of ITSTPC matched tracks vs Phi (physical primary); Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchEtaPhysPrim = new TEfficiency("mFractionITSTPCmatchEtaPhysPrim", "Fraction of ITSTPC matched tracks vs Eta (physical primary); Eta; Eff", 100, -2.f, 2.f); - mPhiPhysPrim = new TH1F("mPhiPhysPrim", "Phi distribution of matched tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mEtaPhysPrim = new TH1F("mEtaPhysPrim", "Eta distribution of matched tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPCPhysPrim = new TH1F("mEtaTPCPhysPrim", "Eta distribution of TPC tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - // ...till here - - mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", 100, 0.f, 20.f, 100, -1.f, 1.f); - mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); - mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); - mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); - mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); - + LOGP(debug, "Creating Variable Binning"); + std::array title{"TPC", "ITS"}; + std::array etaSel{"", ", |eta| < 0.9"}; + std::array maxNCls{156, 7}; // log binning for pT const Int_t nbinsPt = 100; const Double_t xminPt = 0.01; @@ -154,43 +216,151 @@ bool MatchITSTPCQC::init() Double_t xlogPt = xlogminPt + i * dlogxPt; xbinsPt[i] = TMath::Exp(TMath::Log(10) * xlogPt); } - mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); - mPtTPCPhysPrim = new TH1F("mPtTPPhysPrimC", "Pt distribution of TPC tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - mFractionITSTPCmatchPhysPrim = new TEfficiency("mFractionITSTPCmatchPhysPrim", "Fraction of ITSTPC matched tracks vs Pt (physical primary); Pt [GeV/c]; Eff", nbinsPt, xbinsPt); - mPtPhysPrim = new TH1F("mPtPhysPrim", "Pt distribution of matched tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - - mPtTPC->Sumw2(); - mPt->Sumw2(); - mPhiTPC->Sumw2(); - mPhi->Sumw2(); - mPhiVsPt->Sumw2(); - mPhiVsPtTPC->Sumw2(); - mPtTPCPhysPrim->Sumw2(); - mPtPhysPrim->Sumw2(); - mPhiTPCPhysPrim->Sumw2(); - mPhiPhysPrim->Sumw2(); - mEtaTPC->Sumw2(); - mEtaPhysPrim->Sumw2(); - mEtaTPCPhysPrim->Sumw2(); - mEtaVsPt->Sumw2(); - mEtaVsPtTPC->Sumw2(); - - mPtTPC->SetOption("logy"); - mPt->SetOption("logy"); - mEta->SetOption("logy"); - mChi2Matching->SetOption("logy"); - mChi2Refit->SetOption("logy"); - mTimeResVsPt->SetOption("colz logz logy logx"); - mPtTPC->GetYaxis()->SetTitleOffset(1.4); - mPt->GetYaxis()->SetTitleOffset(1.4); - mEta->GetYaxis()->SetTitleOffset(1.4); + LOGP(debug, "Creating Histograms"); + // Data and MC + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i] = new TH1D(Form("mPtNum_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum[i]->Sumw2(); + mPtNum[i]->SetOption("logy"); + mPtNum[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen[i] = new TH1D(Form("mPtDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen[i]->Sumw2(); + mPtDen[i]->SetOption("logy"); + mPtDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch[i] = new TEfficiency(Form("mFractionITSTPCmatch_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum_noEta0[i] = new TH1D(Form("mPtNum_noEta0_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks without |eta| < 0.05, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum_noEta0[i]->Sumw2(); + mPtNum_noEta0[i]->SetOption("logy"); + mPtNum_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen_noEta0[i] = new TH1D(Form("mPtDen_noEta0_%s", title[i].c_str()), Form("Pt distribution of %s tracks without |eta| < 0.05 %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen_noEta0[i]->Sumw2(); + mPtDen_noEta0[i]->SetOption("logy"); + mPtDen_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatch_noEta0[i] = new TEfficiency(Form("mFractionITSTPCmatch_noEta0_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt without |eta| < 0.05 %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + + // Phi + mPhiNum[i] = new TH1F(Form("mPhiNum_%s", title[i].c_str()), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiNum[i]->Sumw2(); + mPhiDen[i] = new TH1F(Form("mPhiDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiDen[i]->Sumw2(); + mFractionITSTPCmatchPhi[i] = new TEfficiency(Form("mFractionITSTPCmatchPhi_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i] = new TH2F(Form("mPhiVsPtNum_%s", title[i].c_str()), Form("Phi vs Pt distribution of ITSTPC matched tracks wrt %s %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i]->Sumw2(); + mPhiVsPtDen[i] = new TH2F(Form("mPhiVsPtDen_%s", title[i].c_str()), Form("Phi vs Pt distribution of %s tracks %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchPhiVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks %s, Phi vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNum[i] = new TH1F(Form("mEtaNum_%s", title[i].c_str()), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaNum[i]->Sumw2(); + mEtaNum[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaDen[i] = new TH1F(Form("mEtaDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaDen[i]->Sumw2(); + mEtaDen[i]->GetYaxis()->SetTitleOffset(1.4); + mFractionITSTPCmatchEta[i] = new TEfficiency(Form("mFractionITSTPCmatchEta_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks , wrt %s tracks, vs Eta; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + mEtaVsPtNum[i] = new TH2F(Form("mEtaVsPtNum_%s", title[i].c_str()), Form("Eta vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mEtaVsPtNum[i]->Sumw2(); + mEtaVsPtDen[i] = new TH2F(Form("mEtaVsPtDen_%s", title[i].c_str()), Form("Eta vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mEtaVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchEtaVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Eta vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; Eff", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + + // Clusters + mClsVsPtNum[i] = new TH2F(Form("mClsVsPtNum_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + mClsVsPtNum[i]->Sumw2(); + mClsVsPtDen[i] = new TH2F(Form("mClsVsPtDen_%s", title[i].c_str()), Form("#Clusters vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; #Clusters", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + mClsVsPtDen[i]->Sumw2(); + mFractionITSTPCmatchClsVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchClsVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, #Clusters vs Pt; #it{p}_{T} [GeV#it{c}]; #Clusters; Eff", title[i].c_str()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + + // Chi2 + mChi2VsPtNum[i] = new TH2F(Form("mChi2VsPtNum_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + mChi2VsPtNum[i]->Sumw2(); + mChi2VsPtDen[i] = new TH2F(Form("mChi2VsPtDen_%s", title[i].c_str()), Form("Chi2 vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; Chi2", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + mChi2VsPtDen[i]->Sumw2(); + mFractionITSTPCmatchChi2VsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchChi2VsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Chi2 vs Pt; #it{p}_{T} [GeV#it{c}]; Chi2; Eff", title[i].c_str()), 100, 0.f, 20.f, 200, 0, 300); + + // 1/pt + m1OverPtNum[i] = new TH1D(Form("m1OverPtNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks, wrt %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtNum[i]->Sumw2(); + m1OverPtDen[i] = new TH1D(Form("m1OverPtDen_%s", title[i].c_str()), Form("1/Pt distribution of %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtDen[i]->Sumw2(); + mFractionITSTPCmatch1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatch1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt, wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + mPtNumVsTrkPID[i][j] = new TH1D(Form("mPtNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + mPtNumVsTrkPID[i][j]->Sumw2(); + mPtDenVsTrkPID[i][j] = new TH1D(Form("mPtDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Pt distribution of %s tracks %s, TrkPID %i; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + mPtDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPtVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPtVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s, TrkPID %i; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 20.f); + + // Phi + mPhiNumVsTrkPID[i][j] = new TH1D(Form("mPhiNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiNumVsTrkPID[i][j]->Sumw2(); + mPhiDenVsTrkPID[i][j] = new TH1D(Form("mPhiDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Phi distribution of %s tracks %s, TrkPID %i; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + mPhiDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchPhiVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Phi %s, TrkPID %i; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, 0.f, 2 * TMath::Pi()); + + // Eta + mEtaNumVsTrkPID[i][j] = new TH1D(Form("mEtaNumVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaNumVsTrkPID[i][j]->Sumw2(); + mEtaDenVsTrkPID[i][j] = new TH1D(Form("mEtaDenVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Eta distribution of %s tracks %s, TrkPID %i; Eta; dNdEta", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + mEtaDenVsTrkPID[i][j]->Sumw2(); + mFractionITSTPCmatchEtaVsTrkPID[i][j] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsTrkPID_%s_PID%i", title[i].c_str(), j), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Eta %s, TrkPID %i; Eta; Eff", title[i].c_str(), etaSel[i].c_str(), j), 100, -2.f, 2.f); + } + } + } + + mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", 100, 0.f, 20.f, 100, -1.f, 1.f); + mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); + mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); + mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); + mChi2Matching->SetOption("logy"); mChi2Matching->GetYaxis()->SetTitleOffset(1.4); + mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); + mChi2Refit->SetOption("logy"); mChi2Refit->GetYaxis()->SetTitleOffset(1.4); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 200, -100, 100); + mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCArVsPtNum->Sumw2(); + mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCArVsPtDen->Sumw2(); + mFractionITSTPCmatchDCArVsPt = new TEfficiency("mFractionITSTPCmatchDCArVsPt", "Fraction of ITSTPC matched tracks wrt TPC vs DCAr; #it{p}_{T} [GeV#it{c}]; DCAr; Eff", 100, 0, 20., 200, -30, 30); + + mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); + mTimeResVsPt->SetOption("colz logz logy logx"); mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); if (mUseMC) { mcReader.initFromDigitContext("collisioncontext.root"); + + for (int i = 0; i < matchType::SIZE; ++i) { + mPtPhysPrimNum[i] = new TH1F(Form("mPtPhysPrimNum_%s", title[i].c_str()), Form("Pt distribution of matched tracks (physical primary), wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimNum[i]->Sumw2(); + mPtPhysPrimDen[i] = new TH1F(Form("mPtPhysPrimDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks (physical primary) %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhiPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi (physical primary), wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + + mEtaPhysPrimNum[i] = new TH1F(Form("mEtaPhysPrimNum_%s", title[i].c_str()), Form("Eta distribution of matched tracks (physical primary), wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimNum[i]->Sumw2(); + mEtaPhysPrimDen[i] = new TH1F(Form("mEtaPhysPrimDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks (physical primary); Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchEtaPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Eta (physical primary), wrt %s tracks; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + + mPhiPhysPrimNum[i] = new TH1F(Form("mPhiPhysPrimNum_%s", title[i].c_str()), Form("Phi distribution of matched tracks (physical primary), wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimNum[i]->Sumw2(); + mPhiPhysPrimDen[i] = new TH1F(Form("mPhiPhysPrimDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks (physical primary) %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Pt (physical primary), wrt %s tracks %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + + m1OverPtPhysPrimNum[i] = new TH1D(Form("m1OverPtPhysPrimNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtPhysPrimNum[i]->Sumw2(); + m1OverPtPhysPrimDen[i] = new TH1D(Form("m1OverPtPhysPrimDen_%s", title[i].c_str()), Form("1/PtPt distribution of %s tracks (physical primary) %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtPhysPrimDen[i]->Sumw2(); + mFractionITSTPCmatchPhysPrim1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + } } return true; @@ -205,7 +375,7 @@ void MatchITSTPCQC::initDataRequest() mSrc &= mAllowedSources; - if ((mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0)) { + if (mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0 || mSrc[GID::Source::ITS] == 0) { LOG(fatal) << "We cannot do ITSTPC QC, some sources are missing, check sources in " << mSrc; } @@ -224,48 +394,109 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) static int evCount = 0; mRecoCont.collectData(ctx, *mDataRequest.get()); mTPCTracks = mRecoCont.getTPCTracks(); + mITSTracks = mRecoCont.getITSTracks(); mITSTPCTracks = mRecoCont.getTPCITSTracks(); LOG(debug) << "****** Number of found ITSTPC tracks = " << mITSTPCTracks.size(); LOG(debug) << "****** Number of found TPC tracks = " << mTPCTracks.size(); + LOG(debug) << "****** Number of found ITS tracks = " << mITSTracks.size(); - // cache selection for TPC tracks + // cache selection for TPC and ITS tracks std::vector isTPCTrackSelectedEntry(mTPCTracks.size(), false); + std::vector isITSTrackSelectedEntry(mITSTracks.size(), false); TrackCuts cuts; + // ITS track + cuts.setMinPtITSCut(mPtITSCut); + cuts.setEtaITSCut(mEtaITSCut); + cuts.setMinNClustersITS(mMinNClustersITS); + cuts.setMaxChi2PerClusterITS(mMaxChi2PerClusterITS); + for (auto it = mRequiredITSHits.begin(); it != mRequiredITSHits.end(); it++) { + cuts.setRequireHitsInITSLayers((*it).first, (*it).second); + } + // TPC track + cuts.setMinPtTPCCut(mPtTPCCut); + cuts.setEtaTPCCut(mEtaTPCCut); + cuts.setMinNTPCClustersCut(mNTPCClustersCut); + cuts.setMaxDCATPCCut(mDCATPCCut); + cuts.setMaxDCATPCCutY(mDCATPCCutY); + // ITS-TPC track kinematics + cuts.setMinPtCut(mPtCut); + cuts.setMaxPtCut(mPtMaxCut); + cuts.setEtaCut(-mEtaCut, mEtaCut); for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { auto const& trkTpc = mTPCTracks[itrk]; - // if (selectTrack(trkTpc)) { - // isTPCTrackSelectedEntry[itrk] = true; - // } o2::dataformats::GlobalTrackID id(itrk, GID::TPC); if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary isTPCTrackSelectedEntry[itrk] = true; } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trkIts = mITSTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::ITS); + if (cuts.isSelected(id, mRecoCont)) { + // NB: same cuts for numerator and denominator tracks of ITS-TPC matching + // To change cuts only for numerator, something like o2::dataformats::GlobalTrackID id(itrk, GID::ITSTPC) is necessary + isITSTrackSelectedEntry[itrk] = true; + } + } + // numerator + eta, chi2... if (mUseMC) { - mMapLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapLabels[i].clear(); + } for (int itrk = 0; itrk < static_cast(mITSTPCTracks.size()); ++itrk) { auto const& trk = mITSTPCTracks[itrk]; auto idxTrkTpc = trk.getRefTPC().getIndex(); + if (trk.getRefITS().getSource() != GID::ITS) { + continue; + } if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); - if (mMapLabels.find(lbl) == mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) == mMapLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::TPC].insert({lbl, {itrk, true}}); + } else { + mMapLabels[matchType::TPC].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::TPC].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + auto idxTrkIts = trk.getRefITS().getIndex(); + if (isITSTrackSelectedEntry[idxTrkIts] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) == mMapLabels[matchType::ITS].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapLabels.insert({lbl, {itrk, true}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, true}}); } else { - mMapLabels.insert({lbl, {itrk, false}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest pt - if (mITSTPCTracks[mMapLabels.at(lbl).mIdx].getPt() < trk.getPt()) { - mMapLabels.at(lbl).mIdx = itrk; + if (mITSTPCTracks[mMapLabels[matchType::ITS].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::ITS].at(lbl).mIdx = itrk; } } } @@ -273,183 +504,424 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) LOG(info) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapLabels) { - auto const& trk = mITSTPCTracks[el.second.mIdx]; - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mPhiVsPt->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); - mEtaVsPt->Fill(trkTpc.getPt(), trkTpc.getEta()); - // we fill also the denominator - mPtTPC->Fill(trkTpc.getPt()); - mPhiTPC->Fill(trkTpc.getPhi()); - mPhiVsPtTPC->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEtaTPC->Fill(trkTpc.getEta()); - mEtaVsPtTPC->Fill(trkTpc.getPt(), trkTpc.getEta()); - if (el.second.mIsPhysicalPrimary) { - mPtPhysPrim->Fill(trkTpc.getPt()); - mPhiPhysPrim->Fill(trkTpc.getPhi()); - mEtaPhysPrim->Fill(trkTpc.getEta()); + for (int i = 0; i < matchType::SIZE; ++i) { + for (auto const& el : mMapLabels[i]) { + auto const& trk = mITSTPCTracks[el.second.mIdx]; + o2::track::TrackParCov trkDen; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkDen = mTPCTracks[trk.getRefTPC()]; + } else { + trkDen = mITSTracks[trk.getRefITS()]; + if (std::abs(trkDen.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + } + } + if (isEtaITSOk) { + mPtNum[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiNum[i]->Fill(trkDen.getPhi()); + mPhiVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtDen[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtDen_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiDen[i]->Fill(trkDen.getPhi()); + mPhiVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + // we fill also the denominator + mPtDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPt()); + mPhiDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getPhi()); + } + } + mEtaNum[i]->Fill(trkDen.getEta()); + mEtaVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getEta()); // we fill also the denominator - mPtTPCPhysPrim->Fill(trkTpc.getPt()); - mPhiTPCPhysPrim->Fill(trkTpc.getPhi()); - mEtaTPCPhysPrim->Fill(trkTpc.getEta()); + mEtaDen[i]->Fill(trkDen.getEta()); + mEtaVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getEta()); + if (i == matchType::TPC) { + auto tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + mClsVsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (tpcTrk.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtNum->Fill(tpcTrk.getPt(), dca[0]); + mDCArVsPtDen->Fill(tpcTrk.getPt(), dca[0]); + } + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + mClsVsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtDen[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaDenVsTrkPID[i][trkDen.getPID()]->Fill(trkDen.getEta()); + } + if (el.second.mIsPhysicalPrimary) { + if (isEtaITSOk) { + mPtPhysPrimNum[i]->Fill(trkDen.getPt()); + mPhiPhysPrimNum[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtPhysPrimDen[i]->Fill(trkDen.getPt()); + mPhiPhysPrimDen[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaPhysPrimNum[i]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaPhysPrimDen[i]->Fill(trkDen.getEta()); + } + ++mNITSTPCSelectedTracks[i]; } - ++mNITSTPCSelectedTracks; } } - + int iITSTPC = 0; for (auto const& trk : mITSTPCTracks) { if (trk.getRefTPC().getIndex() >= mTPCTracks.size()) { - LOG(fatal) << "******************** ATTENTION! idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; - continue; + LOG(fatal) << "******************** ATTENTION! for TPC track associated to matched track: idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; } - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - auto idxTrkTpc = trk.getRefTPC().getIndex(); - if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { - if (!mUseMC) { - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mPhiVsPt->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); - mEtaVsPt->Fill(trkTpc.getPt(), trkTpc.getEta()); + std::array title{"TPC", "ITS"}; + for (int i = 0; i < matchType::SIZE; ++i) { + o2::track::TrackParCov trkRef; + int idxTrkRef; + bool fillHisto = false; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkRef = mTPCTracks[trk.getRefTPC()]; + idxTrkRef = trk.getRefTPC().getIndex(); + if (isTPCTrackSelectedEntry[idxTrkRef] == true) { + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } + } else { + idxTrkRef = trk.getRefITS().getIndex(); + if (trk.getRefITS().getSource() == GID::ITSAB) { + // do not use afterburner tracks + LOG(debug) << "Track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " is from afterburner"; + continue; + } + if (idxTrkRef >= mITSTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for ITS track associated to matched track (NOT from AB): idx = " << trk.getRefITS().getIndex() << ", size of container = " << mITSTracks.size() << " in TF " << evCount; + } + trkRef = mITSTracks[trk.getRefITS()]; + LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); + if (isITSTrackSelectedEntry[idxTrkRef] == true) { + LOG(debug) << "Track was selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we keep it in the numerator, pt = " << trkRef.getPt(); + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } else { + LOG(debug) << "Track was not selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + if (std::abs(trkRef.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + LOG(debug) << "Track (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " will be discarded when filling pt of phi related histograms, since eta = " << trkRef.getEta() << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + } + if (fillHisto == true) { + if (!mUseMC) { + LOG(debug) << "Filling num (" << title[i] << ") with track with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " with pt = " << trkRef.getPt(); + if (isEtaITSOk) { + mPtNum[i]->Fill(trkRef.getPt()); + if (std::abs(trkRef.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkRef.getPt()); + } + mPhiNum[i]->Fill(trkRef.getPhi()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mPtNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPt()); + mPhiNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getPhi()); + } + mPhiVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getPhi()); + m1OverPtNum[i]->Fill(trkRef.getSign() * trkRef.getPtInv()); + } + mEtaNum[i]->Fill(trkRef.getEta()); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + mEtaNumVsTrkPID[i][trkRef.getPID()]->Fill(trkRef.getEta()); + } + mEtaVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getEta()); + if (i == matchType::TPC) { + const auto& tpcTrk = mTPCTracks[trk.getRefTPC()]; + mClsVsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(tpcTrk.getPt(), tpcTrk.getChi2()); + } else { + const auto& itsTrk = mITSTracks[trk.getRefITS()]; + mClsVsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getNClusters()); + mChi2VsPtNum[i]->Fill(itsTrk.getPt(), itsTrk.getChi2()); + } + } + if (i == matchType::TPC) { + mResidualPt->Fill(trk.getPt(), trk.getPt() - trkRef.getPt()); + mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkRef.getPhi()); + mResidualEta->Fill(trk.getEta(), trk.getEta() - trkRef.getEta()); + mChi2Matching->Fill(trk.getChi2Match()); + mChi2Refit->Fill(trk.getChi2Refit()); + mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); + math_utils::Point3D v{}; + std::array dca; + if (trkRef.propagateParamToDCA(v, mBz, &dca)) { + mDCAr->Fill(dca[0]); + if (!mUseMC) { + mDCArVsPtNum->Fill(trkRef.getPt(), dca[0]); + } + } + LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); + } + } else { + LOG(debug) << "Not filling num (" << title[i] << ") for ITSTPC track " << iITSTPC << " for track with pt " << trkRef.getPt(); } - mResidualPt->Fill(trk.getPt(), trk.getPt() - trkTpc.getPt()); - mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkTpc.getPhi()); - mResidualEta->Fill(trk.getEta(), trk.getEta() - trkTpc.getEta()); - mChi2Matching->Fill(trk.getChi2Match()); - mChi2Refit->Fill(trk.getChi2Refit()); - mTimeResVsPt->Fill(trkTpc.getPt(), trk.getTimeMUS().getTimeStampError()); - LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); - ++mNITSTPCSelectedTracks; } + ++iITSTPC; } // now filling the denominator for the efficiency calculation if (mUseMC) { - mMapTPCLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapRefLabels[i].clear(); + } // filling the map where we store for each MC label, the track id of the reconstructed // track with the highest number of TPC clusters for (int itrk = 0; itrk < static_cast(mTPCTracks.size()); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); - if (mMapLabels.find(lbl) != mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) != mMapLabels[matchType::TPC].end()) { // the track was already added to the denominator continue; } - if (mMapTPCLabels.find(lbl) == mMapTPCLabels.end()) { + if (mMapRefLabels[matchType::TPC].find(lbl) == mMapRefLabels[matchType::TPC].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapTPCLabels.insert({lbl, {itrk, true}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, true}}); } else { - mMapTPCLabels.insert({lbl, {itrk, false}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest number of TPC clusters - if (mTPCTracks[mMapTPCLabels.at(lbl).mIdx].getNClusters() < trk.getNClusters()) { - mMapTPCLabels.at(lbl).mIdx = itrk; + if (mTPCTracks[mMapRefLabels[matchType::TPC].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + } + // same for ITS + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of ITS clusters + for (int itrk = 0; itrk < static_cast(mITSTracks.size()); ++itrk) { + auto const& trk = mITSTracks[itrk]; + if (isITSTrackSelectedEntry[itrk] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) != mMapLabels[matchType::ITS].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::ITS].find(lbl) == mMapRefLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of ITS clusters + if (mITSTracks[mMapRefLabels[matchType::ITS].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::ITS].at(lbl).mIdx = itrk; } } } } - LOG(info) << "number of entries in map for denominator (without duplicates) = " << mMapTPCLabels.size() + mMapLabels.size(); + LOG(info) << "number of entries in map for denominator of TPC tracks (without duplicates) = " << mMapRefLabels[matchType::TPC].size() + mMapLabels[matchType::TPC].size(); + LOG(info) << "number of entries in map for denominator of ITS tracks (without duplicates) = " << mMapRefLabels[matchType::ITS].size() + mMapLabels[matchType::ITS].size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapTPCLabels) { + for (auto const& el : mMapRefLabels[matchType::TPC]) { auto const& trk = mTPCTracks[el.second.mIdx]; - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mPhiVsPtTPC->Fill(trk.getPt(), trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); - mEtaVsPtTPC->Fill(trk.getPt(), trk.getEta()); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } if (el.second.mIsPhysicalPrimary) { - mPtTPCPhysPrim->Fill(trk.getPt()); - mPhiTPCPhysPrim->Fill(trk.getPhi()); - mEtaTPCPhysPrim->Fill(trk.getEta()); + mPtPhysPrimDen[matchType::TPC]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::TPC]->Fill(trk.getPhi()); + mEtaPhysPrimDen[matchType::TPC]->Fill(trk.getEta()); + m1OverPtPhysPrimDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); } ++mNTPCSelectedTracks; } + for (auto const& el : mMapRefLabels[matchType::ITS]) { + auto const& trk = mITSTracks[el.second.mIdx]; + if (std::abs(trk.getEta()) < 0.9) { + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + if (el.second.mIsPhysicalPrimary) { + if (std::abs(trk.getEta()) < 0.9) { + mPtPhysPrimDen[matchType::ITS]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::ITS]->Fill(trk.getPhi()); + m1OverPtPhysPrimDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaPhysPrimDen[matchType::ITS]->Fill(trk.getEta()); + } + ++mNITSSelectedTracks; + } } else { // if we are in data, we loop over all tracks (no check on the label) for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mPhiVsPtTPC->Fill(trk.getPt(), trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); - mEtaVsPtTPC->Fill(trk.getPt(), trk.getEta()); + LOG(debug) << "Filling den (TPC) with track with pt = " << trk.getPt(); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for den pt, phi, phi vs pt, 1.pt histos"; + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); + mClsVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getChi2()); + math_utils::Point3D v{}; + std::array dca{}; + if (auto trc = trk; trc.propagateParamToDCA(v, mBz, &dca)) { + mDCArVsPtDen->Fill(trc.getPt(), dca[0]); + } ++mNTPCSelectedTracks; } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trk = mITSTracks[itrk]; + LOG(debug) << "Checking den for track (ITS) " << itrk << " with pt " << trk.getPt() << " and eta = " << trk.getEta(); + if (isITSTrackSelectedEntry[itrk] == true) { + if (std::abs(trk.getEta()) < 0.9) { + LOG(debug) << "Filling den for track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta(); + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for num pt, phi, phi vs pt, 1.pt histos"; + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + mClsVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getNClusters()); + mChi2VsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getChi2()); + ++mNITSSelectedTracks; + } else { + LOG(debug) << "Not filling for this track (ITS) " << itrk << " with pt = " << trk.getPt(); + } + } } evCount++; } //__________________________________________________________ - -bool MatchITSTPCQC::selectTrack(o2::tpc::TrackTPC const& track) -{ - - if (track.getPt() < mPtCut) { - return false; - } - if (std::abs(track.getEta()) > mEtaCut) { - return false; - } - if (track.getNClusters() < mNTPCClustersCut) { - return false; - } - - math_utils::Point3D v{}; - std::array dca; - if (!(const_cast(track).propagateParamToDCA(v, mBz, &dca, mDCACut)) || std::abs(dca[0]) > mDCACutY) { - return false; - } - - return true; -} - -//__________________________________________________________ - void MatchITSTPCQC::finalize() { + std::array title{"TPC", "ITS"}; + // first we use denominators and nominators to set the TEfficiency; later they are scaled - for (int i = 0; i < mPtTPC->GetNbinsX(); ++i) { - if (mPtTPC->GetBinContent(i + 1) < mPt->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPtTPC[i] = " << mPtTPC->GetBinContent(i + 1) << ", mPt[i] = " << mPt->GetBinContent(i + 1); + // some checks + for (int ti = 0; ti < matchType::SIZE; ++ti) { + for (int i = 0; i < mPtDen[ti]->GetNbinsX(); ++i) { + if (mPtDen[ti]->GetBinContent(i + 1) < mPtNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPtNum[ti]->GetBinLowEdge(i + 1) << " , " << mPtNum[ti]->GetBinLowEdge(i + 1) + mPtNum[ti]->GetBinWidth(i + 1) << "]: mPtDen[i] = " << mPtDen[ti]->GetBinContent(i + 1) << ", mPtNum[i] = " << mPtNum[ti]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mPhiTPC->GetNbinsX(); ++i) { - if (mPhiTPC->GetBinContent(i + 1) < mPhi->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPhiTPC[i] = " << mPhiTPC->GetBinContent(i + 1) << ", mPhi[i] = " << mPhi->GetBinContent(i + 1); + for (int i = 0; i < mPtDen_noEta0[ti]->GetNbinsX(); ++i) { + if (mPtDen_noEta0[ti]->GetBinContent(i + 1) < mPtNum_noEta0[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPtNum_noEta0[ti]->GetBinLowEdge(i + 1) << " , " << mPtNum_noEta0[ti]->GetBinLowEdge(i + 1) + mPtNum_noEta0[ti]->GetBinWidth(i + 1) << "]: mPtDen_noEta0[i] = " << mPtDen_noEta0[ti]->GetBinContent(i + 1) << ", mPtNum_noEta0[i] = " << mPtNum_noEta0[ti]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mEtaTPC->GetNbinsX(); ++i) { - if (mEtaTPC->GetBinContent(i + 1) < mEta->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mEtaTPC[i] = " << mEtaTPC->GetBinContent(i + 1) << ", mEta[i] = " << mEta->GetBinContent(i + 1); + for (int i = 0; i < mPhiDen[ti]->GetNbinsX(); ++i) { + if (mPhiDen[ti]->GetBinContent(i + 1) < mPhiNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mPhiNum[ti]->GetBinLowEdge(i + 1) << " , " << mPhiNum[ti]->GetBinLowEdge(i + 1) + mPhiNum[ti]->GetBinWidth(i + 1) << "]: mPhiDen[i] = " << mPhiDen[ti]->GetBinContent(i + 1) << ", mPhiNum[i] = " << mPhiNum[ti]->GetBinContent(i + 1); + } + } + for (int i = 0; i < mEtaDen[ti]->GetNbinsX(); ++i) { + if (mEtaDen[ti]->GetBinContent(i + 1) < mEtaNum[ti]->GetBinContent(i + 1)) { + LOG(error) << title[ti] << ": bin " << i + 1 << " in [" << mEtaNum[ti]->GetBinLowEdge(i + 1) << " , " << mEtaNum[ti]->GetBinLowEdge(i + 1) + mEtaNum[ti]->GetBinWidth(i + 1) << "]: mEtaDen[i] = " << mEtaDen[ti]->GetBinContent(i + 1) << ", mEtaNum[i] = " << mEtaNum[ti]->GetBinContent(i + 1); + } } - } - // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents - setEfficiency(mFractionITSTPCmatch, mPt, mPtTPC); - setEfficiency(mFractionITSTPCmatchPhi, mPhi, mPhiTPC); - setEfficiency(mFractionITSTPCmatchEta, mEta, mEtaTPC); - setEfficiency(mFractionITSTPCmatchPhiVsPt, mPhiVsPt, mPhiVsPtTPC, true); - setEfficiency(mFractionITSTPCmatchEtaVsPt, mEtaVsPt, mEtaVsPtTPC, true); - if (mUseMC) { - setEfficiency(mFractionITSTPCmatchPhysPrim, mPtPhysPrim, mPtTPCPhysPrim); - setEfficiency(mFractionITSTPCmatchPhiPhysPrim, mPhiPhysPrim, mPhiTPCPhysPrim); - setEfficiency(mFractionITSTPCmatchEtaPhysPrim, mEtaPhysPrim, mEtaTPCPhysPrim); + // filling the efficiency + setEfficiency(mFractionITSTPCmatch[ti], mPtNum[ti], mPtDen[ti]); + setEfficiency(mFractionITSTPCmatch_noEta0[ti], mPtNum_noEta0[ti], mPtDen_noEta0[ti]); + setEfficiency(mFractionITSTPCmatchPhi[ti], mPhiNum[ti], mPhiDen[ti]); + setEfficiency(mFractionITSTPCmatchEta[ti], mEtaNum[ti], mEtaDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiVsPt[ti], mPhiVsPtNum[ti], mPhiVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatchEtaVsPt[ti], mEtaVsPtNum[ti], mEtaVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatch1OverPt[ti], m1OverPtNum[ti], m1OverPtDen[ti]); + setEfficiency(mFractionITSTPCmatchClsVsPt[ti], mClsVsPtNum[ti], mClsVsPtDen[ti], true); + setEfficiency(mFractionITSTPCmatchChi2VsPt[ti], mChi2VsPtNum[ti], mChi2VsPtDen[ti], true); + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + setEfficiency(mFractionITSTPCmatchPtVsTrkPID[ti][j], mPtNumVsTrkPID[ti][j], mPtDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchPhiVsTrkPID[ti][j], mPhiNumVsTrkPID[ti][j], mPhiDenVsTrkPID[ti][j]); + setEfficiency(mFractionITSTPCmatchEtaVsTrkPID[ti][j], mEtaNumVsTrkPID[ti][j], mEtaDenVsTrkPID[ti][j]); + } + } + if (mUseMC) { + setEfficiency(mFractionITSTPCmatchPhysPrim[ti], mPtPhysPrimNum[ti], mPtPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhiPhysPrim[ti], mPhiPhysPrimNum[ti], mPhiPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchEtaPhysPrim[ti], mEtaPhysPrimNum[ti], mEtaPhysPrimDen[ti]); + setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[ti], m1OverPtPhysPrimNum[ti], m1OverPtPhysPrimDen[ti]); + } } - + setEfficiency(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen, true); /* mPtTPC->Scale(scaleFactTPC); mPt->Scale(scaleFactITSTPC); @@ -472,27 +944,52 @@ void MatchITSTPCQC::finalize() void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D) { - if (!eff) { + if (eff == nullptr) { LOG(fatal) << "Cannot get TEfficiency object "; } - if (!hnum) { + if (hnum == nullptr) { LOG(fatal) << "Cannot get numerator histogram for TEfficiency object " << eff->GetName(); } - if (!hden) { + if (hden == nullptr) { LOG(fatal) << "Cannot get denominator histogram for TEfficiency object " << eff->GetName(); } - LOG(info) << "Setting efficiency " << eff->GetName() << " from " << hnum->GetName() << " and " << hden->GetName(); // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents - if constexpr (0) { // checking - LOG(info) << "Num " << hnum->GetName() << " " << hnum->GetNbinsX() << " " << hnum->GetNbinsY(); - LOG(info) << "Den " << hden->GetName() << " " << hden->GetNbinsX() << " " << hden->GetNbinsY(); - for (int i = 1; i <= hden->GetNbinsX(); i++) { - if (hden->GetBinContent(i) < hnum->GetBinContent(i)) { - LOG(warning) << "bin " << i << " den: " << hden->GetBinContent(i) << " < num: " << hnum->GetBinContent(i) << " should be the opposite"; + if constexpr (false) { // checking + bool bad{false}; + LOG(info) << "Setting efficiency " << eff->GetName() << " from " << hnum->GetName() << " and " << hden->GetName(); + LOG(info) << "Num " << hnum->GetName() << " " << hnum->GetNbinsX() << " " << hnum->GetNbinsY() << " with " << hnum->GetEntries() << " entries"; + LOG(info) << "Den " << hden->GetName() << " " << hden->GetNbinsX() << " " << hden->GetNbinsY() << " with " << hden->GetEntries() << " entries"; + if (hnum->GetDimension() != hden->GetDimension()) { + LOGP(warning, "Histograms have different dimensions (num={} to den={})", hnum->GetDimension(), hden->GetDimension()); + bad = true; + } + if (!TEfficiency::CheckBinning(*hnum, *hden)) { + LOGP(warning, "Histograms do not have a compatible binning"); + bad = true; + } + if (!is2D) { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + if (hden->GetBinContent(i) < hnum->GetBinContent(i)) { + LOG(warning) << "bin " << i << " den: " << hden->GetBinContent(i) << " < num: " << hnum->GetBinContent(i) << " should be the opposite"; + bad = true; + } + } + } else { + for (int i = 1; i <= hden->GetNbinsX(); i++) { + for (int j = 1; j <= hden->GetNbinsY(); j++) { + if (hden->GetBinContent(i, j) < hnum->GetBinContent(i, j)) { + LOGP(warning, "bin {}/{} -> den: {} < num: {}", i, j, hden->GetBinContent(i, j), hnum->GetBinContent(i, j)); + bad = true; + } + } } } + if (bad) { + return; + } } + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents if (!eff->SetTotalHistogram(*hden, "f")) { LOG(fatal) << "Something went wrong when defining the efficiency denominator " << eff->GetName() << " from " << hnum->GetName(); } @@ -511,30 +1008,84 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i void MatchITSTPCQC::getHistos(TObjArray& objar) { - objar.Add(mPtTPC); - objar.Add(mFractionITSTPCmatch); - objar.Add(mPt); - objar.Add(mPhiTPC); - objar.Add(mPhiVsPtTPC); - objar.Add(mFractionITSTPCmatchPhi); - objar.Add(mPhi); - objar.Add(mPhiVsPt); - objar.Add(mPtTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhysPrim); - objar.Add(mPtPhysPrim); - objar.Add(mPhiTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhiPhysPrim); - objar.Add(mPhiPhysPrim); - objar.Add(mEta); - objar.Add(mEtaVsPt); + for (int i = 0; i < matchType::SIZE; ++i) { + objar.Add(mPtNum[i]); + objar.Add(mPtDen[i]); + objar.Add(mFractionITSTPCmatch[i]); + + objar.Add(mPtNum_noEta0[i]); + objar.Add(mPtDen_noEta0[i]); + objar.Add(mFractionITSTPCmatch_noEta0[i]); + + objar.Add(mPtPhysPrimNum[i]); + objar.Add(mPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim[i]); + + objar.Add(mPhiNum[i]); + objar.Add(mPhiDen[i]); + objar.Add(mFractionITSTPCmatchPhi[i]); + + if (mUseTrkPID) { // Vs Tracking PID hypothesis + for (int j = 0; j < o2::track::PID::NIDs; ++j) { + // Pt + objar.Add(mPtNumVsTrkPID[i][j]); + objar.Add(mPtDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPtVsTrkPID[i][j]); + // Phi + objar.Add(mPhiNumVsTrkPID[i][j]); + objar.Add(mPhiDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchPhiVsTrkPID[i][j]); + // Eta + objar.Add(mEtaNumVsTrkPID[i][j]); + objar.Add(mEtaDenVsTrkPID[i][j]); + objar.Add(mFractionITSTPCmatchEtaVsTrkPID[i][j]); + } + } + + objar.Add(mPhiPhysPrimNum[i]); + objar.Add(mPhiPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhiPhysPrim[i]); + + objar.Add(mPhiVsPtNum[i]); + objar.Add(mPhiVsPtDen[i]); + objar.Add(mFractionITSTPCmatchPhiVsPt[i]); + + objar.Add(mEtaNum[i]); + objar.Add(mEtaDen[i]); + objar.Add(mFractionITSTPCmatchEta[i]); + + objar.Add(mEtaPhysPrimNum[i]); + objar.Add(mEtaPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchEtaPhysPrim[i]); + + objar.Add(mEtaVsPtNum[i]); + objar.Add(mEtaVsPtDen[i]); + objar.Add(mFractionITSTPCmatchEtaVsPt[i]); + + objar.Add(mClsVsPtNum[i]); + objar.Add(mClsVsPtDen[i]); + objar.Add(mFractionITSTPCmatchClsVsPt[i]); + + objar.Add(mChi2VsPtNum[i]); + objar.Add(mChi2VsPtDen[i]); + objar.Add(mFractionITSTPCmatchChi2VsPt[i]); + + objar.Add(m1OverPtNum[i]); + objar.Add(m1OverPtDen[i]); + objar.Add(mFractionITSTPCmatch1OverPt[i]); + + objar.Add(m1OverPtPhysPrimNum[i]); + objar.Add(m1OverPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } objar.Add(mChi2Matching); objar.Add(mChi2Refit); objar.Add(mTimeResVsPt); - objar.Add(mEtaPhysPrim); - objar.Add(mEtaTPC); - objar.Add(mEtaVsPtTPC); - objar.Add(mEtaTPCPhysPrim); objar.Add(mResidualPt); objar.Add(mResidualPhi); objar.Add(mResidualEta); + objar.Add(mDCAr); + objar.Add(mDCArVsPtNum); + objar.Add(mDCArVsPtDen); + objar.Add(mFractionITSTPCmatchDCArVsPt); } diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index a8da2c4865401..6a3486dd12044 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -46,18 +46,28 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "TOFBase/Utils.h" +#ifdef WITH_OPENMP +#include +#endif + using namespace o2::globaltracking; using evGIdx = o2::dataformats::EvIndex; using trkType = o2::dataformats::MatchInfoTOFReco::TrackType; using Cluster = o2::tof::Cluster; using GTrackID = o2::dataformats::GlobalTrackID; using timeEst = o2::dataformats::TimeStampWithError; +using TrackTunePar = o2::globaltracking::TrackTuneParams; + +bool MatchTOF::mHasFillScheme = false; +bool MatchTOF::mFillScheme[o2::constants::lhc::LHCMaxBunches] = {0}; ClassImp(MatchTOF); //______________________________________________ -void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) +void MatchTOF::run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit) { + mFirstTForbit = firstTForbit; + if (!mMatchParams) { mMatchParams = &o2::globaltracking::MatchTOFParams::Instance(); mSigmaTimeCut = mMatchParams->nsigmaTimeCut; @@ -79,23 +89,24 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) mOutTOFLabels[i].clear(); } - for (int i = 0; i < trkType::SIZE; i++) { - mTracksWork[i].clear(); - mTrackGid[i].clear(); - } for (int it = 0; it < trkType::SIZE; it++) { - mMatchedTracksIndex[it].clear(); - mLTinfos[it].clear(); - if (mMCTruthON) { - mTracksLblWork[it].clear(); - } - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksIndex[sec][it].clear(); + mTracksWork[sec][it].clear(); + mTrackGid[sec][it].clear(); + mLTinfos[sec][it].clear(); + if (mMCTruthON) { + mTracksLblWork[sec][it].clear(); + } mTracksSectIndexCache[it][sec].clear(); + mTracksSeed[it][sec].clear(); } } - mSideTPC.clear(); - mExtraTPCFwdTime.clear(); + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mSideTPC[sec].clear(); + mExtraTPCFwdTime[sec].clear(); + } mTimerTot.Start(); bool isPrepareTOFClusters = prepareTOFClusters(); @@ -122,32 +133,46 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) mTimerTot.Start(); std::array nMatches = {0}; - for (int sec = o2::constants::math::NSectors; sec--;) { - mMatchedTracksPairs.clear(); // new sector - LOG(debug) << "Doing matching for sector " << sec << "..."; - if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { - mTimerMatchITSTPC.Start(sec == o2::constants::math::NSectors - 1); + // run matching (this can be multi-thread) + mTimerMatchITSTPC.Start(); + + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksPairsSec[sec].clear(); // new sector + } + + o2::tof::Geo::Init(); + + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { doMatching(sec); - mTimerMatchITSTPC.Stop(); } - if (mIsTPCused) { - mTimerMatchTPC.Start(sec == o2::constants::math::NSectors - 1); + } + mTimerMatchITSTPC.Stop(); + + mTimerMatchTPC.Start(); + if (mIsTPCused) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { doMatchingForTPC(sec); - mTimerMatchTPC.Stop(); } + } + mTimerMatchTPC.Stop(); + // finalize + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { if (mStoreMatchable) { - // fill per sector - mMatchedTracksPairsSec[sec].clear(); - mMatchedTracksPairsSec[sec].insert(mMatchedTracksPairsSec[sec].end(), mMatchedTracksPairs.begin(), mMatchedTracksPairs.end()); - // if MC check if good or fake matches if (mMCTruthON) { for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { int trkType = (int)matchingPair.getTrackType(); int itrk = matchingPair.getIdLocal(); const auto& labelsTOF = mTOFClusLabels->getLabels(matchingPair.getTOFClIndex()); - const auto& labelTrack = mTracksLblWork[trkType][itrk]; + const auto& labelTrack = mTracksLblWork[sec][trkType][itrk]; // we have not found the track label among those associated to the TOF cluster --> fake match! We will associate the label of the main channel, but negative bool fake = true; @@ -160,15 +185,50 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) matchingPair.setFakeMatch(); } } + } else { + for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { + int bct0 = int((matchingPair.getSignal() - matchingPair.getLTIntegralOut().getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = matchingPair.getSignal() - bct0 * Geo::BC_TIME_INPS; + if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(2)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(3)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(4)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(0)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(1)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(5)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(6)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(7)) < 600) { + } else if (std::abs(tof - matchingPair.getLTIntegralOut().getTOF(8)) < 600) { + } else { // no pion, kaon, proton, electron, muon, deuteron, triton, 3He, 4He + matchingPair.setFakeMatch(); + } + } } } LOG(debug) << "...done. Now check the best matches"; - nMatches[sec] = mMatchedTracksPairs.size(); - selectBestMatches(); + nMatches[sec] = mMatchedTracksPairsSec[sec].size(); + selectBestMatches(sec); + + if (mStoreMatchable) { + for (auto& matchingPair : mMatchedTracksPairsSec[sec]) { + trkType trkTypeSplitted = trkType::TPC; + auto sourceID = matchingPair.getTrackRef().getSource(); + if (sourceID == o2::dataformats::GlobalTrackID::ITSTPC) { + trkTypeSplitted = trkType::ITSTPC; + } else if (sourceID == o2::dataformats::GlobalTrackID::TPCTRD) { + trkTypeSplitted = trkType::TPCTRD; + } else if (sourceID == o2::dataformats::GlobalTrackID::ITSTPCTRD) { + trkTypeSplitted = trkType::ITSTPCTRD; + } + matchingPair.setTrackType(trkTypeSplitted); + } + } } std::string nMatchesStr = "Number of pairs matched per sector: "; - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { nMatchesStr += fmt::format("{} : {} ; ", sec, nMatches[sec]); } LOG(info) << nMatchesStr; @@ -356,24 +416,86 @@ bool MatchTOF::prepareTPCData() }; mRecoCont->createTracksVariadic(creator); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + if (mIsTPCused) { + propagateTPCTracks(sec); + } + if (mIsITSTPCused || mIsTPCTRDused || mIsITSTPCTRDused) { + propagateConstrTracks(sec); + } + } + + // re-assign tracks which change sector (no multi-threading) + std::array globalPos; + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + if (mIsTPCused) { + for (auto& it : mTracksSeed[trkType::UNCONS][sec]) { + auto& pair = mTracksWork[sec][trkType::UNCONS][it]; + auto& trc = pair.first; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + int itnew = mTracksWork[sector][trkType::UNCONS].size(); + + mSideTPC[sector].push_back(mSideTPC[sec][it]); + mExtraTPCFwdTime[sector].push_back(mExtraTPCFwdTime[sec][it]); + mTracksWork[sector][trkType::UNCONS].emplace_back(pair); + mTrackGid[sector][trkType::UNCONS].emplace_back(mTrackGid[sec][trkType::UNCONS][it]); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::UNCONS].emplace_back(mTracksLblWork[sec][trkType::UNCONS][it]); + } + mLTinfos[sector][trkType::UNCONS].emplace_back(mLTinfos[sec][trkType::UNCONS][it]); + mVZtpcOnly[sector].push_back(mVZtpcOnly[sec][it]); + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(itnew); + } + } + + for (auto& it : mTracksSeed[trkType::CONSTR][sec]) { + auto& pair = mTracksWork[sec][trkType::CONSTR][it]; + auto& trc = pair.first; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + int itnew = mTracksWork[sector][trkType::CONSTR].size(); + + mTracksWork[sector][trkType::CONSTR].emplace_back(pair); + mTrackGid[sector][trkType::CONSTR].emplace_back(mTrackGid[sec][trkType::CONSTR][it]); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::CONSTR].emplace_back(mTracksLblWork[sec][trkType::CONSTR][it]); + } + mLTinfos[sector][trkType::CONSTR].emplace_back(mLTinfos[sec][trkType::CONSTR][it]); + mTracksSectIndexCache[trkType::CONSTR][sector].push_back(itnew); + } + } + for (int it = 0; it < trkType::SIZE; it++) { - mMatchedTracksIndex[it].resize(mTracksWork[it].size()); - std::fill(mMatchedTracksIndex[it].begin(), mMatchedTracksIndex[it].end(), -1); // initializing all to -1 + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { + mMatchedTracksIndex[sec][it].resize(mTracksWork[sec][it].size()); + std::fill(mMatchedTracksIndex[sec][it].begin(), mMatchedTracksIndex[sec][it].end(), -1); // initializing all to -1 + } } if (mIsTPCused) { LOG(debug) << "Number of UNCONSTRAINED tracks that failed to be propagated to TOF = " << mNotPropagatedToTOF[trkType::UNCONS]; // sort tracks in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTracksSectIndexCache[trkType::UNCONS][sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; if (!indexCache.size()) { continue; } - std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { - auto& trcA = mTracksWork[trkType::UNCONS][a].second; - auto& trcB = mTracksWork[trkType::UNCONS][b].second; + std::sort(indexCache.begin(), indexCache.end(), [this, sec](int a, int b) { + auto& trcA = mTracksWork[sec][trkType::UNCONS][a].second; + auto& trcB = mTracksWork[sec][trkType::UNCONS][b].second; return ((trcA.getTimeStamp() - trcA.getTimeStampError()) - (trcB.getTimeStamp() - trcB.getTimeStampError()) < 0.); }); } // loop over tracks of single sector @@ -382,22 +504,121 @@ bool MatchTOF::prepareTPCData() LOG(debug) << "Number of CONSTRAINED tracks that failed to be propagated to TOF = " << mNotPropagatedToTOF[trkType::CONSTR]; // sort tracks in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNlanes) +#endif + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTracksSectIndexCache[trkType::CONSTR][sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " tracks"; if (!indexCache.size()) { continue; } - std::sort(indexCache.begin(), indexCache.end(), [this](int a, int b) { - auto& trcA = mTracksWork[trkType::CONSTR][a].second; - auto& trcB = mTracksWork[trkType::CONSTR][b].second; + std::sort(indexCache.begin(), indexCache.end(), [this, sec](int a, int b) { + auto& trcA = mTracksWork[sec][trkType::CONSTR][a].second; + auto& trcB = mTracksWork[sec][trkType::CONSTR][b].second; return ((trcA.getTimeStamp() - mSigmaTimeCut * trcA.getTimeStampError()) - (trcB.getTimeStamp() - mSigmaTimeCut * trcB.getTimeStampError()) < 0.); }); } // loop over tracks of single sector } + if (mRecoCont->inputsTPCclusters) { + mTPCClusterIdxStruct = &mRecoCont->inputsTPCclusters->clusterIndex; + mTPCTracksArray = mRecoCont->getTPCTracks(); + mTPCTrackClusIdx = mRecoCont->getTPCTracksClusterRefs(); + mTPCRefitterShMap = mRecoCont->clusterShMapTPC; + mTPCRefitterOccMap = mRecoCont->occupancyMapTPC; + } + return true; } + +//______________________________________________ +void MatchTOF::propagateTPCTracks(int sec) +{ + auto& trkWork = mTracksWork[sec][trkType::UNCONS]; + + for (int it = 0; it < trkWork.size(); it++) { + o2::track::TrackParCov& trc = trkWork[it].first; + + o2::track::TrackLTIntegral& intLT0 = mLTinfos[sec][trkType::UNCONS][it]; + + const auto& trackTune = TrackTuneParams::Instance(); + if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level + if (trackTune.useTPCOuterCorr) { + trc.updateParams(trackTune.tpcParOuter); + } + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already man> + trc.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); + } + } + if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + + if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { + if (!propagateToRefXWithoutCov(trc, o2::constants::geom::XTPCOuterRef, 10, mBz) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagat> + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + } + + o2::base::Propagator::Instance()->estimateLTFast(intLT0, trc); + + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix w> + mNotPropagatedToTOF[trkType::UNCONS]++; + continue; + } + + // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); + // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, ti> + + std::array globalPos; + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + if (sector == sec) { + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); + } else { + mTracksSeed[trkType::UNCONS][sec].push_back(it); // to be moved to another sector + } + } +} +//______________________________________________ +void MatchTOF::propagateConstrTracks(int sec) +{ + std::array globalPos; + + auto& trkWork = mTracksWork[sec][trkType::CONSTR]; + + for (int it = 0; it < trkWork.size(); it++) { + o2::track::TrackParCov& trc = trkWork[it].first; + o2::track::TrackLTIntegral& intLT0 = mLTinfos[sec][trkType::CONSTR][it]; + + // propagate to matching Xref + if (!propagateToRefXWithoutCov(trc, mXRef, 2, mBz)) { // we first propagate to 371 cm without considering the covariance matrix + mNotPropagatedToTOF[trkType::CONSTR]++; + continue; + } + + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked;> + mNotPropagatedToTOF[trkType::CONSTR]++; + continue; + } + + trc.getXYZGlo(globalPos); + + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + if (sector == sec) { + mTracksSectIndexCache[trkType::CONSTR][sector].push_back(it); + } else { + mTracksSeed[trkType::CONSTR][sec].push_back(it); // to be moved to another sector + } + } +} //______________________________________________ void MatchTOF::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) { @@ -413,44 +634,19 @@ void MatchTOF::addITSTPCSeed(const o2::dataformats::TrackTPCITS& _tr, o2::datafo void MatchTOF::addConstrainedSeed(o2::track::TrackParCov& trc, o2::dataformats::GlobalTrackID srcGID, o2::track::TrackLTIntegral intLT0, timeEst timeMUS) { std::array globalPos; - // current track index - int it = mTracksWork[trkType::CONSTR].size(); - - // propagate to matching Xref - trc.getXYZGlo(globalPos); - LOG(debug) << "Global coordinates Before propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; - LOG(debug) << "Radius xy Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); - LOG(debug) << "Radius xyz Before propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); - if (!propagateToRefXWithoutCov(trc, mXRef, 2, mBz)) { // we first propagate to 371 cm without considering the covariance matrix - mNotPropagatedToTOF[trkType::CONSTR]++; - return; - } - - // the "rough" propagation worked; now we can propagate considering also the cov matrix - if (!propagateToRefX(trc, mXRef, 2, intLT0) || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> - mNotPropagatedToTOF[trkType::CONSTR]++; - return; - } - trc.getXYZGlo(globalPos); - LOG(debug) << "Global coordinates After propagating to 371 cm: globalPos[0] = " << globalPos[0] << ", globalPos[1] = " << globalPos[1] << ", globalPos[2] = " << globalPos[2]; - LOG(debug) << "Radius xy After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1]); - LOG(debug) << "Radius xyz After propagating to 371 cm = " << TMath::Sqrt(globalPos[0] * globalPos[0] + globalPos[1] * globalPos[1] + globalPos[2] * globalPos[2]); - LOG(debug) << "The track will go to sector " << o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); - int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); // create working copy of track param - mTracksWork[trkType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); - mTrackGid[trkType::CONSTR].emplace_back(srcGID); - mLTinfos[trkType::CONSTR].emplace_back(intLT0); + mTracksWork[sector][trkType::CONSTR].emplace_back(std::make_pair(trc, timeMUS)); + mTrackGid[sector][trkType::CONSTR].emplace_back(srcGID); + mLTinfos[sector][trkType::CONSTR].emplace_back(intLT0); if (mMCTruthON) { - mTracksLblWork[trkType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); + mTracksLblWork[sector][trkType::CONSTR].emplace_back(mRecoCont->getTPCITSTrackMCLabel(srcGID)); } - mTracksSectIndexCache[trkType::CONSTR][sector].push_back(it); //delete trc; // Check: is this needed? } //______________________________________________ void MatchTOF::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalTrackID srcGID, float time0, float terr) @@ -466,7 +662,6 @@ void MatchTOF::addTRDSeed(const o2::trd::TrackTRD& _tr, o2::dataformats::GlobalT auto trc = _tr.getOuterParam(); o2::track::TrackLTIntegral intLT0 = _tr.getLTIntegralOut(); - ; // empty for the moment // o2::dataformats::TimeStampWithError timeEst ts(time0, terr + mExtraTimeToleranceTRD); @@ -480,9 +675,6 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT std::array globalPos; - // current track index - int it = mTracksWork[trkType::UNCONS].size(); - // create working copy of track param timeEst timeInfo; // set @@ -491,65 +683,71 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT extraErr = 100; } + float trackTime0 = _tr.getTime0() * mTPCTBinMUS; + timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); + // timeInfo.setTimeStampError(trackTime0 - time0 + terr + extraErr); + timeInfo.setTimeStamp(trackTime0); + auto trc = _tr.getOuterParam(); - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); - if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level - if (trackTune.useTPCOuterCorr) { - trc.updateParams(trackTune.tpcParOuter); - } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already manipulated - trc.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); - } - } - if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri - mNotPropagatedToTOF[trkType::UNCONS]++; - return; - } + trc.getXYZGlo(globalPos); - o2::track::TrackLTIntegral intLT0; //mTPCTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + + mSideTPC[sector].push_back(_tr.hasASideClustersOnly() ? 1 : (_tr.hasCSideClustersOnly() ? -1 : 0)); + mExtraTPCFwdTime[sector].push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); + // mExtraTPCFwdTime[sector].push_back(time0 + terr - trackTime0 + extraErr); + mTracksWork[sector][trkType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); + mTrackGid[sector][trkType::UNCONS].emplace_back(srcGID); + + if (mMCTruthON) { + mTracksLblWork[sector][trkType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); + } + o2::track::TrackLTIntegral intLT0; // mTPCTracksWork.back().getLTIntegralOut(); // we get the integrated length from TPC-ITC outward propagation // compute track length up to now - o2::base::Propagator::Instance()->estimateLTFast(intLT0, trc); + mLTinfos[sector][trkType::UNCONS].emplace_back(intLT0); + float vz0 = _tr.getZAt(0, mBz); + if (std::abs(vz0) > 9000) { + vz0 = _tr.getZ() - _tr.getX() * _tr.getTgl(); + } + mVZtpcOnly[sector].push_back(vz0); - if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { - if (!propagateToRefX(trc, o2::constants::geom::XTPCOuterRef, 10, intLT0) || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happ + /* + const auto& trackTune = TrackTuneParams::Instance(); + if (!trackTune.sourceLevelTPC) { // correct only if TPC track was not corrected at the source level + if (trackTune.useTPCOuterCorr) { + trc.updateParams(trackTune.tpcParOuter); + } + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { // only TRD-refitted track have cov.matrix already manipulated + trc.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); + } + } + if (!propagateToRefXWithoutCov(trc, mXRef, 10, mBz)) { // we first propagate to 371 cm without considering the covariance matri mNotPropagatedToTOF[trkType::UNCONS]++; return; } - } - - // the "rough" propagation worked; now we can propagate considering also the cov matrix - if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> - mNotPropagatedToTOF[trkType::UNCONS]++; - return; - } - - float trackTime0 = _tr.getTime0() * mTPCTBinMUS - mTPCDriftTimeOffset; - - timeInfo.setTimeStampError((_tr.getDeltaTBwd() + 5) * mTPCTBinMUS + extraErr); - mExtraTPCFwdTime.push_back((_tr.getDeltaTFwd() + 5) * mTPCTBinMUS + extraErr); - - // timeInfo.setTimeStampError(trackTime0 - time0 + terr + extraErr); - // mExtraTPCFwdTime.push_back(time0 + terr - trackTime0 + extraErr); - timeInfo.setTimeStamp(trackTime0); - mSideTPC.push_back(_tr.hasASideClustersOnly() ? 1 : (_tr.hasCSideClustersOnly() ? -1 : 0)); - - // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); - // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, time0 + terr - trackTime0); + if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { + if (!propagateToRefX(trc, o2::constants::geom::XTPCOuterRef, 10, intLT0) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happ + mNotPropagatedToTOF[trkType::UNCONS]++; + return; + } + } - trc.getXYZGlo(globalPos); - int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); + // the "rough" propagation worked; now we can propagate considering also the cov matrix + if (!propagateToRefX(trc, mXRef, 2, intLT0)) { // || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix worked; CHECK: can it happen that it does not if the prop> + mNotPropagatedToTOF[trkType::UNCONS]++; + return; + } - mTracksWork[trkType::UNCONS].emplace_back(std::make_pair(trc, timeInfo)); - mTrackGid[trkType::UNCONS].emplace_back(srcGID); + // printf("time0 %f -> %f (diff = %f, err = %f)\n",trackTime0, time0, trackTime0 - time0, terr); + // printf("time errors %f,%f -> %f,%f\n",(_tr.getDeltaTBwd() + 5) * mTPCTBinMUS,(_tr.getDeltaTFwd() + 5) * mTPCTBinMUS,trackTime0 - time0 + terr, time0 + terr - trackTime0); - if (mMCTruthON) { - mTracksLblWork[trkType::UNCONS].emplace_back(mRecoCont->getTPCTrackMCLabel(srcGID)); - } - mLTinfos[trkType::UNCONS].emplace_back(intLT0); + trc.getXYZGlo(globalPos); + int sector = o2::math_utils::angle2Sector(TMath::ATan2(globalPos[1], globalPos[0])); - mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); - //delete trc; // Check: is this needed? + mTracksSectIndexCache[trkType::UNCONS][sector].push_back(it); + */ + // delete trc; // Check: is this needed? } //______________________________________________ bool MatchTOF::prepareTOFClusters() @@ -568,7 +766,7 @@ bool MatchTOF::prepareTOFClusters() // mTOFClusLblWork.reserve(mNumOfClusters); // } - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { mTOFClusSectIndexCache[sec].clear(); //mTOFClusSectIndexCache[sec].reserve(100 + 1.2 * mNumOfClusters / o2::constants::math::NSectors); // we cannot do this, we don't have mNumOfClusters yet } @@ -589,7 +787,7 @@ bool MatchTOF::prepareTOFClusters() } // sort clusters in each sector according to their time (increasing in time) - for (int sec = o2::constants::math::NSectors; sec--;) { + for (int sec = o2::constants::math::NSectors - 1; sec > -1; sec--) { auto& indexCache = mTOFClusSectIndexCache[sec]; LOG(debug) << "Sorting sector" << sec << " | " << indexCache.size() << " TOF clusters"; if (!indexCache.size()) { @@ -642,15 +840,18 @@ void MatchTOF::doMatching(int sec) nStepsInsideSameStrip[ii] = 0; } int nStripsCrossedInPropagation = 0; // how many strips were hit during the propagation - auto& trackWork = mTracksWork[type][cacheTrk[itrk]]; + auto& trackWork = mTracksWork[sec][type][cacheTrk[itrk]]; auto& trefTrk = trackWork.first; - auto& intLT = mLTinfos[type][cacheTrk[itrk]]; + float pt = trefTrk.getPt(); + auto& intLT = mLTinfos[sec][type][cacheTrk[itrk]]; float timeShift = intLT.getL() * 33.35641; // integrated time for 0.75 beta particles in ps, to take into account the t.o.f. delay with respect the interaction BC // using beta=0.75 to cover beta range [0.59 , 1.04] also for a 8 m track lenght with a 10 ns track resolution (TRD) // Printf("intLT (before doing anything): length = %f, time (Pion) = %f", intLT.getL(), intLT.getTOF(o2::track::PID::Pion)); float minTrkTime = (trackWork.second.getTimeStamp() - mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps float maxTrkTime = (trackWork.second.getTimeStamp() + mSigmaTimeCut * trackWork.second.getTimeStampError()) * 1.E6 + timeShift + 100E3; // maximum time in ps + 100 ns for slow tracks (beta->0.2) + const float sqrt12inv = 1. / sqrt(12.); + float resT = (trackWork.second.getTimeStampError() + 100E-3) * sqrt12inv; int istep = 1; // number of steps float step = 1.0; // step size in cm @@ -731,6 +932,44 @@ void MatchTOF::doMatching(int sec) } //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[nStripsCrossedInPropagation - 1] == 0) { + // fine propagation inside the strip -> 1 mm step + trkLTInt[nStripsCrossedInPropagation - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[nStripsCrossedInPropagation - 1], -deltaPosTemp[1]); + detId[nStripsCrossedInPropagation - 1][0] = detIdTemp[0]; detId[nStripsCrossedInPropagation - 1][1] = detIdTemp[1]; detId[nStripsCrossedInPropagation - 1][2] = detIdTemp[2]; @@ -739,16 +978,18 @@ void MatchTOF::doMatching(int sec) deltaPos[nStripsCrossedInPropagation - 1][0] = deltaPosTemp[0]; deltaPos[nStripsCrossedInPropagation - 1][1] = deltaPosTemp[1]; deltaPos[nStripsCrossedInPropagation - 1][2] = deltaPosTemp[2]; - trkLTInt[nStripsCrossedInPropagation - 1] = intLT; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those deltaPos[nStripsCrossedInPropagation - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[nStripsCrossedInPropagation - 1][4]) * Geo::XPAD; // residual in x deltaPos[nStripsCrossedInPropagation - 1][1] += deltaPosTemp[1]; // residual in y deltaPos[nStripsCrossedInPropagation - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[nStripsCrossedInPropagation - 1][3]) * Geo::ZPAD; // residual in z nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]++; } + */ } for (Int_t imatch = 0; imatch < nStripsCrossedInPropagation; imatch++) { @@ -822,12 +1063,52 @@ void MatchTOF::doMatching(int sec) int eventIdTOF; int sourceIdTOF; for (auto iPropagation = 0; iPropagation < nStripsCrossedInPropagation; iPropagation++) { + int bct0 = int((mTOFClusWork[cacheTOF[itof]].getTime() - trkLTInt[iPropagation].getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = mTOFClusWork[cacheTOF[itof]].getTime() - bct0 * Geo::BC_TIME_INPS; + if (tof - trkLTInt[iPropagation].getTOF(6) > 2000) { // reject tracks slower than triton + continue; + } + float cosangle = TMath::Cos(Geo::getAngles(indices[1], indices[2]) * TMath::DegToRad()); + const float errXinvMin = 1. / (mMatchParams->maxResX * mMatchParams->maxResX); + const float errZinvMin = 1. / (mMatchParams->maxResZ * mMatchParams->maxResZ); + float errXinv2 = 1. / (trefTrk.getSigmaY2()); + float errZinv2 = 1. / (trefTrk.getSigmaZ2() * cosangle); // should be valid only at eta=0 + + if (errXinv2 < errXinvMin) { + errXinv2 = errXinvMin; + } + if (errZinv2 < errZinvMin) { + errZinv2 = errZinvMin; + } + LOG(debug) << "TOF Cluster [" << itof << ", " << cacheTOF[itof] << "]: indices = " << indices[0] << ", " << indices[1] << ", " << indices[2] << ", " << indices[3] << ", " << indices[4]; LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[iPropagation][0] << ", " << detId[iPropagation][1] << ", " << detId[iPropagation][2] << ", " << detId[iPropagation][3] << ", " << detId[iPropagation][4]; float resX = deltaPos[iPropagation][0] - (indices[4] - detId[iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[iPropagation][2] - (indices[3] - detId[iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[iPropagation][1]; + float resXor = resX; + float resZor = resZ; float res = TMath::Sqrt(resX * resX + resZ * resZ); + if (resX < -1.25) { // distance from closest border + resX += 1.25; + } else if (resX > 1.25) { + resX -= 1.25; + } else { + resX = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + + if (resZ < -1.75) { // distance from closest border + resZ += 1.75; + } else if (resZ > 1.75) { + resZ -= 1.75; + } else { + resZ = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + LOG(debug) << "resX = " << resX << ", resZ = " << resZ << ", res = " << res; if (indices[0] != detId[iPropagation][0]) { continue; @@ -838,14 +1119,20 @@ void MatchTOF::doMatching(int sec) if (indices[2] != detId[iPropagation][2]) { continue; } - float chi2 = res; // TODO: take into account also the time! + float chi2 = 0.5 * (resX * resX * errXinv2 + resZ * resZ * errZinv2); // TODO: take into account also the time! - if (res < mSpaceTolerance) { // matching ok! + if (res < mSpaceTolerance && chi2 < mMatchParams->maxChi2) { // matching ok! LOG(debug) << "MATCHING FOUND: We have a match! between track " << mTracksSectIndexCache[type][sec][itrk] << " and TOF cluster " << mTOFClusSectIndexCache[indices[0]][itof]; foundCluster = true; // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairs.emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, 0., resX, resZ); // subracting 100 ns to max track which was artificially added + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[iPropagation], mTrackGid[sec][type][cacheTrk[itrk]], type, (trefTOF.getTime() - (minTrkTime + maxTrkTime - 100E3) * 0.5) * 1E-6, trefTOF.getZ(), resXor, resZor, resY); // subracting 100 ns to max track which was artificially added + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResT(resT); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setVz(0.0); // not needed for constrained tracks + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setChannel(mainChannel); } } } @@ -885,13 +1172,15 @@ void MatchTOF::doMatchingForTPC(int sec) std::vector> trkLTInt; std::vector, 2>> deltaPos; std::vector> nStepsInsideSameStrip; + std::vector> Zshift; LOG(debug) << "Trying to match %d tracks" << cacheTrk.size(); for (int itrk = 0; itrk < cacheTrk.size(); itrk++) { - auto& trackWork = mTracksWork[trkType::UNCONS][cacheTrk[itrk]]; + auto& trackWork = mTracksWork[sec][trkType::UNCONS][cacheTrk[itrk]]; auto& trefTrk = trackWork.first; - auto& intLT = mLTinfos[trkType::UNCONS][cacheTrk[itrk]]; + float pt = trefTrk.getPt(); + auto& intLT = mLTinfos[sec][trkType::UNCONS][cacheTrk[itrk]]; float timeShift = intLT.getL() * 33.35641; // integrated time for 0.75 beta particles in ps, to take into account the t.o.f. delay with respect the interaction BC // using beta=0.75 to cover beta range [0.59 , 1.04] also for a 8 m track lenght with a 10 ns track resolution (TRD) @@ -899,11 +1188,14 @@ void MatchTOF::doMatchingForTPC(int sec) BCcand.clear(); nStripsCrossedInPropagation.clear(); - int side = mSideTPC[cacheTrk[itrk]]; + int side = mSideTPC[sec][cacheTrk[itrk]]; // look at BC candidates for the track - double minTrkTime = (trackWork.second.getTimeStamp() - trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps + double tpctime = trackWork.second.getTimeStamp(); // in mus + double minTrkTime = (tpctime - trackWork.second.getTimeStampError()) * 1.E6 + timeShift; // minimum time in ps minTrkTime = int(minTrkTime / BCgranularity) * BCgranularity; // align min to a BC - double maxTrkTime = (trackWork.second.getTimeStamp() + mExtraTPCFwdTime[cacheTrk[itrk]]) * 1.E6 + timeShift; // maximum time in ps + double maxTrkTime = (tpctime + mExtraTPCFwdTime[sec][cacheTrk[itrk]]) * 1.E6 + timeShift; // maximum time in ps + const float sqrt12inv = 1. / sqrt(12.); + float resT = (maxTrkTime - minTrkTime) * sqrt12inv; if (mIsCosmics) { for (double tBC = minTrkTime; tBC < maxTrkTime; tBC += BCgranularity) { @@ -954,6 +1246,8 @@ void MatchTOF::doMatchingForTPC(int sec) detId.reserve(BCcand.size()); trkLTInt.clear(); trkLTInt.reserve(BCcand.size()); + Zshift.clear(); + Zshift.reserve(BCcand.size()); deltaPos.clear(); deltaPos.reserve(BCcand.size()); nStepsInsideSameStrip.clear(); @@ -1016,6 +1310,8 @@ void MatchTOF::doMatchingForTPC(int sec) posFloat[2] = pos[2]; } + float ZshiftCurrent = posFloat[2] - pos[2]; + Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp, sec); if (detIdTemp[2] == -1) { @@ -1035,6 +1331,46 @@ void MatchTOF::doMatchingForTPC(int sec) //Printf("nStepsInsideSameStrip[nStripsCrossedInPropagation-1] = %d", nStepsInsideSameStrip[nStripsCrossedInPropagation - 1]); if (nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1] == 0) { + trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; + // temporary variables since propagation can fail + int detIdTemp2[5] = {0, 0, 0, 0, 0}; + float deltaPosTemp2[3] = {deltaPosTemp[0], deltaPosTemp[1], deltaPosTemp[2]}; + int nstep = 0; + const int maxnstep = 50; + float xStart = trefTrk.getX(); + float xStop = xStart; + trefTrk.getXYZGlo(pos); + for (int ii = 0; ii < 3; ii++) { // we need to change the type... + posFloat[ii] = pos[ii]; + } + + while (deltaPosTemp2[1] < -0.05 && detIdTemp2[2] != -1 && nstep < maxnstep) { // continuing propagation if dy is negative and we are still inside the strip volume + nstep++; + xStop += 0.1; + propagateToRefXWithoutCov(trefTrk, xStop, 0.1, mBz, posFloat); + + posFloat[2] += ZshiftCurrent; + + Geo::getPadDxDyDz(posFloat, detIdTemp2, deltaPosTemp2, sec); + if (detIdTemp2[2] != -1) { // if propation was succesful -> update params + float dx = deltaPosTemp2[0] - deltaPosTemp[0]; + float dy = deltaPosTemp2[1] - deltaPosTemp[1]; + float dz = deltaPosTemp2[2] - deltaPosTemp[2]; + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], sqrt(dx * dx + dy * dy + dz * dz)); + detIdTemp[0] = detIdTemp2[0]; + detIdTemp[1] = detIdTemp2[1]; + detIdTemp[2] = detIdTemp2[2]; + detIdTemp[3] = detIdTemp2[3]; + detIdTemp[4] = detIdTemp2[4]; + deltaPosTemp[0] = deltaPosTemp2[0]; + deltaPosTemp[1] = deltaPosTemp2[1]; + deltaPosTemp[2] = deltaPosTemp2[2]; + } + } + + // adjust accordingly to DeltaY + updateTL(trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1], -deltaPosTemp[1]); + detId[ibc][nStripsCrossedInPropagation[ibc] - 1][0] = detIdTemp[0]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = detIdTemp[1]; detId[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = detIdTemp[2]; @@ -1044,16 +1380,19 @@ void MatchTOF::doMatchingForTPC(int sec) deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] = deltaPosTemp[1]; deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] = deltaPosTemp[2]; - trkLTInt[ibc][nStripsCrossedInPropagation[ibc] - 1] = intLT; + Zshift[ibc][nStripsCrossedInPropagation[ibc] - 1] = ZshiftCurrent; // Printf("intLT (after matching to strip %d): length = %f, time (Pion) = %f", nStripsCrossedInPropagation - 1, trkLTInt[nStripsCrossedInPropagation - 1].getL(), trkLTInt[nStripsCrossedInPropagation - 1].getTOF(o2::track::PID::Pion)); nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; - } else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) - // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y - deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z - nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; } + /* // obsolete + else { // a further propagation step in the same strip -> update info (we sum up on all matching with strip - we will divide for the number of steps a bit below) + // N.B. the integrated length and time are taken (at least for now) from the first time we crossed the strip, so here we do nothing with those + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][0] += deltaPosTemp[0] + (detIdTemp[4] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][4]) * Geo::XPAD; // residual in x + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][1] += deltaPosTemp[1]; // residual in y + deltaPos[ibc][nStripsCrossedInPropagation[ibc] - 1][2] += deltaPosTemp[2] + (detIdTemp[3] - detId[ibc][nStripsCrossedInPropagation[ibc] - 1][3]) * Geo::ZPAD; // residual in z + nStepsInsideSameStrip[ibc][nStripsCrossedInPropagation[ibc] - 1]++; + } + */ } } for (int ibc = 0; ibc < BCcand.size(); ibc++) { @@ -1145,17 +1484,67 @@ void MatchTOF::doMatchingForTPC(int sec) continue; } + int bct0 = int((mTOFClusWork[cacheTOF[itof]].getTime() - trkLTInt[ibc][iPropagation].getTOF(0) + 5000) * Geo::BC_TIME_INPS_INV); // bc taken assuming speed of light (el) and 5 ns of margin + if (bct0 < 0) { // if negative time (it can happen at the beginng of the TF int was truncated per excess... adjusting) + bct0--; + } + float tof = mTOFClusWork[cacheTOF[itof]].getTime() - bct0 * Geo::BC_TIME_INPS; + if (tof - trkLTInt[ibc][iPropagation].getTOF(6) > 2000) { // reject tracks slower than triton + continue; + } + + if (mMatchParams->applyPIDcutTPConly) { // for TPC only tracks allowing possibility to apply a PID cut + if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(2)) < 2000) { // pion hypotesis + } else if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(3)) < 2000) { // kaon hypoteis + } else if (std::abs(tof - trkLTInt[ibc][iPropagation].getTOF(4)) < 2000) { // proton hypotesis + } else { // reject matching + continue; + } + } + + float cosangle = TMath::Cos(Geo::getAngles(indices[1], indices[2]) * TMath::DegToRad()); + const float errXinvMin = 1. / (mMatchParams->maxResX * mMatchParams->maxResX); + const float errZinvMin = 1. / (mMatchParams->maxResZ * mMatchParams->maxResZ); + float errXinv2 = 1. / (trefTrk.getSigmaY2()); + float errZinv2 = 1. / (trefTrk.getSigmaZ2() * cosangle); // should be valid only at eta=0 + + if (errXinv2 < errXinvMin) { + errXinv2 = errXinvMin; + } + if (errZinv2 < errZinvMin) { + errZinv2 = errZinvMin; + } + LOG(debug) << "TOF Cluster [" << itof << ", " << cacheTOF[itof] << "]: indices = " << indices[0] << ", " << indices[1] << ", " << indices[2] << ", " << indices[3] << ", " << indices[4]; LOG(debug) << "Propagated Track [" << itrk << "]: detId[" << iPropagation << "] = " << detId[ibc][iPropagation][0] << ", " << detId[ibc][iPropagation][1] << ", " << detId[ibc][iPropagation][2] << ", " << detId[ibc][iPropagation][3] << ", " << detId[ibc][iPropagation][4]; float resX = deltaPos[ibc][iPropagation][0] - (indices[4] - detId[ibc][iPropagation][4]) * Geo::XPAD + posCorr[0]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster float resZ = deltaPos[ibc][iPropagation][2] - (indices[3] - detId[ibc][iPropagation][3]) * Geo::ZPAD + posCorr[2]; // readjusting the residuals due to the fact that the propagation fell in a pad that was not exactly the one of the cluster + float resY = deltaPos[ibc][iPropagation][1]; if (BCcand[ibc] > bcClus) { resZ += (BCcand[ibc] - bcClus) * vdriftInBC * side; // add bc correction } else { resZ -= (bcClus - BCcand[ibc]) * vdriftInBC * side; } + float resXor = resX; + float resZor = resZ; float res = TMath::Sqrt(resX * resX + resZ * resZ); + if (resX < -1.25) { // distance from closest border + resX += 1.25; + } else if (resX > 1.25) { + resX -= 1.25; + } else { + resX = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + + if (resZ < -1.75) { // distance from closest border + resZ += 1.75; + } else if (resZ > 1.75) { + resZ -= 1.75; + } else { + resZ = 1E-3 / (pt + 1E-3); // high-pt should be favoured + } + if (indices[0] != detId[ibc][iPropagation][0]) { continue; } @@ -1167,14 +1556,21 @@ void MatchTOF::doMatchingForTPC(int sec) } LOG(debug) << "resX = " << resX << ", resZ = " << resZ << ", res = " << res; - float chi2 = mIsCosmics ? resX : res; // TODO: take into account also the time! + float chi2 = mIsCosmics ? resX : 0.5 * (resX * resX * errXinv2 + resZ * resZ * errZinv2); // TODO: take into account also the time! - if (res < mSpaceTolerance) { // matching ok! + if (res < mSpaceTolerance && chi2 < mMatchParams->maxChi2) { // matching ok! LOG(debug) << "MATCHING FOUND: We have a match! between track " << mTracksSectIndexCache[trkType::UNCONS][sec][itrk] << " and TOF cluster " << mTOFClusSectIndexCache[indices[0]][itof]; foundCluster = true; // set event indexes (to be checked) + int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - mMatchedTracksPairs.emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, resZ / mTPCVDrift * side, trefTOF.getZ(), resX, resZ); // TODO: check if this is correct! + mMatchedTracksPairsSec[sec].emplace_back(cacheTrk[itrk], eventIndexTOFCluster, mTOFClusWork[cacheTOF[itof]].getTime(), chi2, trkLTInt[ibc][iPropagation], mTrackGid[sec][trkType::UNCONS][cacheTrk[itrk]], trkType::UNCONS, trefTOF.getTime() * 1E-6 - tpctime, trefTOF.getZ(), resXor, resZor, resY); // TODO: check if this is correct! + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setPt(pt); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResX(sqrt(1. / errXinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResZ(sqrt(1. / errZinv2)); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setResT(resT); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setVz(mVZtpcOnly[sec][itrk] + Zshift[ibc][iPropagation]); + mMatchedTracksPairsSec[sec][mMatchedTracksPairsSec[sec].size() - 1].setChannel(mainChannel); } } } @@ -1183,24 +1579,48 @@ void MatchTOF::doMatchingForTPC(int sec) return; } //______________________________________________ -int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints) +int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit) { + const auto& FT0Params = o2::ft0::InteractionTag::Instance(); + + if ((!mHasFillScheme) && o2::tof::Utils::hasFillScheme()) { + mHasFillScheme = true; + for (int ibc = 0; ibc < o2::tof::Utils::getNinteractionBC(); ibc++) { + mFillScheme[o2::tof::Utils::getInteractionBC(ibc)] = true; + } + } + if (FITRecPoints.size() == 0) { return -1; } int index = -1; - int distMax = 5; // require bc distance below 5 + int distMax = 10; + bool bestQuality = false; // prioritize FT0 BC with good quality (FT0-AC + vertex) to remove umbiguity in Pb-Pb (not a strict cut because inefficient in pp) + const int distThr = 8; for (unsigned int i = 0; i < FITRecPoints.size(); i++) { + const auto& ft = FITRecPoints[i]; + if (!FT0Params.isSelected(ft)) { + continue; + } const o2::InteractionRecord ir = FITRecPoints[i].getInteractionRecord(); - int bct0 = ir.orbit * o2::constants::lhc::LHCMaxBunches + ir.bc; + if (mHasFillScheme && !mFillScheme[ir.bc]) { + continue; + } + bool quality = (std::abs(FITRecPoints[i].getCollisionTime(0)) < 1000 && std::abs(FITRecPoints[i].getVertex()) < 1000); + if (bestQuality && !quality) { // if current has no good quality and the one previoulsy selected has -> discard the current one + continue; + } + int bct0 = (ir.orbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; int dist = bc - bct0; - if (dist < 0 || dist > distMax) { + bool worseDistance = dist < 0 || dist > distThr || dist > distMax; + if (worseDistance) { // discard if BC is not in the proper range or it is worse than the one already found continue; } + bestQuality = quality; distMax = dist; index = i; } @@ -1208,13 +1628,13 @@ int MatchTOF::findFITIndex(int bc, const gsl::span& FI return index; } //______________________________________________ -void MatchTOF::selectBestMatches() +void MatchTOF::selectBestMatches(int sec) { if (mSetHighPurity) { - BestMatchesHP(mMatchedTracksPairs, mMatchedTracks, mMatchedTracksIndex, mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork, mOutTOFLabels); + BestMatchesHP(mMatchedTracksPairsSec[sec], mMatchedTracks, mMatchedTracksIndex[sec], mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork[sec], mOutTOFLabels); return; } - BestMatches(mMatchedTracksPairs, mMatchedTracks, mMatchedTracksIndex, mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mTracksWork, mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork, mOutTOFLabels, mMatchParams->calibMaxChi2); + BestMatches(mMatchedTracksPairsSec[sec], mMatchedTracks, mMatchedTracksIndex[sec], mMatchedClustersIndex, mFITRecPoints, mTOFClusWork, mTracksWork[sec], mCalibInfoTOF, mTimestamp, mMCTruthON, mTOFClusLabels, mTracksLblWork[sec], mOutTOFLabels, mMatchParams->calibMaxChi2); } //______________________________________________ void MatchTOF::BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2) @@ -1226,20 +1646,11 @@ void MatchTOF::BestMatches(std::vector& match int i = 0; // then we take discard the pairs if their track or cluster was already matched (since they are ordered in chi2, we will take the best matching) - for (const o2::dataformats::MatchInfoTOFReco& matchingPair : matchedTracksPairs) { + for (o2::dataformats::MatchInfoTOFReco& matchingPair : matchedTracksPairs) { int trkType = (int)matchingPair.getTrackType(); int itrk = matchingPair.getIdLocal(); - if (matchedTracksIndex[trkType][itrk] != -1) { // the track was already filled - continue; - } - if (matchedClustersIndex[matchingPair.getTOFClIndex()] != -1) { // the cluster was already filled - continue; - } - matchedTracksIndex[trkType][itrk] = matchedTracks[trkType].size(); // index of the MatchInfoTOF correspoding to this track - matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // index of the track that was matched to this cluster - int trkTypeSplitted = trkType; auto sourceID = matchingPair.getTrackRef().getSource(); if (sourceID == o2::dataformats::GlobalTrackID::TPCTRD) { @@ -1247,6 +1658,86 @@ void MatchTOF::BestMatches(std::vector& match } else if (sourceID == o2::dataformats::GlobalTrackID::ITSTPCTRD) { trkTypeSplitted = (int)trkType::ITSTPCTRD; } + + if (matchedClustersIndex[matchingPair.getTOFClIndex()] != -1) { // the cluster was already filled + continue; + } + if (matchedTracksIndex[trkType][itrk] != -1) { // the track was already filled + if (trkType) { // not applied for unconstrained tracks (trkType == 0) + // let's check if we can use both matching to improve TOF time + int pairInd = matchedTracksIndex[trkType][itrk]; + auto& prevMatching = matchedTracks[trkTypeSplitted][pairInd]; + + if (pairInd >= matchedTracks[trkTypeSplitted].size()) { + LOG(error) << "Pair index out of range when trying to update TOF time. This should not happen"; + continue; + } + + int istripOld = TOFClusWork[prevMatching.getTOFClIndex()].getMainContributingChannel() / o2::tof::Geo::NPADS; + int istripNew = TOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel() / o2::tof::Geo::NPADS; + + if (istripOld != istripNew) { // we accept it only if in a different strip + const o2::track::TrackLTIntegral& intLTnew = matchingPair.getLTIntegralOut(); + const o2::track::TrackLTIntegral& intLTold = prevMatching.getLTIntegralOut(); + + const float cinv = 1.E+10 / TMath::C(); // cm/ps + float deltaT = (intLTnew.getL() - intLTold.getL()) * cinv; + + float timeNew = TOFClusWork[matchingPair.getTOFClIndex()].getTime() - deltaT; + float timeOld = TOFClusWork[prevMatching.getTOFClIndex()].getTime(); + + if (std::abs(timeNew - timeOld) < 200) { + // update time information averaging the two (the second one corrected for the difference in the track length) + prevMatching.setSignal((timeNew + timeOld) * 0.5); + float geanttime = (TOFClusWork[matchingPair.getTOFClIndex()].getTgeant() + TOFClusWork[prevMatching.getTOFClIndex()].getTgeant() - deltaT * 1E-3) * 0.5; + double t0 = (TOFClusWork[matchingPair.getTOFClIndex()].getT0true() + TOFClusWork[prevMatching.getTOFClIndex()].getT0true()) * 0.5; + prevMatching.setTgeant(geanttime); + prevMatching.setT0true(t0); + prevMatching.setChi2(0); // flag such cases with chi2 equal to zero + matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // flag also the second cluster as already used + } + } + } + + continue; + } + matchedTracksIndex[trkType][itrk] = matchedTracks[trkTypeSplitted].size(); // index of the MatchInfoTOF correspoding to this track + matchedClustersIndex[matchingPair.getTOFClIndex()] = matchedTracksIndex[trkType][itrk]; // index of the track that was matched to this cluster + + matchingPair.setTgeant(TOFClusWork[matchingPair.getTOFClIndex()].getTgeant()); + matchingPair.setT0true(TOFClusWork[matchingPair.getTOFClIndex()].getT0true()); + + // let's check if cluster has multiple-hits (noferini) + const auto& tofcl = TOFClusWork[matchingPair.getTOFClIndex()]; + if (tofcl.getNumOfContributingChannels() > 1) { + // has an additional hit Up or Down (Z-dir) + matchingPair.setHitPatternUpDown(tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUp) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDown) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownRight)); + // has an additional hit Left or Right (X-dir) + matchingPair.setHitPatternLeftRight(tofcl.isAdditionalChannelSet(o2::tof::Cluster::kLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpLeft) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kDownRight) || + tofcl.isAdditionalChannelSet(o2::tof::Cluster::kUpRight)); + } + + // estimate collision time using FT0 info if available + ULong64_t bclongtofCal = (matchingPair.getSignal() - 10000) * o2::tof::Geo::BC_TIME_INPS_INV; + double t0Best = bclongtofCal * o2::tof::Geo::BC_TIME_INPS; // here just BC + float t0BestRes = 200; + if (FITRecPoints.size() > 0) { + int index = findFITIndex(bclongtofCal, FITRecPoints, mFirstTForbit); + if (index > -1 && FITRecPoints[index].isValidTime(1) && FITRecPoints[index].isValidTime(2)) { // require A and C + t0Best += FITRecPoints[index].getCollisionTime(0); + t0BestRes = 15; + } + } + matchingPair.setFT0Best(t0Best, t0BestRes); matchedTracks[trkTypeSplitted].push_back(matchingPair); // array of MatchInfoTOF // get fit info @@ -1254,14 +1745,15 @@ void MatchTOF::BestMatches(std::vector& match const o2::track::TrackLTIntegral& intLT = matchingPair.getLTIntegralOut(); - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } - } else { // move time to time in orbit to avoid loss of precision when truncating from double to float + } else if (!mIsFIT) { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); bcStarOrbit = (bcStarOrbit / o2::constants::lhc::LHCMaxBunches) * o2::constants::lhc::LHCMaxBunches; // truncation t0info = bcStarOrbit * o2::tof::Geo::BC_TIME_INPS; @@ -1280,7 +1772,14 @@ void MatchTOF::BestMatches(std::vector& match } int mask = 0; - float deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + float deltat; + + if (t0info > 0 && mIsFIT) { // FT0 BC found + deltat = TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion) - o2::tof::Geo::LATENCYWINDOW_IN_BC * o2::tof::Geo::BC_TIME_INPS; // latency subtracted + mask = 1 << 8; // only FT0 BC allowed (-> BC=0 since t0info already subtracted and assumed to be aligned to the true IntBC) + } else if (!mIsFIT) { + deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + } const o2::track::TrackParCov& trc = TracksWork[trkType][itrk].first; float pt = trc.getPt(); // from outer parameters! @@ -1301,7 +1800,7 @@ void MatchTOF::BestMatches(std::vector& match flags = flags | o2::dataformats::CalibInfoTOF::kMultiHit; } - if (matchingPair.getChi2() < calibMaxChi2) { // extra cut in ChiSquare for storing calib info + if (matchingPair.getChi2() < calibMaxChi2 && t0info > 0) { // extra cut in ChiSquare for storing calib info CalibInfoTOF.emplace_back(TOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel(), Timestamp / 1000 + int(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() * 1E-12), // add time stamp deltat, @@ -1394,12 +1893,13 @@ void MatchTOF::BestMatchesHP(std::vector& mat // get fit info double t0info = 0; - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } } else { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); @@ -1448,7 +1948,7 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st refReached = true; // we reached the 371cm reference } istep++; - if (fabs(trc.getY()) > trc.getX() * tanHalfSector) { // we are still in the same sector + if (std::abs(trc.getY()) > trc.getX() * tanHalfSector) { // we are still in the same sector // we need to rotate the track to go to the new sector //Printf("propagateToRefX: changing sector"); auto alphaNew = o2::math_utils::angle2Alpha(trc.getPhiPos()); @@ -1467,9 +1967,8 @@ bool MatchTOF::propagateToRefX(o2::track::TrackParCov& trc, float xRef, float st //Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trc.getSnp(), TMath::ASin(trc.getSnp())*TMath::RadToDeg()); return refReached && std::abs(trc.getSnp()) < 0.95; // Here we need to put MAXSNP } - //______________________________________________ -bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) +bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) { // propagate track to matching reference X without using the covariance matrix // we create the copy of the track in a TrackPar object (no cov matrix) @@ -1488,7 +1987,7 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef refReached = true; // we reached the 371cm reference } istep++; - if (fabs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector + if (std::abs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector // we need to rotate the track to go to the new sector //Printf("propagateToRefX: changing sector"); auto alphaNew = o2::math_utils::angle2Alpha(trcNoCov.getPhiPos()); @@ -1505,6 +2004,61 @@ bool MatchTOF::propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); //Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trcNoCov.getSnp(), TMath::ASin(trcNoCov.getSnp())*TMath::RadToDeg()); + return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && std::abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP +} +//______________________________________________ +void MatchTOF::updateTL(o2::track::TrackLTIntegral& intLT, float deltal) +{ + for (int i = 0; i < intLT.getNTOFs(); i++) { + float betainv = intLT.getTOF(i) / intLT.getL(); + intLT.setTOF(intLT.getTOF(i) + deltal * betainv, i); + } + intLT.setL(intLT.getL() + deltal); +} + +//______________________________________________ +bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField, float pos[3]) +{ + // propagate track to matching reference X without using the covariance matrix + // we create the copy of the track in a TrackPar object (no cov matrix) + o2::track::TrackPar trcNoCov(trc); + const float tanHalfSector = tan(o2::constants::math::SectorSpanRad / 2); + bool refReached = false; + float xStart = trcNoCov.getX(); + // the first propagation will be from 2m, if the track is not at least at 2m + if (xStart < 50.) { + xStart = 50.; + } + int istep = 1; + bool hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + while (hasPropagated) { + if (trcNoCov.getX() > xRef) { + refReached = true; // we reached the 371cm reference + } + istep++; + if (fabs(trcNoCov.getY()) > trcNoCov.getX() * tanHalfSector) { // we are still in the same sector + // we need to rotate the track to go to the new sector + // Printf("propagateToRefX: changing sector"); + auto alphaNew = o2::math_utils::angle2Alpha(trcNoCov.getPhiPos()); + if (!trcNoCov.rotateParam(alphaNew) != 0) { + // Printf("propagateToRefX: failed to rotate"); + break; // failed (this line is taken from MatchTPCITS and the following comment too: RS: check effect on matching tracks to neighbouring sector) + } + } + if (refReached) { + break; + } + hasPropagated = trcNoCov.propagateParamTo(xStart + istep * stepInCm, bzField); + } + // if (std::abs(trc.getSnp()) > MAXSNP) Printf("propagateToRefX: condition on snp not ok, returning false"); + // Printf("propagateToRefX: snp of teh track is %f (--> %f grad)", trcNoCov.getSnp(), TMath::ASin(trcNoCov.getSnp())*TMath::RadToDeg()); + + o2::track::TrackParametrization::dim3_t xyz; + trcNoCov.getXYZGlo(xyz); + pos[0] = xyz[0]; + pos[1] = xyz[1]; + pos[2] = xyz[2]; + return refReached && std::abs(trcNoCov.getSnp()) < 0.95 && TMath::Abs(trcNoCov.getZ()) < Geo::MAXHZTOF; // Here we need to put MAXSNP } @@ -1528,7 +2082,15 @@ void MatchTOF::updateTimeDependentParams() mTPCBin2Z = mTPCTBinMUS * mTPCVDrift; mBz = o2::base::Propagator::Instance()->getNominalBz(); - mMaxInvPt = abs(mBz) > 0.1 ? 1. / (abs(mBz) * 0.05) : 999.; + mMaxInvPt = std::abs(mBz) > 0.1 ? 1. / (std::abs(mBz) * 0.05) : 999.; + + const auto& trackTune = TrackTuneParams::Instance(); + float scale = mTPCCorrMapsHelper->getInstLumiCTP(); + if (scale < 0.f) { + scale = 0.f; + } + mCovDiagInner = trackTune.getCovInnerTotal(scale); + mCovDiagOuter = trackTune.getCovOuterTotal(scale); } //_________________________________________________________ @@ -1541,7 +2103,7 @@ bool MatchTOF::makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCT // correct the time of the track auto timeTOFMUS = (tofCl.getTime() - intLT.getTOF(tpcTrOrig.getPID())) * 1e-6; // tof time in \mus, FIXME: account for time of flight to R TOF - auto timeTOFTB = (timeTOFMUS + mTPCDriftTimeOffset) * mTPCTBinMUSInv; // TOF time in TPC timebins, including TPC time offset + auto timeTOFTB = timeTOFMUS * mTPCTBinMUSInv; // TOF time in TPC timebins, including TPC time offset auto deltaTBins = timeTOFTB - tpcTrOrig.getTime0(); // time shift in timeBins float timeErr = 0.010; // assume 10 ns error FIXME auto dzCorr = deltaTBins * mTPCBin2Z; @@ -1576,23 +2138,23 @@ bool MatchTOF::makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCT if (mTPCClusterIdxStruct) { // refit was requested float chi2 = 0; mTPCRefitter->setTrackReferenceX(o2::constants::geom::XTPCInnerRef); - if (mTPCRefitter->RefitTrackAsTrackParCov(trConstr, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, false, true) < 0) { // outward refit after resetting cov.mat. - LOG(debug) << "Inward Refit failed"; + if (mTPCRefitter->RefitTrackAsTrackParCov(trConstr, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, false, true) < 0) { // inward refit after resetting cov.mat. + LOGP(debug, "Inward Refit failed {}", trConstr.asString()); return false; } - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); // if needed, correct TPC track after inwar refit + const auto& trackTune = TrackTuneParams::Instance(); // if needed, correct TPC track after inward refit if (!trackTune.useTPCInnerCorr) { trConstr.updateParams(trackTune.tpcParInner); } - if (trackTune.tpcCovInnerType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - trConstr.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovInnerType != TrackTuneParams::AddCovType::Disable) { + trConstr.updateCov(mCovDiagInner, trackTune.tpcCovInnerType == TrackTuneParams::AddCovType::WithCorrelations); } trConstr.setChi2Refit(chi2); // mTPCRefitter->setTrackReferenceX(o2::constants::geom::XTPCOuterRef); if (mTPCRefitter->RefitTrackAsTrackParCov(trConstrOut, tpcTrOrig.getClusterRef(), timeTOFTB, &chi2, true, true) < 0) { // outward refit after resetting cov.mat. - LOG(debug) << "Outward refit failed"; + LOGP(debug, "Outward Refit failed {}", trConstrOut.asString()); return false; } } @@ -1605,7 +2167,7 @@ void MatchTOF::checkRefitter() { if (mTPCClusterIdxStruct) { mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, - mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), - nullptr, o2::base::Propagator::Instance()); + mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), + mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); } } diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index cd66d67755488..5f99ad2202073 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -9,8 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "GPUO2ExternalUser.h" // Needed for propper settings in GPUParam.h +#include "GPUParam.h" +#include "GPUParam.inc" +#ifdef WITH_OPENMP +#include +#endif + #include #include +#include #include #include "Field/MagneticField.h" @@ -27,12 +35,12 @@ #include "CommonConstants/PhysicsConstants.h" #include "CommonConstants/GeomConstants.h" #include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/GlobalParams.h" #include #include #include #include -#include "DataFormatsParameters/GRPObject.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CommonUtils/NameConf.h" @@ -42,13 +50,11 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "DataFormatsGlobalTracking/TrackTuneParams.h" #include "DataFormatsTPC/WorkflowHelper.h" - +#include "DetectorsBase/GRPGeomHelper.h" #include "ITStracking/IOUtils.h" -#include "GPUO2Interface.h" // Needed for propper settings in GPUParam.h - -#ifdef WITH_OPENMP -#include +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/IOUtils.h" #endif using namespace o2::globaltracking; @@ -57,8 +63,13 @@ using MatrixDSym4 = ROOT::Math::SMatrix>; using NAMES = o2::base::NameConf; using GTrackID = o2::dataformats::GlobalTrackID; +using TrackTunePar = o2::globaltracking::TrackTuneParams; constexpr float MatchTPCITS::Tan70, MatchTPCITS::Cos70I2, MatchTPCITS::MaxSnp, MatchTPCITS::MaxTgp; +LinksPoolMT* TPCABSeed::gLinksPool = nullptr; + +const o2::gpu::GPUTPCGeometry MatchTPCITS::TPCGeometry{}; + //______________________________________________ MatchTPCITS::MatchTPCITS() = default; @@ -66,7 +77,13 @@ MatchTPCITS::MatchTPCITS() = default; MatchTPCITS::~MatchTPCITS() = default; //______________________________________________ -void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) +void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) { ///< perform matching for provided input if (!mInitDone) { @@ -84,8 +101,8 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) break; } if (mVDriftCalibOn) { // in the beginning of the output vector we send the full and reference VDrift used for this TF - mTglITSTPC.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); - mTglITSTPC.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); + calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, mTPCDrift.refTP); + calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); } mTimer[SWDoMatching].Start(false); @@ -93,7 +110,7 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) doMatching(sec); } mTimer[SWDoMatching].Stop(); - if (0) { // enabling this creates very verbose output + if constexpr (false) { // enabling this creates very verbose output mTimer[SWTot].Stop(); printCandidatesTPC(); printCandidatesITS(); @@ -102,12 +119,16 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) selectBestMatches(); - refitWinners(); - - if (mUseFT0 && Params::Instance().runAfterBurner) { - runAfterBurner(); + bool fullMatchRefitDone = false; + if (mUseFT0 && mParams->runAfterBurner) { + fullMatchRefitDone = runAfterBurner(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); + } + if (!fullMatchRefitDone) { + refitWinners(matchedTracks, matchLabels, calib); // it afterburner is active, full matches refit will be done by it + } + if (mParams->verbosity > 0) { + reportSizes(matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); } - #ifdef _ALLOW_DEBUG_TREES_ if (mDBGOut && isDebugFlag(WinnerMatchesTree)) { dumpWinnerMatches(); @@ -118,13 +139,19 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) mTimer[SWTot].Stop(); if (mParams->verbosity > 0) { - for (int i = 0; i < NStopWatches; i++) { - LOGF(info, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots of TF#%d", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1, mTFCount); - } + reportTiming(); } mTFCount++; } +//______________________________________________ +void MatchTPCITS::reportTiming() +{ + for (int i = 0; i < NStopWatches; i++) { + LOGF(info, "Timing for %15s: Cpu: %.3e Real: %.3e s in %d slots of TF#%d", TimerName[i], mTimer[i].CpuTime(), mTimer[i].RealTime(), mTimer[i].Counter() - 1, mTFCount); + } +} + //______________________________________________ void MatchTPCITS::end() { @@ -140,21 +167,17 @@ void MatchTPCITS::clear() mMatchRecordsTPC.clear(); mMatchRecordsITS.clear(); mWinnerChi2Refit.clear(); - mMatchedTracks.clear(); mITSWork.clear(); mTPCWork.clear(); mInteractions.clear(); mITSROFTimes.clear(); mITSTrackROFContMapping.clear(); mITSClustersArray.clear(); + mITSClusterSizes.clear(); mTPCABSeeds.clear(); mTPCABIndexCache.clear(); mABWinnersIDs.clear(); mABClusterLinkIndex.clear(); - mABTrackletRefs.clear(); - mABTrackletClusterIDs.clear(); - mABTrackletLabels.clear(); - mTglITSTPC.clear(); mNMatchesControl = 0; for (int sec = o2::constants::math::NSectors; sec--;) { @@ -165,9 +188,11 @@ void MatchTPCITS::clear() } if (mMCTruthON) { - mOutLabels.clear(); - mITSROFTimes.clear(); mTPCLblWork.clear(); + mITSLblWork.clear(); + } + for (int i = 0; i < mNThreads; i++) { + mABLinksPool.threadPool[i].clear(); } } @@ -211,7 +236,7 @@ void MatchTPCITS::init() // make sure T2GRot matrices are loaded into ITS geometry helper o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); - mSectEdgeMargin2 = mParams->crudeAbsDiffCut[o2::track::kY] * mParams->crudeAbsDiffCut[o2::track::kY]; ///< precalculated ^2 + mSectEdgeMargin = mParams->crudeAbsDiffCut[o2::track::kY] / std::sqrt(Cos70I2); #ifdef _ALLOW_DEBUG_TREES_ // debug streamer @@ -220,7 +245,9 @@ void MatchTPCITS::init() } #endif - mRGHelper.init(); // prepare helper for TPC track / ITS clusters matching + if (mParams->runAfterBurner) { // only used in AfterBurner + mRGHelper.init(mParams->lowestLayerAB); // prepare helper for TPC track / ITS clusters matching + } clear(); @@ -257,6 +284,14 @@ void MatchTPCITS::updateTimeDependentParams() o2::math_utils::Point3D p0(90., 1., 1), p1(90., 100., 100.); auto matbd = o2::base::Propagator::Instance()->getMatBudget(mParams->matCorr, p0, p1); mTPCmeanX0Inv = matbd.meanX2X0 / matbd.length; + + const auto& trackTune = TrackTuneParams::Instance(); + float scale = mTPCCorrMapsHelper->getInstLumiCTP(); + if (scale < 0.f) { + scale = 0.f; + } + mCovDiagInner = trackTune.getCovInnerTotal(scale); + mCovDiagOuter = trackTune.getCovOuterTotal(scale); } //______________________________________________ @@ -264,8 +299,9 @@ void MatchTPCITS::selectBestMatches() { ///< loop over match records and select the ones with best chi2 mTimer[SWSelectBest].Start(false); - int nValidated = 0, iter = 0, nValidatedTotal = 0; - + int nValidated = 0, iter = 0; + mNMatches = 0; + mNCalibPrelim = 0; do { nValidated = 0; int ntpc = mTPCWork.size(), nremaining = 0; @@ -277,6 +313,9 @@ void MatchTPCITS::selectBestMatches() nremaining++; if (validateTPCMatch(it)) { nValidated++; + if (mVDriftCalibOn && (!mFieldON || std::abs(tTPC.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) { + mNCalibPrelim++; + } continue; } } @@ -284,31 +323,45 @@ void MatchTPCITS::selectBestMatches() LOGP(info, "iter {}: Validated {} of {} remaining matches", iter, nValidated, nremaining); } iter++; - nValidatedTotal += nValidated; + mNMatches += nValidated; } while (nValidated); mTimer[SWSelectBest].Stop(); - LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", nValidatedTotal, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter); + LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", mNMatches, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter); } //______________________________________________ bool MatchTPCITS::validateTPCMatch(int iTPC) { - const auto& tTPC = mTPCWork[iTPC]; + auto& tTPC = mTPCWork[iTPC]; auto& rcTPC = mMatchRecordsTPC[tTPC.matchID]; // best TPC->ITS match - /* // should never happen - if (rcTPC.nextRecID == Validated) { - LOG(warning) << "TPC->ITS was already validated"; - return false; // RS do we need this - } - */ // check if it is consistent with corresponding ITS->TPC match auto& tITS = mITSWork[rcTPC.partnerID]; // partner ITS track auto& rcITS = mMatchRecordsITS[tITS.matchID]; // best ITS->TPC match record if (rcITS.nextRecID == Validated) { return false; } - if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + int cloneID = tITS.getCloneShift(); // check if there is a clone of tITS + while (cloneID) { + cloneID += rcTPC.partnerID; + auto& tITSClone = mITSWork[cloneID]; + if (isDisabledITS(tITSClone)) { // ignore clone + break; + } + int nextITSCloneMatchID = tITSClone.matchID; + if (rcITS.isBetter(mMatchRecordsITS[nextITSCloneMatchID])) { // best ITS->TPC match record for the clone is worse than the rcITS + LOGP(debug, "Suppressing clone cloneID={} of winner clone {} of source ITS {}", cloneID, rcTPC.partnerID, tITS.sourceID); + while (nextITSCloneMatchID > MinusOne) { + auto& rcITSClone = mMatchRecordsITS[nextITSCloneMatchID]; + removeITSfromTPC(cloneID, rcITSClone.partnerID); + nextITSCloneMatchID = rcITSClone.nextRecID; + } + tITSClone.matchID = MinusTen; // disable + break; + } + return false; // ignore match at this iteration + } // unlink winner TPC track from all ITS candidates except winning one int nextTPC = rcTPC.nextRecID; while (nextTPC > MinusOne) { @@ -327,6 +380,7 @@ bool MatchTPCITS::validateTPCMatch(int iTPC) nextITS = rcITSrem.nextRecID; } rcITS.nextRecID = Validated; + tTPC.gid.setBit(0); // Flag full match return true; } return false; @@ -358,24 +412,30 @@ int MatchTPCITS::getNMatchRecordsITS(const TrackLocITS& tTPC) const } //______________________________________________ -void MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, GTrackID srcGID, int tpcID) +int MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr, GTrackID srcGID, int tpcID) { // account single TPC seed, can be from standalone TPC track or constrained track from match to TRD and/or TOF const float SQRT12DInv = 2. / sqrt(12.); if (_tr.getX() > o2::constants::geom::XTPCInnerRef + 0.1 || std::abs(_tr.getQ2Pt()) > mMinTPCTrackPtInv) { - return; + return -99; } const auto& tpcOrig = mTPCTracksArray[tpcID]; // discard tracks w/o certain number of total or innermost pads (last cluster is innermost one) if (tpcOrig.getNClusterReferences() < mParams->minTPCClusters) { - return; + return -89; } uint8_t clSect = 0, clRow = 0; uint32_t clIdx = 0; tpcOrig.getClusterReference(mTPCTrackClusIdx, tpcOrig.getNClusterReferences() - 1, clSect, clRow, clIdx); - if (clRow > mParams->askMinTPCRow) { - return; + if (clRow > mParams->askMinTPCRow[clSect]) { + return -9; + } + const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx]; + uint8_t padFromEdge = uint8_t(clus.getPad()); + if (padFromEdge > TPCGeometry.NPads(clRow) / 2) { + padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge; } + // create working copy of track param bool extConstrained = srcGID.getSource() != GTrackID::TPC; if (extConstrained) { @@ -384,35 +444,37 @@ void MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float terr += tpcTimeBin2MUS(tpcOrig.hasBothSidesClusters() ? mParams->safeMarginTPCITSTimeBin : mTPCTimeEdgeTSafeMargin); } auto& trc = mTPCWork.emplace_back( - TrackLocTPC{_tr, {t0 - terr, t0 + terr}, extConstrained ? t0 : tpcTimeBin2MUS(tpcOrig.getTime0()) - mTPCDriftTimeOffset, + TrackLocTPC{_tr, {t0 - terr, t0 + terr}, extConstrained ? t0 : tpcTimeBin2MUS(tpcOrig.getTime0()), // for A/C constrained tracks the terr is half-interval, for externally constrained tracks it is sigma*Nsigma terr * (extConstrained ? mTPCExtConstrainedNSigmaInv : SQRT12DInv), tpcID, srcGID, MinusOne, + clRow, + padFromEdge, (extConstrained || tpcOrig.hasBothSidesClusters()) ? TrackLocTPC::Constrained : (tpcOrig.hasASideClustersOnly() ? TrackLocTPC::ASide : TrackLocTPC::CSide)}); // propagate to matching Xref - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); // only TPC standalone need to be corrected on the input, provided they were not corrected at the source level, // other inputs are corrected in respective upstream matching processes if (srcGID.getSource() == GTrackID::TPC && !trackTune.sourceLevelTPC) { if (trackTune.useTPCInnerCorr) { trc.updateParams(trackTune.tpcParInner); } - if (trackTune.tpcCovInnerType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - trc.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovInnerType != TrackTuneParams::AddCovType::Disable) { + trc.updateCov(mCovDiagInner, trackTune.tpcCovInnerType == TrackTuneParams::AddCovType::WithCorrelations); } } if (!propagateToRefX(trc)) { mTPCWork.pop_back(); // discard track whose propagation to XMatchingRef failed - return; + return -1; } if (mMCTruthON) { mTPCLblWork.emplace_back(mTPCTrkLabels[tpcID]); } - // cache work track index mTPCSectIndexCache[o2::math_utils::angle2Sector(trc.getAlpha())].push_back(mTPCWork.size() - 1); + return 0; } //______________________________________________ @@ -426,18 +488,46 @@ bool MatchTPCITS::prepareTPCData() mTPCTrackClusIdx = inp.getTPCTracksClusterRefs(); mTPCClusterIdxStruct = &inp.inputsTPCclusters->clusterIndex; mTPCRefitterShMap = inp.clusterShMapTPC; + mTPCRefitterOccMap = inp.occupancyMapTPC; + if (mMCTruthON) { mTPCTrkLabels = inp.getTPCTracksMCLabels(); } - int ntr = mTPCTracksArray.size(); - mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntr); // number of records might be actually more than N tracks! - mTPCWork.reserve(ntr); + int ntr = mTPCTracksArray.size(), ntrW = 0.7 * ntr; + mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntrW); // number of records might be actually more than N tracks! + mTPCWork.reserve(ntrW); if (mMCTruthON) { - mTPCLblWork.reserve(ntr); + mTPCLblWork.reserve(ntrW); } for (int sec = o2::constants::math::NSectors; sec--;) { - mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntr / o2::constants::math::NSectors); + mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntrW / o2::constants::math::NSectors); + } + + mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); + mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 + mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOcc.clear(); + if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOcc.resize(nTPCOccBins); + std::vector mltHistTB(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + for (int i = 0; i < nTPCOccBins; i++) { + mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mltHistTB[i + sumBins]; + } + mTBinClOcc[i] = sm; + } + } else { + mTBinClOcc.resize(1); } auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) { @@ -449,20 +539,27 @@ bool MatchTPCITS::prepareTPCData() } else if (std::abs(trk.getQ2Pt()) > mMinTPCTrackPtInv) { return true; } + int resAdd = -100; + int tpcIndex = -1; if constexpr (isTPCTrack()) { // unconstrained TPC track, with t0 = TrackTPC.getTime0+0.5*(DeltaFwd-DeltaBwd) and terr = 0.5*(DeltaFwd+DeltaBwd) in TimeBins if (!this->mSkipTPCOnly && trk.getNClusters() > 0) { - this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0) - this->mTPCDriftTimeOffset, this->tpcTimeBin2MUS(terr), gid, gid.getIndex()); + resAdd = this->addTPCSeed(trk, this->tpcTimeBin2MUS(time0), this->tpcTimeBin2MUS(terr), gid, (tpcIndex = gid.getIndex())); } } if constexpr (isTPCTOFTrack()) { // TPC track constrained by TOF time, time and its error in \mus - this->addTPCSeed(trk, time0, terr, gid, this->mRecoCont->getTPCContributorGID(gid)); + resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid))); } if constexpr (isTRDTrack()) { // TPC track constrained by TRD trigger time, time and its error in \mus - this->addTPCSeed(trk, time0, terr, gid, this->mRecoCont->getTPCContributorGID(gid)); + resAdd = this->addTPCSeed(trk, time0, terr, gid, (tpcIndex = this->mRecoCont->getTPCContributorGID(gid))); } +#ifdef _ALLOW_DEBUG_TREES_ + if (resAdd > -10 && mDBGOut && isDebugFlag(TPCOrigTree)) { + dumpTPCOrig(resAdd == 0, tpcIndex); + } +#endif // note: TPCTRDTPF tracks are actually TRD track with extra TOF cluster return true; }; @@ -531,7 +628,6 @@ bool MatchTPCITS::prepareTPCData() mITSROFofTPCBin[ib] = itsROF; } */ - mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), nullptr, o2::base::Propagator::Instance()); mInteractionMUSLUT.clear(); mInteractionMUSLUT.resize(maxTime + 3 * o2::constants::lhc::LHCOrbitMUS, -1); mTimer[SWPrepTPC].Stop(); @@ -557,7 +653,46 @@ bool MatchTPCITS::prepareITSData() const auto patterns = inp.getITSClustersPatterns(); auto pattIt = patterns.begin(); mITSClustersArray.reserve(clusITS.size()); +#ifdef ENABLE_UPGRADES + bool withITS3 = o2::GlobalParams::Instance().withITS3; + if (withITS3) { + o2::its3::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mIT3Dict); + } else { + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + } +#else o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); +#endif + + // ITS clusters sizes + mITSClusterSizes.reserve(clusITS.size()); + auto pattIt2 = patterns.begin(); + for (auto& clus : clusITS) { + auto pattID = clus.getPatternID(); + unsigned int npix; +#ifdef ENABLE_UPGRADES + auto ib = o2::its3::constants::detID::isDetITS3(clus.getChipID()); + if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID, ib) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order +#else + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mITSDict->isGroup(pattID)) { +#endif + o2::itsmft::ClusterPattern patt; + patt.acquirePattern(pattIt2); + npix = patt.getNPixels(); + } else { +#ifdef ENABLE_UPGRADES + if (withITS3) { + npix = mIT3Dict->getNpixels(pattID, ib); + } else { + npix = mITSDict->getNpixels(pattID); + } +#else + npix = mITSDict->getNpixels(pattID); +#endif + } + mITSClusterSizes.push_back(std::clamp(npix, 0u, 255u)); + } + if (mMCTruthON) { mITSClsLabels = inp.mcITSClusters.get(); } @@ -579,15 +714,20 @@ bool MatchTPCITS::prepareITSData() for (int sec = o2::constants::math::NSectors; sec--;) { mITSTimeStart[sec].resize(nROFs, -1); // start of ITS work tracks in every sector } + long nHBF = o2::base::GRPGeomHelper::getNHBFPerTF(); + long maxBCs = nHBF * long(o2::constants::lhc::LHCMaxBunches); + o2::track::TrackLTIntegral trackLTInt; + trackLTInt.setTimeNotNeeded(); for (int irof = 0; irof < nROFs; irof++) { const auto& rofRec = mITSTrackROFRec[irof]; - auto nBC = rofRec.getBCData().differenceInBC(mStartIR); - if (uint64_t(nBC) > 256 * uint64_t(o2::constants::lhc::LHCMaxBunches)) { // RS: fixme: use real NHBFPerTF from GRP + long nBC = rofRec.getBCData().differenceInBC(mStartIR); + if (nBC > maxBCs || nBC < 0) { if (++errCount < MaxErrors2Report) { - LOG(alarm) << "ITS ROF start " << rofRec.getBCData() << " does not match to TF with 1st orbit " << mStartIR; + LOGP(alarm, "ITS ROF#{} start {} is not compatible with TF 1st orbit {} or TF length of {} HBFs", + irof, rofRec.getBCData().asString(), mStartIR.asString(), nHBF); } - return false; + break; } float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; float tMax = (nBC + mITSROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; @@ -625,13 +765,18 @@ bool MatchTPCITS::prepareITSData() continue; } // make sure the track is at the ref. radius - if (!propagateToRefX(trc)) { + trackLTInt.clearFast(); + if (!propagateToRefX(trc, &trackLTInt)) { mITSWork.pop_back(); // discard failed track continue; // add to cache only those ITS tracks which reached ref.X and have reasonable snp } + trc.xrho = trackLTInt.getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions + trc.dL = trackLTInt.getL(); + if (mMCTruthON) { mITSLblWork.emplace_back(mITSTrkLabels[it]); } + trc.setUserField(0); // cache work track index int sector = o2::math_utils::angle2Sector(trc.getAlpha()); mITSSectIndexCache[sector].push_back(nWorkTracks); @@ -641,19 +786,16 @@ bool MatchTPCITS::prepareITSData() // when propagated to Xr (in this neighbouring sector) and the edge will be (neglecting the curvature) // [(Xr*tg(10)-Yr)/(tgPhir+tg70)]^2 / cos(70)^2 // for the next sector // [(Xr*tg(10)+Yr)/(tgPhir-tg70)]^2 / cos(70)^2 // for the prev sector - // Distances to the sector edges in neighbourings sectors (at Xref in theit proper frames) + // Distances to the sector edges in neighbourings sectors (at Xref in their proper frames) float trcY = trc.getY(), tgp = trc.getSnp(); tgp /= std::sqrt((1.f - tgp) * (1.f + tgp)); // tan of track direction XY - // sector up - float dy2Up = (YMaxAtXMatchingRef - trcY) / (tgp + Tan70); - if ((dy2Up * dy2Up * Cos70I2) < mSectEdgeMargin2) { // need to check this track for matching in sector up - addLastTrackCloneForNeighbourSector(sector < (o2::constants::math::NSectors - 1) ? sector + 1 : 0); - } - // sector down - float dy2Dn = (YMaxAtXMatchingRef + trcY) / (tgp - Tan70); - if ((dy2Dn * dy2Dn * Cos70I2) < mSectEdgeMargin2) { // need to check this track for matching in sector down - addLastTrackCloneForNeighbourSector(sector > 1 ? sector - 1 : o2::constants::math::NSectors - 1); + float dyUpDn[2] = {std::abs((YMaxAtXMatchingRef - trcY) / (tgp + Tan70)), std::abs((YMaxAtXMatchingRef + trcY) / (tgp - Tan70))}; // sector up, down edge distances + // we do the cloning for closest edge only + int sel = dyUpDn[0] < dyUpDn[1] ? 0 : 1; + if (dyUpDn[sel] < mSectEdgeMargin) { // need to check this track for matching in sector up or down + int sectNeib = sel == 0 ? (sector < (o2::constants::math::NSectors - 1) ? sector + 1 : 0) : (sector > 1 ? sector - 1 : o2::constants::math::NSectors - 1); + addLastTrackCloneForNeighbourSector(sectNeib, &trackLTInt); } } } @@ -771,12 +913,22 @@ void MatchTPCITS::doMatching(int sec) } o2::math_utils::Bracketf_t trange(timeCorr - timeCorrErr, timeCorr + timeCorrErr); LOG(debug) << "TPC range: " << trange.asString() << " ITS bracket: " << trefITS.tBracket.asString() << " DZ: " << (trefITS.getZ() - trefTPC.getZ()) << " DT: " << timeCorr; - if (trefITS.tBracket.isOutside(trange)) { - continue; - } - if (timeCorr < 0) { // RS TODO: similar check will be needed to other TF edge - if (timeCorr + mParams->tfEdgeTimeToleranceMUS < 0) { - // continue; + // check if the assigned time is strictly within the ITS bracket + auto cmpITSBracket = trefITS.tBracket.isOutside(timeCorr); + if (cmpITSBracket) { // no, check if brackets are overlapping at all + if (trefITS.tBracket.isOutside(trange)) { + continue; + } + if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Adjust) { + if (cmpITSBracket == o2::math_utils::Bracketf_t::Below) { + timeCorr = trefITS.tBracket.getMin(); + trange.setMin(timeCorr); + } else { + timeCorr = trefITS.tBracket.getMax(); + trange.setMax(timeCorr); + } + } else if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Reject) { + continue; } } @@ -943,7 +1095,7 @@ bool MatchTPCITS::registerMatchRecordTPC(int iITS, int iTPC, float chi2, int can } //______________________________________________ -void MatchTPCITS::registerMatchRecordITS(int iITS, int iTPC, float chi2, int candIC) +void MatchTPCITS::registerMatchRecordITS(const int iITS, int iTPC, float chi2, int candIC) { ///< register TPC match in ITS tracks match records, ordering them in quality auto& tITS = mITSWork[iITS]; @@ -997,7 +1149,26 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kTgl], RejectOnTgl + NSigmaShift))) { return rejFlag; } - diff = tITS.getParam(o2::track::kY) - tTPC.getParam(o2::track::kY); + // do we need to account for different PID hypotheses used for ITS and TPC tracks propagation to ref. X? + bool testOtherPID = false; + float itsParam[5] = {tITS.getY(), tITS.getZ(), tITS.getSnp(), tITS.getTgl(), tITS.getQ2Pt()}; + if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + o2::track::TrackPar tPID(mITSTracksArray[tITS.sourceID].getParamOut()); // clone original ITS track at highest update point + tPID.setPID(tTPC.getPID(), true); + if (!tPID.correctForELoss(tITS.xrho)) { + return RejectoOnPIDCorr; + } + float dCurv = (tPID.getQ2Pt() - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff; + itsParam[o2::track::kQ2Pt] = tPID.getQ2Pt(); + itsParam[o2::track::kSnp] += dCurvL; + if (std::abs(itsParam[o2::track::kSnp]) >= 1.) { + itsParam[o2::track::kSnp] = std::copysign(0.99, itsParam[o2::track::kSnp]); + } + itsParam[o2::track::kY] += dCurvL * dLEff * 0.5; + testOtherPID = true; + } + + diff = itsParam[o2::track::kY] - tTPC.getParam(o2::track::kY); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kY], RejectOnY))) { return rejFlag; } @@ -1007,7 +1178,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& } if (tTPC.constraint == TrackLocTPC::Constrained) { // in continuous only constrained tracks can be compared in Z - diff = tITS.getParam(o2::track::kZ) - tTPC.getParam(o2::track::kZ); + diff = itsParam[o2::track::kZ] - tTPC.getParam(o2::track::kZ); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kZ], RejectOnZ))) { return rejFlag; } @@ -1017,7 +1188,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& } } - diff = tITS.getParam(o2::track::kSnp) - tTPC.getParam(o2::track::kSnp); + diff = itsParam[o2::track::kSnp] - tTPC.getParam(o2::track::kSnp); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kSnp], RejectOnSnp))) { return rejFlag; } @@ -1026,7 +1197,7 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& return rejFlag; } - diff = tITS.getParam(o2::track::kQ2Pt) - tTPC.getParam(o2::track::kQ2Pt); + diff = itsParam[o2::track::kQ2Pt] - tTPC.getParam(o2::track::kQ2Pt); if ((rejFlag = roughCheckDif(diff, mParams->crudeAbsDiffCut[o2::track::kQ2Pt], RejectOnQ2Pt))) { return rejFlag; } @@ -1034,9 +1205,18 @@ int MatchTPCITS::compareTPCITSTracks(const TrackLocITS& tITS, const TrackLocTPC& if ((rejFlag = roughCheckDif(diff, mParams->crudeNSigma2Cut[o2::track::kQ2Pt], RejectOnQ2Pt + NSigmaShift))) { return rejFlag; } - // calculate mutual chi2 excluding Z in continuos mode - chi2 = getPredictedChi2NoZ(tITS, tTPC); - if (chi2 > mParams->cutMatchingChi2 || chi2 < 0.) { // sometime due to the numerical stability the chi2 is negative, reject it. + // calculate mutual chi2 excluding Z in continuous mode + if (testOtherPID) { // temporarily substitute pion params by alternative ones + auto tITSAlt = tITS; + tITSAlt.setPID(tTPC.getPID()); + tITSAlt.setParam(itsParam[o2::track::kY], o2::track::kY); + tITSAlt.setParam(itsParam[o2::track::kSnp], o2::track::kSnp); + tITSAlt.setParam(itsParam[o2::track::kQ2Pt], o2::track::kQ2Pt); + chi2 = getPredictedChi2NoZ(tITSAlt, tTPC); + } else { + chi2 = getPredictedChi2NoZ(tITS, tTPC); + } + if (chi2 > mParams->cutMatchingChi2 || chi2 < 0.) { // sometimes due to the numerical stability the chi2 is negative, reject it. return RejectOnChi2; } @@ -1135,7 +1315,7 @@ float MatchTPCITS::getPredictedChi2NoZ(const o2::track::TrackParCov& trITS, cons } //______________________________________________ -void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) +void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector, o2::track::TrackLTIntegral* trackLTInt) { // add clone of the src ITS track cache, propagate it to ref.X in requested sector // and register its index in the sector cache. Used for ITS tracks which are so close @@ -1143,9 +1323,16 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) mITSWork.push_back(mITSWork.back()); // clone the last track defined in given sector auto& trc = mITSWork.back(); if (trc.rotate(o2::math_utils::sector2Angle(sector)) && - o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., MatCorrType::USEMatCorrNONE)) { + o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, trackLTInt)) { // TODO: use faster prop here, no 3d field, materials mITSSectIndexCache[sector].push_back(mITSWork.size() - 1); // register track CLONE + // flag clone + mITSWork.back().setCloneBefore(); + if (trackLTInt) { + mITSWork.back().xrho = trackLTInt->getXRho(); // we collect seen x*rho and distance to the reference X for further PID correcrions + mITSWork.back().dL = trackLTInt->getL(); + } + mITSWork[mITSWork.size() - 2].setCloneAfter(); if (mMCTruthON) { mITSLblWork.emplace_back(mITSTrkLabels[trc.sourceID]); } @@ -1155,19 +1342,20 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) } //______________________________________________ -bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc) +bool MatchTPCITS::propagateToRefX(o2::track::TrackParCov& trc, o2::track::TrackLTIntegral* lti) { // propagate track to matching reference X, making sure its assigned alpha // is consistent with TPC sector + constexpr float TgHalfSector = 0.17632698f; bool refReached = false; refReached = mParams->XMatchingRef < 10.; // RS: tmp, to cover XMatchingRef~0 int trialsLeft = 2; - while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag, lti)) { if (refReached) { break; } // make sure the track is indeed within the sector defined by alpha - if (fabs(trc.getY()) < mParams->XMatchingRef * tan(o2::constants::math::SectorSpanRad / 2)) { + if (fabs(trc.getY()) < mParams->XMatchingRef * TgHalfSector) { refReached = true; break; // ok, within } @@ -1229,56 +1417,205 @@ void MatchTPCITS::print() const } //______________________________________________ -void MatchTPCITS::refitWinners() +void MatchTPCITS::refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit winning tracks - mTimer[SWRefit].Start(false); + matchedTracks.reserve(mNMatches + mABWinnersIDs.size()); + matchedTracks.resize(mNMatches); + if (mMCTruthON) { + matchLabels.reserve(mNMatches + mABWinnersIDs.size()); + matchLabels.resize(mNMatches); + } + if (mVDriftCalibOn) { + calib.reserve(mNCalibPrelim * 1.2 + 1); + } + std::vector tpcToFit; + tpcToFit.reserve(mNMatches); + for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { + const auto& tTPC = mTPCWork[iTPC]; + if (!isDisabledTPC(tTPC) && tTPC.gid.testBit(0)) { + tpcToFit.push_back(iTPC); + } + } LOG(debug) << "Refitting winner matches"; mWinnerChi2Refit.resize(mITSWork.size(), -1.f); - int iITS; - for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { - if (!refitTrackTPCITS(iTPC, iITS)) { - continue; + int nToFit = (int)tpcToFit.size(); + unsigned int nFailedRefit{0}; + +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) \ + reduction(+ : nFailedRefit) +#endif + for (int ifit = 0; ifit < nToFit; ifit++) { + int iTPC = tpcToFit[ifit], iITS; + const auto& tTPC = mTPCWork[iTPC]; + if (refitTrackTPCITS(ifit, iTPC, iITS, matchedTracks, matchLabels, calib)) { + mWinnerChi2Refit[iITS] = matchedTracks.back().getChi2Refit(); + } else { + ++nFailedRefit; + } + } + LOGP(info, "Failed {} TPC-ITS refits out of {}", nFailedRefit, nToFit); + + // suppress tracks failed on refit and fill calib/debug data (if needed) + int last = nToFit; + mNMatches = 0; + for (int ifit = 0; ifit < nToFit; ifit++) { + int itpc = tpcToFit[ifit]; + if (!matchedTracks[ifit].isValid()) { // move the last good track from the back to the slot to delete + while (--last > ifit && !matchedTracks[last].isValid()) { + } // find the highest valid track + if (last > ifit) { + matchedTracks[ifit] = matchedTracks[last]; + matchedTracks[last].invalidate(); + itpc = tpcToFit[last]; + if (mMCTruthON) { + matchLabels[ifit] = matchLabels[last]; + } + } else { + break; + } + } + if (mDBGOut || mVDriftCalibOn) { + fillCalibDebug(ifit, itpc, matchedTracks[ifit], calib); } - mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); + mNMatches++; + } + // adjust sizes + matchedTracks.resize(mNMatches); + if (mMCTruthON) { + matchLabels.resize(mNMatches); } mTimer[SWRefit].Stop(); } //______________________________________________ -bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) +void MatchTPCITS::fillCalibDebug(int ifit, int iTPC, const o2::dataformats::TrackTPCITS& match, pmr::vector>& calib) +{ + const auto& tTPC = mTPCWork[iTPC]; + int iITS = mMatchRecordsTPC[tTPC.matchID].partnerID; + const auto& tITS = mITSWork[iITS]; + float minDiffFT0 = -999., timeC = 0.f; + std::vector dtimes; + bool fillVDCalib = mVDriftCalibOn && (!mFieldON || std::abs(match.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt); + if (fillVDCalib || mDBGOut) { + timeC = match.getTimeMUS().getTimeStamp(); // find closest FIT record + float minDiffA = mParams->maxVDritTimeOffset; + if (mInteractions.size()) { + int timeC0 = timeC - minDiffA; + if (timeC0 < 0) { + timeC0 = 0; + } + auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0); + for (int ent = entStart; ent < (int)mInteractions.size(); ent++) { + float diff = mInteractions[ent].tBracket.mean() - timeC; + if (diff > minDiffA) { + break; // all following will be the same + } + if (diff < -minDiffA) { + continue; + } + dtimes.push_back(diff); + minDiffFT0 = diff; + minDiffA = std::abs(minDiffFT0); + } + } + } + if (fillVDCalib) { + calib.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); + } +#ifdef _ALLOW_DEBUG_TREES_ + if (mDBGOut) { + o2::track::TrackPar itsRefPIDCorr(tITS); // version ad hoc corrected for TPC PID being different from the pion, as it is done in the matching + o2::track::TrackParCov itsRefAltPID(tITS); // version with full propagation to account for TPC PID being different from the pion + itsRefPIDCorr.setX(0); + if (tTPC.getPID() > tITS.getPID() && tITS.dL > 0.f && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + itsRefAltPID = mITSTracksArray[tITS.sourceID].getParamOut(); // clone original ITS track at highest update point + itsRefPIDCorr.setPID(tTPC.getPID(), true); + itsRefPIDCorr = itsRefAltPID; + // fast correction + if (!itsRefPIDCorr.correctForELoss(tITS.xrho)) { + itsRefPIDCorr.setX(-10); + } else { + float q2ptPID = itsRefPIDCorr.getQ2Pt(); + float dCurv = (q2ptPID - tITS.getQ2Pt()) * mBz * o2::constants::math::B2C, dLEff = tITS.dL * mParams->ITSStepEffFraction, dCurvL = dCurv * dLEff; + itsRefPIDCorr = tITS; + itsRefPIDCorr.setPID(tTPC.getPID(), true); + itsRefPIDCorr.setQ2Pt(q2ptPID); + auto snp = tITS.getSnp() + dCurvL; + if (std::abs(snp) >= 1.) { + snp = std::copysign(0.99, snp); + } + itsRefPIDCorr.setSnp(snp); + itsRefPIDCorr.setY(tITS.getY() + dCurvL * dLEff * 0.5); + } + // full propagation + if (!itsRefAltPID.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(itsRefAltPID, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + itsRefAltPID.setX(-10); + } + } + int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + (*mDBGOut) << "refit" + << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << mITSTracksArray[tITS.sourceID] << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << match + << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes + << "itsRefAltPID=" << itsRefAltPID << "itsRefPIDCorr=" << itsRefPIDCorr; + if (mMCTruthON) { + (*mDBGOut) << "refit" + << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC]; + } + (*mDBGOut) << "refit" + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries() + << "tf=" << mTFCount << "\n"; + } +#endif +} + +//______________________________________________ +bool MatchTPCITS::refitTrackTPCITS(int slot, int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit in inward direction the pair of TPC and ITS tracks const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[iTPC]; - if (isDisabledTPC(tTPC)) { - return false; // no match - } const auto& tpcMatchRec = mMatchRecordsTPC[tTPC.matchID]; iITS = tpcMatchRec.partnerID; const auto& tITS = mITSWork[iITS]; const auto& itsTrOrig = mITSTracksArray[tITS.sourceID]; - - mMatchedTracks.emplace_back(tTPC, tITS); // create a copy of TPC track at xRef - auto& trfit = mMatchedTracks.back(); + auto& trfit = matchedTracks[slot]; + ((o2::track::TrackParCov&)trfit) = (const o2::track::TrackParCov&)tTPC; + trfit.getParamOut() = (const o2::track::TrackParCov&)tITS; // create a copy of TPC track at xRef + trfit.getParamOut().setUserField(0); // reset eventual clones flag + trfit.setPID(tTPC.getPID(), true); + trfit.getParamOut().setPID(tTPC.getPID(), true); // in continuos mode the Z of TPC track is meaningless, unless it is CE crossing // track (currently absent, TODO) if (!mCompareTracksDZ) { trfit.setZ(tITS.getZ()); // fix the seed Z } - float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus - float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time + float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus + float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time if (timeErr > mITSTimeResMUS && tTPC.constraint != TrackLocTPC::Constrained) { timeErr = mITSTimeResMUS; // chose smallest error deltaT = tTPC.constraint == TrackLocTPC::ASide ? tITS.tBracket.mean() - tTPC.time0 : tTPC.time0 - tITS.tBracket.mean(); } timeErr += mParams->globalTimeExtraErrorMUS; - float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias - if (timeC < 0) { // RS TODO similar check is needed for other edge of TF + float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias + o2::math_utils::Bracketf_t::Relation relITS; + if (mParams->ITSTimeOutliersPolicy != MatchTPCITSParams::TimeOutliersPolicy::Tolerate && (relITS = tITS.tBracket.isOutside(timeC))) { /// track time is outside of the nominal ITS time + if (mParams->ITSTimeOutliersPolicy == MatchTPCITSParams::TimeOutliersPolicy::Adjust) { + timeC = relITS == o2::math_utils::Bracketf_t::Below ? tITS.tBracket.getMin() : tITS.tBracket.getMax(); /// assign ITS boundary + } else { // == MatchTPCITSParams::TimeOutliersPolicy::Reject + trfit.invalidate(); + return false; + } + } + if (timeC < 0) { // RS TODO similar check is needed for other edge of TF if (timeC + std::min(timeErr, mParams->tfEdgeTimeToleranceMUS * mTPCTBinMUSInv) < 0) { - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } timeC = 0.; @@ -1319,7 +1656,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) if (nclRefit != ncl) { LOGP(debug, "Refit in ITS failed after ncl={}, match between TPC track #{} and ITS track #{}", nclRefit, tTPC.sourceID, tITS.sourceID); LOGP(debug, "{:s}", trfit.asString()); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } @@ -1332,16 +1669,28 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) maxStep, MatCorrType::USEMatCorrNONE, nullptr, &trfit.getLTIntegralOut())) { LOG(error) << "LTOF integral might be incorrect"; } + auto& tofL = trfit.getLTIntegralOut(); // this is TL integral calculated from the RefX to the DCA to the beamline, invert material integrals for outward propagation + tofL.setX2X0(-tofL.getX2X0()); + tofL.setXRho(-tofL.getXRho()); // outward refit auto& tracOut = trfit.getParamOut(); // this is a clone of ITS outward track already at the matching reference X - auto& tofL = trfit.getLTIntegralOut(); + if (tTPC.getPID() > tITS.getPID() && tTPC.getP2() / tTPC.getPID().getMass2() < mParams->minBetaGammaForPIDDiff) { + // in case the TPC track hypothesis is not pion, we redo the outward propagation to ref.x with TPC PID + tracOut = mITSTracksArray[tITS.sourceID].getParamOut(); + tracOut.setPID(tTPC.getPID(), true); + if (!tracOut.rotate(tTPC.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(tracOut, mParams->XMatchingRef, MaxSnp, 2., mUseMatCorrFlag)) { + LOGP(debug, "Failed to rotate ITSouter with imposed PID to TPC alpha {} or propagate to X={}: {:s}", tTPC.getAlpha(), mParams->XMatchingRef, tracOut.asString()); + trfit.invalidate(); // destroy failed track + return false; + } + } { float xtogo = 0; if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } if (mVDriftCalibOn) { @@ -1349,44 +1698,34 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) } float chi2Out = 0; auto posStart = tracOut.getXYZGlo(); - auto tImposed = (timeC + mTPCDriftTimeOffset) * mTPCTBinMUSInv; + auto tImposed = timeC * mTPCTBinMUSInv; if (std::abs(tImposed - mTPCTracksArray[tTPC.sourceID].getTime0()) > 550) { LOGP(alarm, "Impossible imposed timebin {} for TPC track time0:{}, dBwd:{} dFwd:{} TB | ZShift:{}, TShift:{}", tImposed, mTPCTracksArray[tTPC.sourceID].getTime0(), mTPCTracksArray[tTPC.sourceID].getDeltaTBwd(), mTPCTracksArray[tTPC.sourceID].getDeltaTFwd(), trfit.getZ() - tTPC.getZ(), deltaT); LOGP(info, "Trc: {}", mTPCTracksArray[tTPC.sourceID].asString()); - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), tImposed, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + trfit.invalidate(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); - // account path integrals - float dX = posEnd.x() - posStart.x(), dY = posEnd.y() - posStart.y(), dZ = posEnd.z() - posStart.z(), d2XY = dX * dX + dY * dY; - if (mFieldON) { // circular arc = 2*R*asin(dXY/2R) - float b[3]; - o2::math_utils::Point3D posAv(0.5 * (posEnd.x() + posStart.x()), 0.5 * (posEnd.y() + posStart.y()), 0.5 * (posEnd.z() + posStart.z())); - propagator->getFieldXYZ(posAv, b); - float curvH = std::abs(0.5f * tracOut.getCurvature(b[2])), arcXY = 1. / curvH * std::asin(curvH * std::sqrt(d2XY)); - d2XY = arcXY * arcXY; - } - auto lInt = std::sqrt(d2XY + dZ * dZ); - tofL.addStep(lInt, tracOut.getP2Inv()); + auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); if (trackTune.useTPCOuterCorr) { tracOut.updateParams(trackTune.tpcParOuter); } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - tracOut.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { + tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); } } - trfit.setChi2Match(tpcMatchRec.chi2); trfit.setChi2Refit(chi2); trfit.setTimeMUS(timeC, timeErr); @@ -1394,85 +1733,42 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) trfit.setRefITS({unsigned(tITS.sourceID), o2::dataformats::GlobalTrackID::ITS}); if (mMCTruthON) { // store MC info: we assign TPC track label and declare the match fake if the ITS and TPC labels are different (their fake flag is ignored) - auto& lbl = mOutLabels.emplace_back(mTPCLblWork[iTPC]); - lbl.setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]); + matchLabels[slot] = mTPCLblWork[iTPC]; + matchLabels[slot].setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]); } - // if requested, fill the difference of ITS and TPC tracks tgl for vdrift calibation - float minDiffFT0 = -999.; - std::vector dtimes; - if (mVDriftCalibOn && (!mFieldON || std::abs(trfit.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) { - // find closest FIT record - float minDiffA = mParams->maxVDritTimeOffset; - if (mInteractions.size()) { - int timeC0 = timeC - minDiffA; - if (timeC0 < 0) { - timeC0 = 0; - } - auto entStart = timeC0 < int(mInteractionMUSLUT.size()) ? mInteractionMUSLUT[timeC0] : (mInteractionMUSLUT.size() ? mInteractionMUSLUT.back() : 0); - for (int ent = entStart; ent < (int)mInteractions.size(); ent++) { - float diff = mInteractions[ent].tBracket.mean() - timeC; - if (diff > minDiffA) { - break; // all following will be the same - } - if (diff < -minDiffA) { - continue; - } - dtimes.push_back(diff); - minDiffFT0 = diff; - minDiffA = std::abs(minDiffFT0); - } - } - mTglITSTPC.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); - } -#ifdef _ALLOW_DEBUG_TREES_ - if (mDBGOut) { - (*mDBGOut) << "refit" - << "tpcOrig=" << mTPCTracksArray[tTPC.sourceID] << "itsOrig=" << itsTrOrig << "itsRef=" << tITS << "tpcRef=" << tTPC << "matchRefit=" << trfit << "timeCorr=" << timeC << "dTimeFT0=" << minDiffFT0 << "dTimes=" << dtimes; - if (mMCTruthON) { - (*mDBGOut) << "refit" - << "itsLbl=" << mITSLblWork[iITS] << "tpcLbl=" << mTPCLblWork[iTPC]; - } - (*mDBGOut) << "refit" - << "\n"; - } -#endif - - // trfit.print(); // DBG - return true; } //______________________________________________ -bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) +bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs) { ///< refit AfterBurner track const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[seed.tpcWID]; const auto& winLink = seed.getLink(seed.winLinkID); - auto& newtr = mMatchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster + auto& newtr = matchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster auto& tracOut = newtr.getParamOut(); auto& tofL = newtr.getLTIntegralOut(); auto geom = o2::its::GeometryTGeo::Instance(); auto propagator = o2::base::Propagator::Instance(); tracOut.resetCovariance(); + o2::track::TrackPar refLin(tracOut); propagator->estimateLTFast(tofL, winLink); // guess about initial value for the track integral from the origin - // refit track outward in the ITS - const auto& itsClRefs = mABTrackletRefs[iITSAB]; + const auto& itsClRefs = ABTrackletRefs[iITSAB]; int nclRefit = 0, ncl = itsClRefs.getNClusters(); - float chi2 = 0.f; // NOTE: the ITS cluster absolute indices are stored from inner to outer layers for (int icl = itsClRefs.getFirstEntry(); icl < itsClRefs.getEntriesBound(); icl++) { - const auto& clus = mITSClustersArray[mABTrackletClusterIDs[icl]]; + const auto& clus = mITSClustersArray[ABTrackletClusterIDs[icl]]; float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX(); - if (!tracOut.rotate(alpha) || + if (!tracOut.rotate(alpha, refLin, propagator->getNominalBz()) || // note: here we also calculate the L,T integral // note: we should eventually use TPC pid in the refit (TODO) // note: since we are at small R, we can use field BZ component at origin rather than 3D field - !propagator->propagateToX(tracOut, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) { + !propagator->propagateToX(tracOut, refLin, x, propagator->getNominalBz(), MaxSnp, maxStep, mUseMatCorrFlag, &tofL)) { break; } chi2 += tracOut.getPredictedChi2(clus); @@ -1484,7 +1780,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) if (nclRefit != ncl) { LOGP(debug, "AfterBurner refit in ITS failed after ncl={}, match between TPC track #{} and ITS tracklet #{}", nclRefit, tTPC.sourceID, iITSAB); LOGP(debug, "{:s}", tracOut.asString()); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } // perform TPC refit with interaction time constraint @@ -1493,40 +1789,30 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) { float xtogo = 0; if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || - !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { + !propagator->PropagateToXBxByBz(tracOut, refLin, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } float chi2Out = 0; auto posStart = tracOut.getXYZGlo(); - int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), (timeC + mTPCDriftTimeOffset) * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit + int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), timeC * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); - // account path integrals - float dX = posEnd.x() - posStart.x(), dY = posEnd.y() - posStart.y(), dZ = posEnd.z() - posStart.z(), d2XY = dX * dX + dY * dY; - if (mFieldON) { // circular arc = 2*R*asin(dXY/2R) - float b[3]; - o2::math_utils::Point3D posAv(0.5 * (posEnd.x() + posStart.x()), 0.5 * (posEnd.y() + posStart.y()), 0.5 * (posEnd.z() + posStart.z())); - propagator->getFieldXYZ(posAv, b); - float curvH = std::abs(0.5f * tracOut.getCurvature(b[2])), arcXY = 1. / curvH * std::asin(curvH * std::sqrt(d2XY)); - d2XY = arcXY * arcXY; - } - auto lInt = std::sqrt(d2XY + dZ * dZ); - tofL.addStep(lInt, tracOut.getP2Inv()); + auto lInt = propagator->estimateLTIncrement(tracOut, posStart, posEnd); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); - - const auto& trackTune = o2::globaltracking::TrackTuneParams::Instance(); + const auto& trackTune = TrackTuneParams::Instance(); if (trackTune.useTPCOuterCorr) { tracOut.updateParams(trackTune.tpcParOuter); } - if (trackTune.tpcCovOuterType != o2::globaltracking::TrackTuneParams::AddCovType::Disable) { - tracOut.updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == o2::globaltracking::TrackTuneParams::AddCovType::WithCorrelations); + if (trackTune.tpcCovOuterType != TrackTuneParams::AddCovType::Disable) { + tracOut.updateCov(mCovDiagOuter, trackTune.tpcCovOuterType == TrackTuneParams::AddCovType::WithCorrelations); } } @@ -1677,18 +1963,29 @@ int MatchTPCITS::prepareInteractionTimes() } //______________________________________________ -void MatchTPCITS::runAfterBurner() +bool MatchTPCITS::runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { mTimer[SWABSeeds].Start(false); + mNABRefsClus = 0; prepareABSeeds(); int nIntCand = mInteractions.size(), nABSeeds = mTPCABSeeds.size(); LOGP(info, "AfterBurner will check {} seeds from {} TPC tracks and {} interaction candidates with {} threads", nABSeeds, mTPCABIndexCache.size(), nIntCand, mNThreads); // TMP mTimer[SWABSeeds].Stop(); if (!nIntCand || !mTPCABSeeds.size()) { - return; + return false; } mTimer[SWABMatch].Start(false); + std::vector itsChipClRefsBuff(mNThreads); +#ifdef ENABLE_UPGRADES + // with upgrades the datatype changed, hence we need to initialize + // each element individually + std::generate(itsChipClRefsBuff.begin(), itsChipClRefsBuff.end(), []() { + return ITSChipClustersRefs(o2::its::GeometryTGeo::Instance()->getNumberOfChips()); + }); +#endif + #ifdef WITH_OPENMP #pragma omp parallel for schedule(dynamic) num_threads(mNThreads) #endif @@ -1699,13 +1996,13 @@ void MatchTPCITS::runAfterBurner() continue; } #ifdef WITH_OPENMP - int tid = omp_get_thread_num(); + uint8_t tid = (uint8_t)omp_get_thread_num(); #else - int tid = 0; + uint8_t tid = 0; #endif fillClustersForAfterBurner(intCand.rofITS, 1, itsChipClRefsBuff[tid]); // RS FIXME account for possibility of filling 2 ROFs for (int is = intCand.seedsRef.getFirstEntry(); is < intCand.seedsRef.getEntriesBound(); is++) { // loop over all seeds of this interaction candidate - processABSeed(is, itsChipClRefsBuff[tid]); + processABSeed(is, itsChipClRefsBuff[tid], tid); } } mTimer[SWABMatch].Stop(); @@ -1725,16 +2022,7 @@ void MatchTPCITS::runAfterBurner() if (ABSeed.isDisabled()) { continue; } - if (ABSeed.lowestLayer > mParams->requireToReachLayerAB) { - ABSeed.disable(); - continue; - } - auto candID = ABSeed.getBestLinkID(); - if (candID < 0 || ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { - ABSeed.disable(); - continue; - } - candAB.emplace_back(SID{i, ABSeed.getLink(candID).chi2Norm()}); + candAB.emplace_back(SID{i, ABSeed.getLink(ABSeed.getBestLinkID()).chi2Norm()}); } std::sort(candAB.begin(), candAB.end(), [](SID a, SID b) { return a.chi2 < b.chi2; }); for (int i = 0; i < (int)candAB.size(); i++) { @@ -1758,23 +2046,37 @@ void MatchTPCITS::runAfterBurner() ABSeed.validate(bestID); ABSeed.flagLinkUsedClusters(bestID, mABClusterLinkIndex); mABWinnersIDs.push_back(tTPC.matchID = candAB[i].seedID); + mNABRefsClus += ABSeed.getNLayers(); nwin++; // RSTMP LOG(info) << "Iter: " << iter << " validated seed " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "] for TPC track " << ABSeed.tpcWID << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(link.nContLayers); } mTimer[SWABWinners].Stop(); mTimer[SWABRefit].Start(false); - refitABWinners(); + refitABWinners(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); mTimer[SWABRefit].Stop(); + return true; } //______________________________________________ -void MatchTPCITS::refitABWinners() +void MatchTPCITS::refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { - mABTrackletClusterIDs.reserve(mABWinnersIDs.size() * (o2::its::RecoGeomHelper::getNLayers() - mParams->lowestLayerAB)); - mABTrackletRefs.reserve(mABWinnersIDs.size()); + // refit normal matches + refitWinners(matchedTracks, matchLabels, calib); + + ABTrackletClusterIDs.reserve(mNABRefsClus); + ABTrackletRefs.reserve(mABWinnersIDs.size()); if (mMCTruthON) { - mABTrackletLabels.reserve(mABWinnersIDs.size()); + ABTrackletLabels.reserve(mABWinnersIDs.size()); + } + if (matchedTracks.capacity() < mABWinnersIDs.size() + matchedTracks.size()) { + LOGP(warn, "need to expand matched tracks container from {} to {}", matchedTracks.capacity(), mABWinnersIDs.size() + matchedTracks.size()); + matchedTracks.reserve(mABWinnersIDs.size() + matchedTracks.size()); + if (mMCTruthON) { + matchLabels.reserve(mABWinnersIDs.size() + matchedTracks.size()); + } } + std::map labelOccurence; auto accountClusterLabel = [&labelOccurence, itsClLabs = mITSClsLabels](int clID) { auto labels = itsClLabs->getLabels(clID); @@ -1787,30 +2089,31 @@ void MatchTPCITS::refitABWinners() for (auto wid : mABWinnersIDs) { const auto& ABSeed = mTPCABSeeds[wid]; - int start = mABTrackletClusterIDs.size(); + int start = ABTrackletClusterIDs.size(); int lID = ABSeed.winLinkID, ncl = 0; - auto& clref = mABTrackletRefs.emplace_back(start, ncl); + auto& clref = ABTrackletRefs.emplace_back(start, ncl); while (lID > MinusOne) { const auto& winL = ABSeed.getLink(lID); if (winL.clID > MinusOne) { - mABTrackletClusterIDs.push_back(winL.clID); + ABTrackletClusterIDs.push_back(winL.clID); ncl++; clref.pattern |= 0x1 << winL.layerID; + clref.setClusterSize(winL.layerID, mITSClusterSizes[winL.clID]); if (mMCTruthON) { accountClusterLabel(winL.clID); } } lID = winL.parentID; } - if (!refitABTrack(mABTrackletRefs.size() - 1, ABSeed)) { // on failure, destroy added tracklet reference - mABTrackletRefs.pop_back(); - mABTrackletClusterIDs.resize(start); + clref.setEntries(ncl); + if (!refitABTrack(ABTrackletRefs.size() - 1, ABSeed, matchedTracks, ABTrackletClusterIDs, ABTrackletRefs)) { // on failure, destroy added tracklet reference + ABTrackletRefs.pop_back(); + ABTrackletClusterIDs.resize(start); // RSS if (mMCTruthON) { labelOccurence.clear(); } continue; } - clref.setEntries(ncl); if (mMCTruthON) { o2::MCCompLabel lab; int maxL = 0; // find most encountered label @@ -1824,21 +2127,23 @@ void MatchTPCITS::refitABWinners() lab.setFakeFlag(); } labelOccurence.clear(); - mABTrackletLabels.push_back(lab); // ITSAB tracklet label - auto& lblGlo = mOutLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); + ABTrackletLabels.push_back(lab); // ITSAB tracklet label + auto& lblGlo = matchLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); lblGlo.setFakeFlag(lab != lblGlo); LOG(debug) << "ABWinner ncl=" << ncl << " mcLBAB " << lab << " mcLBGlo " << lblGlo << " chi2=" << ABSeed.getLink(ABSeed.winLinkID).chi2Norm() << " pT = " << ABSeed.track.getPt(); } // build MC label } - LOG(info) << "AfterBurner validated " << mABTrackletRefs.size() << " tracks"; + LOG(info) << "AfterBurner validated " << ABTrackletRefs.size() << " tracks"; } //______________________________________________ -void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs) +void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs, uint8_t tID) { // prepare matching hypothesis tree for given seed auto& ABSeed = mTPCABSeeds[sid]; + ABSeed.threadID = tID; + ABSeed.linksEntry = mABLinksPool.threadPool[tID].size(); followABSeed(ABSeed.track, itsChipClRefs, MinusTen, NITSLayers - 1, ABSeed); // check matches on outermost layer for (int ilr = NITSLayers - 1; ilr > mParams->lowestLayerAB; ilr--) { int nextLinkID = ABSeed.firstInLr[ilr]; @@ -1856,6 +2161,16 @@ void MatchTPCITS::processABSeed(int sid, const ITSChipClustersRefs& itsChipClRef nextLinkID = next2nextLinkID; } } + // is this seed has chance to be validated? + auto candID = ABSeed.getBestLinkID(); + if (ABSeed.isDisabled() || + ABSeed.lowestLayer > mParams->requireToReachLayerAB || + candID < 0 || + ABSeed.getLink(candID).nContLayers < mParams->minContributingLayersAB) { // free unused links + ABSeed.disable(); + mABLinksPool.threadPool[tID].resize(size_t(ABSeed.linksEntry)); + } + /* // RS FIXME remove on final clean-up auto bestLinkID = ABSeed.getBestLinkID(); if (bestLinkID>MinusOne) { @@ -2060,13 +2375,13 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa { // registers new ABLink on the layer, assigning provided kinematics. The link will be registered in a // way preserving the quality ordering of the links on the layer - int lnkID = ABSeed.trackLinks.size(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0); + int lnkID = ABSeed.getNLinks(), nextID = ABSeed.firstInLr[lr], nc = 1 + (parentID > MinusOne ? ABSeed.getLink(parentID).nContLayers : 0); float chi2 = chi2Cl + (parentID > MinusOne ? ABSeed.getLink(parentID).chi2 : 0.); // LOG(info) << "Reg on lr " << lr << " nc = " << nc << " chi2cl=" << chi2Cl << " -> " << chi2; // RSTMP if (ABSeed.firstInLr[lr] == MinusOne) { // no links on this layer yet ABSeed.firstInLr[lr] = lnkID; - ABSeed.trackLinks.emplace_back(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); return lnkID; } // add new link sorting links of this layer in quality @@ -2078,7 +2393,7 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa bool newIsBetter = parentID <= MinusOne ? isBetter(chi2, nextLink.chi2) : isBetter(ABSeed.getLink(parentID).chi2NormPredict(chi2Cl), nextLink.chi2Norm()); if (newIsBetter) { // need to insert new link before nextLink if (count < mParams->maxABLinksOnLayer) { // will insert in front of nextID - ABSeed.trackLinks.emplace_back(trc, clID, parentID, nextID, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, nextID, lr, nc, laddID, chi2); if (topID == MinusOne) { // are we comparing new link with best link on the layer? ABSeed.firstInLr[lr] = lnkID; // flag as best on the layer } else { @@ -2095,7 +2410,7 @@ int MatchTPCITS::registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackPa } while (nextID > MinusOne); // new link is worse than all others, add it only if there is a room to expand if (count < mParams->maxABLinksOnLayer) { - ABSeed.trackLinks.emplace_back(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); + ABSeed.addLink(trc, clID, parentID, MinusOne, lr, nc, laddID, chi2); if (topID > MinusOne) { ABSeed.getLink(topID).nextOnLr = lnkID; // point from previous one } @@ -2372,6 +2687,168 @@ int MatchTPCITS::preselectChipClusters(std::vector& clVecOut, const ClusRan return clVecOut.size(); } +//__________________________________________________________ +void MatchTPCITS::reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) +{ + size_t sizTotShm = 0, capTotShm = 0, sizTot = 0, capTot = 0, siz = 0, cap = 0, cnt = 0, cntCap = 0; + { + siz = matchedTracks.size() * sizeof(o2::dataformats::TrackTPCITS); + cap = matchedTracks.capacity() * sizeof(o2::dataformats::TrackTPCITS); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchedTracks : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletRefs.size() * sizeof(o2::itsmft::TrkClusRef); + cap = ABTrackletRefs.capacity() * sizeof(o2::itsmft::TrkClusRef); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletRefs : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletClusterIDs.size() * sizeof(int); + cap = ABTrackletClusterIDs.capacity() * sizeof(int); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletClusterIDs : size {:9} cap {:9}", siz, cap); + // + siz = matchLabels.size() * sizeof(o2::MCCompLabel); + cap = matchLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchLabels : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletLabels.size() * sizeof(o2::MCCompLabel); + cap = ABTrackletLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletLabels : size {:9} cap {:9}", siz, cap); + // + siz = calib.size() * sizeof(o2::dataformats::Triplet); + cap = calib.capacity() * sizeof(o2::dataformats::Triplet); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, calib : size {:9} cap {:9}", siz, cap); + } + { + siz = mITSClustersArray.size() * sizeof(ITSCluster); + cap = mITSClustersArray.capacity() * sizeof(ITSCluster); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSClustersArray : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsTPC.size() * sizeof(MatchRecord); + cap = mMatchRecordsTPC.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsTPC : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsITS.size() * sizeof(MatchRecord); + cap = mMatchRecordsITS.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsITS : size {:9} cap {:9}", siz, cap); + // + siz = mITSROFTimes.size() * sizeof(BracketF); + cap = mITSROFTimes.capacity() * sizeof(BracketF); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSROFTimes : size {:9} cap {:9}", siz, cap); + // + siz = mTPCWork.size() * sizeof(TrackLocTPC); + cap = mTPCWork.capacity() * sizeof(TrackLocTPC); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCWork : size {:9} cap {:9}", siz, cap); + // + siz = mITSWork.size() * sizeof(TrackLocITS); + cap = mITSWork.capacity() * sizeof(TrackLocITS); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSWork : size {:9} cap {:9}", siz, cap); + // + siz = mWinnerChi2Refit.size() * sizeof(float); + cap = mWinnerChi2Refit.capacity() * sizeof(float); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mWinnerChi2Refit : size {:9} cap {:9}", siz, cap); + // + siz = mTPCABSeeds.size() * sizeof(float); + cap = mTPCABSeeds.capacity() * sizeof(float); + cnt = 0; + cntCap = 0; + for (const auto& a : mTPCABSeeds) { + siz += a.sizeInternal(); + cap += a.capInternal(); + cnt += a.getNLinks(); + cntCap += a.getNLinks(); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABSeeds : size {:9} cap {:9} | internals size:{}/capacity:{} for {} elements", siz, cap, cnt, cntCap, mTPCABSeeds.size()); + // + siz = mTPCABIndexCache.size() * sizeof(int); + cap = mTPCABIndexCache.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABIndexCache : size {:9} cap {:9}", siz, cap); + // + siz = mABWinnersIDs.size() * sizeof(int); + cap = mABWinnersIDs.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABWinnersIDs : size {:9} cap {:9}", siz, cap); + // + siz = mABClusterLinkIndex.size() * sizeof(int); + cap = mABClusterLinkIndex.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABClusterLinkIndex : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCSectIndexCache[is].size() * sizeof(int); + cap += mTPCSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSSectIndexCache[is].size() * sizeof(int); + cap += mITSSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCTimeStart[is].size() * sizeof(int); + cap += mTPCTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCTimeStart : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSTimeStart[is].size() * sizeof(int); + cap += mITSTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSTimeStart : size {:9} cap {:9}", siz, cap); + // + siz = mITSTrackROFContMapping.size() * sizeof(int); + cap = mITSTrackROFContMapping.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, ITSTrackROFContMapping: size {:9} cap {:9}", siz, cap); + } + LOGP(info, "TotalSizes/Capacities: SHM: {}/{} Heap: {}/{}", sizTotShm, capTotShm, sizTot, capTot); +} + //__________________________________________________________ void MatchTPCITS::setNThreads(int n) { @@ -2381,6 +2858,8 @@ void MatchTPCITS::setNThreads(int n) LOG(warning) << "Multithreading is not supported, imposing single thread"; mNThreads = 1; #endif + mABLinksPool.threadPool.resize(mNThreads); + TPCABSeed::gLinksPool = &mABLinksPool; } //<<============================= AfterBurner for TPC-track / ITS cluster matching ===================<< @@ -2397,6 +2876,56 @@ void MatchTPCITS::setDebugFlag(UInt_t flag, bool on) } } +//_________________________________________________________ +void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex) +{ + ///< fill debug tree for TPC original tracks (passing pT cut) + mTimer[SWDBG].Start(false); + const auto& tpcOrig = mTPCTracksArray[tpcIndex]; + uint8_t clSect = 0, clRow = 0, prevRow = 0xff, padFromEdge = -1; + uint32_t clIdx = 0; + int nshared = 0; + std::array shMap{}; + bool prevRawShared = false; + for (int i = 0; i < tpcOrig.getNClusterReferences(); i++) { + tpcOrig.getClusterReference(mTPCTrackClusIdx, i, clSect, clRow, clIdx); + unsigned int absoluteIndex = mTPCClusterIdxStruct->clusterOffset[clSect][clRow] + clIdx; + if (mTPCRefitterShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!(prevRow == clRow && prevRawShared)) { + nshared++; + } + prevRow = clRow; + prevRawShared = true; + } + } + const auto& clus = mTPCClusterIdxStruct->clusters[clSect][clRow][clIdx]; + padFromEdge = uint8_t(clus.getPad()); + if (padFromEdge > TPCGeometry.NPads(clRow) / 2) { + padFromEdge = TPCGeometry.NPads(clRow) - 1 - padFromEdge; + } + int tb = tpcOrig.getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); + (*mDBGOut) << "tpcOrig" + << "tf=" << mTFCount + << "index=" << tpcIndex + << "acc=" << acc + << "chi2TPC=" << tpcOrig.getChi2() + << "nClus=" << tpcOrig.getNClusters() + << "nShared=" << nshared + << "time0=" << tpcOrig.getTime0() + << "trc=" << ((o2::track::TrackParCov&)tpcOrig) + << "minRow=" << clRow + << "padFromEdge=" << padFromEdge + << "multTPC=" << mltTPC; + if (mMCTruthON) { + (*mDBGOut) << "tpcOrig" + << "tpcLbl=" << mTPCTrkLabels[tpcIndex]; + } + (*mDBGOut) << "tpcOrig" + << "\n"; + mTimer[SWDBG].Stop(); +} + //_________________________________________________________ void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float chi2, float tCorr) { @@ -2409,17 +2938,20 @@ void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float c if (chi2 < 0.) { // need to recalculate chi2 = getPredictedChi2NoZ(trackITS, trackTPC); } - o2::MCCompLabel lblITS, lblTPC; (*mDBGOut) << "match" << "tf=" << mTFCount << "chi2Match=" << chi2 << "its=" << trackITS << "tpc=" << trackTPC << "tcorr=" << tCorr; if (mMCTruthON) { - lblITS = mITSLblWork[itsID]; - lblTPC = mTPCLblWork[tpcID]; (*mDBGOut) << "match" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[itsID] << "tpcLbl=" << mTPCLblWork[tpcID]; } + int tb = mTPCTracksArray[trackTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); (*mDBGOut) << "match" - << "rejFlag=" << rejFlag << "\n"; + << "rejFlag=" << rejFlag + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[trackITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[trackITS.roFrame].getNEntries() + << "\n"; mTimer[SWDBG].Stop(); } @@ -2444,14 +2976,16 @@ void MatchTPCITS::dumpWinnerMatches() (*mDBGOut) << "matchWin" << "tf=" << mTFCount << "chi2Match=" << itsMatchRec.chi2 << "chi2Refit=" << mWinnerChi2Refit[iits] << "its=" << tITS << "tpc=" << tTPC; - o2::MCCompLabel lblITS, lblTPC; if (mMCTruthON) { - lblITS = mITSLblWork[iits]; - lblTPC = mTPCLblWork[itpc]; (*mDBGOut) << "matchWin" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[iits] << "tpcLbl=" << mTPCLblWork[itpc]; } + int tb = mTPCTracksArray[tTPC.sourceID].getTime0() * mNTPCOccBinLengthInv; + float mltTPC = tb < 0 ? mTBinClOcc[0] : (tb >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[tb]); (*mDBGOut) << "matchWin" + << "multTPC=" << mltTPC + << "multITSTr=" << mITSTrackROFRec[tITS.roFrame].getNEntries() + << "multITSCl=" << mITSClusterROFRec[tITS.roFrame].getNEntries() << "\n"; } mTimer[SWDBG].Stop(); diff --git a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt index df8942199c1cb..6f29ab1930f95 100644 --- a/Detectors/GlobalTrackingWorkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/CMakeLists.txt @@ -11,6 +11,8 @@ # FIXME: do we actually need a library here, or is the executable enough ? +#add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library(GlobalTrackingWorkflow SOURCES src/TrackWriterTPCITSSpec.cxx src/TPCITSMatchingSpec.cxx @@ -26,10 +28,12 @@ o2_add_library(GlobalTrackingWorkflow src/TrackCosmicsWriterSpec.cxx src/TOFMatcherSpec.cxx src/TOFMatchChecker.cxx + src/HMPMatcherSpec.cxx src/TOFEventTimeChecker.cxx src/GlobalFwdTrackWriterSpec.cxx src/GlobalFwdMatchingAssessmentSpec.cxx src/MatchedMFTMCHWriterSpec.cxx + src/ReaderDriverSpec.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers @@ -38,12 +42,19 @@ o2_add_library(GlobalTrackingWorkflow O2::ITSWorkflow O2::MFTTracking O2::MFTWorkflow + O2::HMPIDWorkflow O2::TPCWorkflow O2::FT0Workflow O2::ITSMFTWorkflow O2::SimulationDataFormat O2::DetectorsVertexing - O2::StrangenessTracking) + O2::StrangenessTracking + $<$:O2::ITS3Reconstruction>) + +o2_add_executable(driver-workflow + COMPONENT_NAME reader + SOURCES src/reader-driver-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow ) o2_add_executable(match-workflow COMPONENT_NAME tpcits @@ -92,6 +103,11 @@ o2_add_executable(match-eventtime-workflow SOURCES src/tof-eventtime-checker-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow O2::TOFWorkflowIO) +o2_add_executable(matcher-workflow + COMPONENT_NAME hmpid + SOURCES src/hmp-matcher-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflow O2::HMPIDWorkflow) + o2_add_executable(matcher-workflow COMPONENT_NAME globalfwd SOURCES src/globalfwd-matcher-workflow.cxx diff --git a/Detectors/GlobalTrackingWorkflow/README.md b/Detectors/GlobalTrackingWorkflow/README.md index 2042f89a582ed..95693115d932d 100644 --- a/Detectors/GlobalTrackingWorkflow/README.md +++ b/Detectors/GlobalTrackingWorkflow/README.md @@ -28,4 +28,23 @@ o2-cosmics-match-workflow --shm-segment-size 10000000000 --run | tee cosmics.log One can account contributions of a limited set of track sources (currently by default: ITS, TPC, ITS-TPC, TPC-TOF, ITS-TPC-TOF) by providing optiont `--track-sources`. +## Using TF throttling when reading root files from detectors processing (tracks, clusters etc.) +The workflows driven by the input from the root files produced by detectors (e.g. by the `o2-global-track-cluster-reader`), can be preceded by the +`o2-reader-driver-workflow` which will allow to have TF throttling via usual `--timeframes-rate-limit ` and `--timeframes-rate-limit-ipcid ` +options. +The `o2-reader-driver-workflow` as well as all reader workflows must be provided with the `--hbfutils-config ,upstream` option, with being the file produced by the +`o2-tfidinfo-writer-workflow` (usually o2_tfidinfo.root file). If this file is in the working directory, then the `--hbfutils-config` option can be shortened to `upstream` only. + +The `o2-reader-driver-workflow` is not supported for `o2simdigitizerworkflow_configuration.ini` version of the `--hbfutils-config`, since it is used only for MC, +where by construction there is only 1 TF, hence the throttling is meaningless. + +Option `--max-tf ` of the `o2-reader-driver-workflow` allows to inject only 1st TFs by the dowsntream readers. + +A typical invocation of the throttled workflow is: + +``` +GLOSET=" --shm-segment-size 24000000000 --timeframes-rate-limit 2 --timeframes-rate-limit-ipcid 0" +HBFSET=" --hbfutils-config upstream,o2_tfidinfo.root " +o2-reader-driver-workflow $GLOSET $HBFSET --max-tf 3 | o2-global-track-cluster-reader $GLOSET $HBFSET --disable-mc --track-types <...> --cluster-types <...> | [ $GLOSET $HBFSET ] | $GLOSET --run +``` diff --git a/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt index 9f65236f4cd2b..8ae5378222f5d 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/helpers/CMakeLists.txt @@ -24,6 +24,7 @@ o2_add_library(GlobalTrackingWorkflowHelpers O2::GlobalTrackingWorkflowReaders O2::TOFWorkflowIO O2::FT0Workflow + O2::HMPIDWorkflow O2::FV0Workflow O2::FDDWorkflow O2::ZDCWorkflow diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx index 3cde15554ad3d..c6c163f4b8911 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx @@ -18,7 +18,10 @@ #include "MFTWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "HMPIDWorkflow/ClustersReaderSpec.h" +#include "HMPIDWorkflow/HMPMatchedReaderSpec.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/GlobalFwdTrackReaderSpec.h" #include "GlobalTrackingWorkflowReaders/MatchedMFTMCHReaderSpec.h" @@ -40,8 +43,8 @@ #include "MCHIO/TrackReaderSpec.h" #include "MCHIO/ClusterReaderSpec.h" #include "MIDWorkflow/TrackReaderSpec.h" -#include "PHOSWorkflow/ReaderSpec.h" -#include "CPVWorkflow/ReaderSpec.h" +#include "PHOSWorkflow/CellReaderSpec.h" +#include "CPVWorkflow/ClusterReaderSpec.h" #include "EMCALWorkflow/PublisherSpec.h" // #include "StrangenessTrackingWorkflow/StrangenessTrackingReaderSpec.h" @@ -66,7 +69,7 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& maskTracksMC = GID::getSourcesMask(GID::NONE); } else { // some detectors do not support MC labels - if (maskClustersMC[GID::MCH]) { + if (maskClusters[GID::MCH] && maskClustersMC[GID::MCH]) { LOG(warn) << "MCH global clusters do not support MC lables, disabling"; maskClustersMC &= ~GID::getSourceMask(GID::MCH); } @@ -95,6 +98,9 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterReaderSpec(maskClustersMC[GID::TPC])); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); + } } if (maskTracks[GID::TPC] && maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterSharingMapSpec()); @@ -122,9 +128,15 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& maskMatches[GID::ITSTPCTOF] || maskMatches[GID::ITSTPCTRDTOF] || maskMatches[GID::TPCTRDTOF]) { specs.emplace_back(o2::tof::getClusterReaderSpec(maskClustersMC[GID::TOF])); } + if (maskClusters[GID::HMP]) { + specs.emplace_back(o2::hmpid::getClusterReaderSpec()); + } if (maskMatches[GID::TPCTOF] || maskTracks[GID::TPCTOF]) { specs.emplace_back(o2::tof::getTOFMatchedReaderSpec(maskTracksMC[GID::TPCTOF], 0, maskTracks[GID::TPCTOF], subSpecStrict)); } + if (maskMatches[GID::HMP]) { + specs.emplace_back(o2::hmpid::getHMPMatchedReaderSpec(maskTracksMC[GID::HMP])); + } if (maskTracks[GID::FT0] || maskClusters[GID::FT0]) { specs.emplace_back(o2::ft0::getRecPointReaderSpec(maskTracksMC[GID::FT0] || maskClustersMC[GID::FT0])); } @@ -152,11 +164,11 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskTracks[GID::PHS] || maskClusters[GID::PHS]) { - specs.emplace_back(o2::phos::getCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); + specs.emplace_back(o2::phos::getPHOSCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); } if (maskTracks[GID::CPV] || maskClusters[GID::CPV]) { - specs.emplace_back(o2::cpv::getClustersReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); } if (maskTracks[GID::EMC] || maskClusters[GID::EMC]) { @@ -164,7 +176,7 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskClusters[GID::MCH]) { - specs.emplace_back(o2::mch::getClusterReaderSpec(maskClustersMC[GID::MCH], "mch-global-cluster-reader", true, false)); + specs.emplace_back(o2::mch::getClusterReaderSpec(maskClustersMC[GID::MCH], "mch-cluster-reader", true, true)); } return 0; diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx index 34f445ed003da..17ad5f09027e1 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/NoInpDummyOutSpec.cxx @@ -47,7 +47,7 @@ void NoInpDummyOut::run(ProcessingContext& pc) { static int counter = 0; // send just once dummy output to trigger the ccdb-fetcher - pc.outputs().make>(Output{"GLO", "DUMMY_OUT", 0, Lifetime::Timeframe}); + pc.outputs().make>(Output{"GLO", "DUMMY_OUT", 0}); if (mLoops >= 0 && ++counter >= mLoops) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(framework::QuitRequest::Me); diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h index 25553c5d56d33..e0e74c3058086 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h @@ -20,11 +20,16 @@ using namespace o2::framework; namespace o2 { +namespace tpc +{ +struct CorrectionMapsLoaderGloOpts; +} + namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC); +framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h index d6803952c5f97..b1b9bf727c876 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/GlobalFwdMatchingAssessmentSpec.h @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "GlobalTracking/MatchGlobalFwdAssessment.h" +#include "DetectorsBase/GRPGeomHelper.h" #include "TStopwatch.h" using namespace o2::framework; @@ -28,17 +29,21 @@ namespace globaltracking class GlobalFwdAssessmentSpec : public Task { public: - GlobalFwdAssessmentSpec(bool useMC, bool processGen, bool midFilterDisabled, bool finalizeAnalysis = false) : mUseMC(useMC), - mMIDFilterDisabled(midFilterDisabled), - mProcessGen(processGen), - mFinalizeAnalysis(finalizeAnalysis){}; + GlobalFwdAssessmentSpec(bool useMC, bool processGen, std::shared_ptr gr, bool midFilterDisabled, bool finalizeAnalysis = false) + : mUseMC(useMC), + mMIDFilterDisabled(midFilterDisabled), + mProcessGen(processGen), + mGGCCDBRequest(gr), + mFinalizeAnalysis(finalizeAnalysis){}; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj); private: void sendOutput(DataAllocator& output); std::unique_ptr mGloFwdAssessment; + std::shared_ptr mGGCCDBRequest; bool mUseMC = true; bool mProcessGen = false; bool mFinalizeAnalysis = false; @@ -64,4 +69,4 @@ DataProcessorSpec getGlobaFwdAssessmentSpec(bool useMC, bool processGen, bool mi } // namespace globaltracking } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h new file mode 100644 index 0000000000000..ca515b22859a9 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/HMPMatcherSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatcherSpec.h // ef ; change to hmp + +#ifndef O2_HMP_MATCHER_SPEC // hmp +#define O2_HMP_MATCHER_SPEC // + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace globaltracking +{ + +/// create a processor spec +framework::DataProcessorSpec getHMPMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, float extratolerancetrd, float extratolerancetof); + +} // namespace globaltracking +} // namespace o2 + +#endif /* O2_HMP_MATCHER_SPEC */ diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h index da56fdfe20a86..d5f5a8b2a0f06 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/PrimaryVertexingSpec.h @@ -24,7 +24,7 @@ namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getPrimaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC); +o2::framework::DataProcessorSpec getPrimaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC, bool useGeom); } // namespace vertexing } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h new file mode 100644 index 0000000000000..3cbf7e5d8f4fe --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/ReaderDriverSpec.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ReaderDriverSpec.h + +#ifndef O2_READER_DRIVER_ +#define O2_READER_DRIVER_ + +#include "Framework/DataProcessorSpec.h" +#include + +namespace o2 +{ +namespace globaltracking +{ + +/// create a processor spec +/// pushes an empty output to provide timing to downstream devices +framework::DataProcessorSpec getReaderDriverSpec(const std::string& metricChannel = "", size_t minSHM = 0); + +} // namespace globaltracking +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h index 3474b46bd11b4..b8071ae83d347 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h @@ -20,11 +20,16 @@ namespace o2 { +namespace tpc +{ +struct CorrectionMapsLoaderGloOpts; +} + namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body); +o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace vertexing } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h index 1b17fcdd4082b..8367cef7f51b0 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/StrangenessTrackingSpec.h @@ -59,9 +59,9 @@ class StrangenessTrackerSpec : public framework::Task std::unique_ptr mGRP = nullptr; }; -o2::framework::DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC); +o2::framework::DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useGeom); o2::framework::WorkflowSpec getWorkflow(bool upstreamClusters = false, bool upstreamV0s = false); } // namespace strangeness_tracking } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h index 46f58b088abd8..79a4ee0ce0360 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h @@ -21,11 +21,15 @@ using namespace o2::framework; namespace o2 { +namespace tpc +{ +struct CorrectionMapsLoaderGloOpts; +} namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd = 0., bool pushMatchable = 0); +framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes = 1); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h index 7d8839e89ba2b..4aaed7d64eec5 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h @@ -20,10 +20,14 @@ using namespace o2::framework; namespace o2 { +namespace tpc +{ +struct CorrectionMapsLoaderGloOpts; +} namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useMC); +framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt index 4a72fb8cede77..cca69282d20cf 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/qc/CMakeLists.txt @@ -14,9 +14,10 @@ o2_add_library(GlobalTrackingWorkflowQC PUBLIC_LINK_LIBRARIES O2::DataFormatsGlobalTracking O2::GlobalTracking ROOT::Core + O2::GLOQC ) -o2_add_executable(match-qc +o2_add_executable(matching-qc COMPONENT_NAME itstpc SOURCES src/itstpc-matching-qc-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingWorkflowQC diff --git a/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h b/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h index 48d96921287c6..6ffd38c8a1c1b 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h +++ b/Detectors/GlobalTrackingWorkflow/qc/include/GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h @@ -17,7 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "GlobalTracking/MatchITSTPCQC.h" +#include "GLOQC/MatchITSTPCQC.h" #include "DetectorsBase/GRPGeomHelper.h" using namespace o2::framework; @@ -29,7 +29,7 @@ namespace globaltracking class ITSTPCMatchingQCDevice : public Task { public: - ITSTPCMatchingQCDevice(std::shared_ptr dr, std::shared_ptr req, bool useMC) : mDataRequest(dr), mCCDBRequest(req), mUseMC(useMC){}; + ITSTPCMatchingQCDevice(std::shared_ptr dr, std::shared_ptr req, bool useMC, bool doK0QC, std::string trkSources) : mDataRequest(dr), mCCDBRequest(req), mUseMC(useMC), mDoK0QC(doK0QC), mTrkSources(trkSources){}; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; @@ -37,17 +37,19 @@ class ITSTPCMatchingQCDevice : public Task private: void sendOutput(DataAllocator& output); - std::unique_ptr mMatchITSTPCQC; + std::unique_ptr mMatchITSTPCQC; std::shared_ptr mDataRequest; std::shared_ptr mCCDBRequest; bool mUseMC = true; + bool mDoK0QC = true; + std::string mTrkSources = "ITS,TPC,ITS-TPC"; }; } // namespace globaltracking namespace framework { -DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC); +DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC, bool doK0QC, std::string trkSources); } // namespace framework } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx index 3653fd8bcc90b..db61300c4cf60 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx @@ -15,10 +15,11 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "GlobalTrackingWorkflowQC/ITSTPCMatchingQCSpec.h" -#include "GlobalTracking/ITSTPCMatchingQCParams.h" +#include "GLOQC/ITSTPCMatchingQCParams.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DetectorsBase/Propagator.h" - +#include "DetectorsVertexing/SVertexerParams.h" +#include "Framework/CCDBParamSpec.h" #include "CommonUtils/NameConf.h" #include @@ -29,22 +30,45 @@ namespace o2 namespace globaltracking { -void ITSTPCMatchingQCDevice::init(InitContext& ic) +void ITSTPCMatchingQCDevice::init(InitContext& /*ic*/) { - const o2::globaltracking::ITSTPCMatchingQCParams* params = &o2::globaltracking::ITSTPCMatchingQCParams::Instance(); + const o2::gloqc::ITSTPCMatchingQCParams& params = o2::gloqc::ITSTPCMatchingQCParams::Instance(); - mMatchITSTPCQC = std::make_unique(); - mMatchITSTPCQC->init(); + mMatchITSTPCQC = std::make_unique(); mMatchITSTPCQC->setDataRequest(mDataRequest); - mMatchITSTPCQC->setPtCut(params->minPtCut); - mMatchITSTPCQC->setEtaCut(params->etaCut); - mMatchITSTPCQC->setMinNTPCClustersCut(params->minNTPCClustersCut); - mMatchITSTPCQC->setMinDCAtoBeamPipeDistanceCut(params->minDCACut); - mMatchITSTPCQC->setMinDCAtoBeamPipeYCut(params->minDCACutY); + mMatchITSTPCQC->setTrkSources(o2::dataformats::GlobalTrackID::getSourcesMask(mTrkSources)); + mMatchITSTPCQC->setMinPtITSCut(params.minPtITSCut); + mMatchITSTPCQC->setEtaITSCut(params.etaITSCut); + mMatchITSTPCQC->setMinNClustersITS(params.minNITSClustersCut); + mMatchITSTPCQC->setMaxChi2PerClusterITS(params.maxChi2PerClusterITS); + mMatchITSTPCQC->setMinPtTPCCut(params.minPtTPCCut); + mMatchITSTPCQC->setEtaTPCCut(params.etaTPCCut); + mMatchITSTPCQC->setMinNTPCClustersCut(params.minNTPCClustersCut); + mMatchITSTPCQC->setMinDCAtoBeamPipeDistanceCut(params.minDCACut); + mMatchITSTPCQC->setMinDCAtoBeamPipeYCut(params.minDCACutY); + mMatchITSTPCQC->setPtCut(params.minPtCut); + mMatchITSTPCQC->setMaxPtCut(params.maxPtCut); + mMatchITSTPCQC->setEtaCut(params.etaCut); + mMatchITSTPCQC->setEtaNo0Cut(params.etaNo0Cut); + mMatchITSTPCQC->setCutK0Mass(params.cutK0Mass); + mMatchITSTPCQC->setMaxK0Eta(params.maxEtaK0); + mMatchITSTPCQC->setK0Scaling(params.K0Scaling); + mMatchITSTPCQC->setMinTPCOccpp(params.minTPCOccpp); + mMatchITSTPCQC->setMaxTPCOccpp(params.maxTPCOccpp); + mMatchITSTPCQC->setNBinsTPCOccpp(params.nBinsTPCOccpp); + mMatchITSTPCQC->setMinTPCOccPbPb(params.minTPCOccPbPb); + mMatchITSTPCQC->setMaxTPCOccPbPb(params.maxTPCOccPbPb); + mMatchITSTPCQC->setNBinsTPCOccPbPb(params.nBinsTPCOccPbPb); + mMatchITSTPCQC->setK0MaxDCA(params.maxK0DCA); + mMatchITSTPCQC->setK0MinCosPA(params.minK0CosPA); o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); if (mUseMC) { mMatchITSTPCQC->setUseMC(mUseMC); } + if (mDoK0QC) { + mMatchITSTPCQC->setDoK0QC(mDoK0QC); + } + mMatchITSTPCQC->init(); } //_____________________________________________________________ @@ -52,6 +76,11 @@ void ITSTPCMatchingQCDevice::init(InitContext& ic) void ITSTPCMatchingQCDevice::run(o2::framework::ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool wasSVParamInitialized = false; + if (!wasSVParamInitialized) { + pc.inputs().get("SVParam"); + wasSVParamInitialized = true; + } mMatchITSTPCQC->run(pc); } @@ -71,9 +100,13 @@ void ITSTPCMatchingQCDevice::sendOutput(DataAllocator& output) TObjArray objar; mMatchITSTPCQC->getHistos(objar); - output.snapshot(Output{"GLO", "ITSTPCMATCHQC", 0, Lifetime::Sporadic}, objar); + output.snapshot(Output{"GLO", "ITSTPCMATCHQC", 0}, objar); TFile* f = new TFile(Form("outITSTPCmatchingQC.root"), "RECREATE"); + if (f == nullptr) { + LOGP(error, "Cannot write QC to file 'outITSTPCmatchingQC.root'"); + return; + } objar.Write("ObjArray", TObject::kSingleKey); f->Close(); } @@ -83,6 +116,9 @@ void ITSTPCMatchingQCDevice::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("GLO", "SVPARAM", 0)) { + return; + } } } // namespace globaltracking @@ -90,27 +126,34 @@ namespace framework { using GID = o2::dataformats::GlobalTrackID; -DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC) +DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC, bool doK0QC, std::string trkSources) { std::vector outputs; outputs.emplace_back("GLO", "ITSTPCMATCHQC", 0, Lifetime::Sporadic); auto dataRequest = std::make_shared(); - GID::mask_t mSrc = GID::getSourcesMask("TPC,ITS-TPC"); - dataRequest->requestTracks(mSrc, useMC); + GID::mask_t srcMask = GID::getSourcesMask(trkSources); + dataRequest->requestTracks(srcMask, useMC); + if (doK0QC) { + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->requestTPCClusters(false); + } auto ccdbRequest = std::make_shared(false, // orbitResetTime - false, // GRPECS=true - false, // GRPLHCIF + true, // GRPECS=true + true, // GRPLHCIF true, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs); + + dataRequest->inputs.emplace_back("SVParam", "GLO", "SVPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/SVertexerParam")); return DataProcessorSpec{ - "itstpc-matching-qc", - dataRequest->inputs, - outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, useMC)}, - Options{{}}}; + .name = "itstpc-matching-qc", + .inputs = dataRequest->inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, useMC, doK0QC, trkSources)}, + }; } } // namespace framework diff --git a/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx b/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx index 16bd2d78d77e2..e3107bedbf1d4 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/qc/src/itstpc-matching-qc-workflow.cxx @@ -21,6 +21,8 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable use of MC information even if available"}}, + {"disable-k0-qc", o2::framework::VariantType::Bool, false, {"disable K0 QC"}}, + {"track-sources", o2::framework::VariantType::String, "ITS,TPC,ITS-TPC", {"comma-separated list of track sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; std::swap(workflowOptions, options); } @@ -33,12 +35,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - // write the configuration used for the workflow - o2::conf::ConfigurableParam::writeINI("o2-itstpc-matching-qc.ini"); LOG(info) << "ITSTPC matching QC: disable-mc = " << configcontext.options().get("disable-mc"); auto useMC = !configcontext.options().get("disable-mc"); + LOG(info) << "ITSTPC matching QC: disable-k0-qc = " << configcontext.options().get("disable-k0-qc"); + auto doK0QC = !configcontext.options().get("disable-k0-qc"); + LOG(info) << "ITSTPC matching QC: track-sources = " << configcontext.options().get("track-sources"); + std::string trkSources = configcontext.options().get("track-sources"); WorkflowSpec specs; - specs.emplace_back(getITSTPCMatchingQCDevice(useMC)); + specs.emplace_back(getITSTPCMatchingQCDevice(useMC, doK0QC, trkSources)); return specs; } diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx index 1fdc7b49616c3..11fa58333f89b 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/GlobalFwdTrackReaderSpec.cxx @@ -65,9 +65,9 @@ void GlobalFwdTrackReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Global Forward tracks at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "GLFWD", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "GLFWD", 0}, mTracks); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0}, mLabels); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx index 012542e448d19..c1810a1deb743 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/IRFrameReaderSpec.cxx @@ -63,7 +63,7 @@ void IRFrameReaderSpec::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(debug) << "Pushing " << mIRF.size() << " IR-frames in at entry " << ent; - pc.outputs().snapshot(Output{mDataOrigin, "IRFRAMES", mSubSpec, Lifetime::Timeframe}, mIRF); + pc.outputs().snapshot(Output{mDataOrigin, "IRFRAMES", mSubSpec}, mIRF); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx index 08c5c5871db84..5f02beebd1746 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/MatchedMFTMCHReaderSpec.cxx @@ -65,7 +65,7 @@ void MatchMFTMCHReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " MFTMCH matches at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0}, mTracks); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx index 06bdf8ff95a45..6e1aba8b2e1f3 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/PrimaryVertexReaderSpec.cxx @@ -84,12 +84,12 @@ void PrimaryVertexReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mVerticesPtr->size() << " vertices at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "PVTX", 0, Lifetime::Timeframe}, mVertices); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}, mPV2MatchIdx); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe}, mPV2MatchIdxRef); + pc.outputs().snapshot(Output{"GLO", "PVTX", 0}, mVertices); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0}, mPV2MatchIdx); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0}, mPV2MatchIdxRef); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0}, mLabels); } if (mVerbose) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx index 0f01618681035..9f252616c9d55 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/SecondaryVertexReaderSpec.cxx @@ -21,7 +21,7 @@ #include "CommonDataFormat/RangeReference.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "TFile.h" #include "TTree.h" @@ -35,9 +35,12 @@ namespace vertexing class SecondaryVertexReader : public o2::framework::Task { using RRef = o2::dataformats::RangeReference; + using V0Index = o2::dataformats::V0Index; using V0 = o2::dataformats::V0; + using CascadeIndex = o2::dataformats::CascadeIndex; using Cascade = o2::dataformats::Cascade; - using DecayNbody = o2::dataformats::DecayNbody; + using Decay3BodyIndex = o2::dataformats::Decay3BodyIndex; + using Decay3Body = o2::dataformats::Decay3Body; public: SecondaryVertexReader() = default; @@ -50,11 +53,14 @@ class SecondaryVertexReader : public o2::framework::Task bool mVerbose = false; + std::vector mV0sIdx, *mV0sIdxPtr = &mV0sIdx; std::vector mV0s, *mV0sPtr = &mV0s; std::vector mPV2V0Ref, *mPV2V0RefPtr = &mPV2V0Ref; + std::vector mCascsIdx, *mCascsIdxPtr = &mCascsIdx; std::vector mCascs, *mCascsPtr = &mCascs; std::vector mPV2CascRef, *mPV2CascRefPtr = &mPV2CascRef; - std::vector m3Bodys, *m3BodysPtr = &m3Bodys; + std::vector m3BodysIdx, *m3BodysIdxPtr = &m3BodysIdx; + std::vector m3Bodys, *m3BodysPtr = &m3Bodys; std::vector mPV23BodyRef, *mPV23BodyRefPtr = &mPV23BodyRef; std::unique_ptr mFile; @@ -62,10 +68,13 @@ class SecondaryVertexReader : public o2::framework::Task std::string mFileName = ""; std::string mFileNameMatches = ""; std::string mSVertexTreeName = "o2sim"; + std::string mV0IdxBranchName = "V0sID"; std::string mV0BranchName = "V0s"; std::string mPVertex2V0RefBranchName = "PV2V0Refs"; + std::string mCascIdxBranchName = "CascadesID"; std::string mCascBranchName = "Cascades"; std::string mPVertex2CascRefBranchName = "PV2CascRefs"; + std::string m3BodyIdxBranchName = "Decays3BodyID"; std::string m3BodyBranchName = "Decays3Body"; std::string mPVertex23BodyRefBranchName = "PV23BodyRefs"; }; @@ -82,14 +91,18 @@ void SecondaryVertexReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << "Pushing " << mV0s.size() << " V0s and " << mCascs.size() << " cascades at entry " << ent; - - pc.outputs().snapshot(Output{"GLO", "V0S", 0, Lifetime::Timeframe}, mV0s); - pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}, mPV2V0Ref); - pc.outputs().snapshot(Output{"GLO", "CASCS", 0, Lifetime::Timeframe}, mCascs); - pc.outputs().snapshot(Output{"GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}, mPV2CascRef); - pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}, m3Bodys); - pc.outputs().snapshot(Output{"GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}, mPV23BodyRef); + LOGP(info, "Pushing {} V0s ({} indices), {} cascades ({} indices) and {} 3-body ({} indices ) at entry {}", + mV0s.size(), mV0sIdx.size(), mCascs.size(), mCascsIdx.size(), m3Bodys.size(), m3BodysIdx.size(), ent); + + pc.outputs().snapshot(Output{"GLO", "V0S_IDX", 0}, mV0sIdx); + pc.outputs().snapshot(Output{"GLO", "V0S", 0}, mV0s); + pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0}, mPV2V0Ref); + pc.outputs().snapshot(Output{"GLO", "CASCS_IDX", 0}, mCascsIdx); + pc.outputs().snapshot(Output{"GLO", "CASCS", 0}, mCascs); + pc.outputs().snapshot(Output{"GLO", "PVTX_CASCREFS", 0}, mPV2CascRef); + pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY_IDX", 0}, m3BodysIdx); + pc.outputs().snapshot(Output{"GLO", "DECAYS3BODY", 0}, m3Bodys); + pc.outputs().snapshot(Output{"GLO", "PVTX_3BODYREFS", 0}, mPV23BodyRef); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -104,17 +117,23 @@ void SecondaryVertexReader::connectTree() assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mSVertexTreeName.c_str())); assert(mTree); + assert(mTree->GetBranch(mV0IdxBranchName.c_str())); assert(mTree->GetBranch(mV0BranchName.c_str())); assert(mTree->GetBranch(mPVertex2V0RefBranchName.c_str())); assert(mTree->GetBranch(mCascBranchName.c_str())); + assert(mTree->GetBranch(mCascIdxBranchName.c_str())); assert(mTree->GetBranch(mPVertex2CascRefBranchName.c_str())); + assert(mTree->GetBranch(m3BodyIdxBranchName.c_str())); assert(mTree->GetBranch(m3BodyBranchName.c_str())); assert(mTree->GetBranch(mPVertex23BodyRefBranchName.c_str())); + mTree->SetBranchAddress(mV0IdxBranchName.c_str(), &mV0sIdxPtr); mTree->SetBranchAddress(mV0BranchName.c_str(), &mV0sPtr); mTree->SetBranchAddress(mPVertex2V0RefBranchName.c_str(), &mPV2V0RefPtr); + mTree->SetBranchAddress(mCascIdxBranchName.c_str(), &mCascsIdxPtr); mTree->SetBranchAddress(mCascBranchName.c_str(), &mCascsPtr); mTree->SetBranchAddress(mPVertex2CascRefBranchName.c_str(), &mPV2CascRefPtr); + mTree->SetBranchAddress(m3BodyIdxBranchName.c_str(), &m3BodysIdxPtr); mTree->SetBranchAddress(m3BodyBranchName.c_str(), &m3BodysPtr); mTree->SetBranchAddress(mPVertex23BodyRefBranchName.c_str(), &mPV23BodyRefPtr); @@ -124,10 +143,13 @@ void SecondaryVertexReader::connectTree() DataProcessorSpec getSecondaryVertexReaderSpec() { std::vector outputs; + outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs + outputs.emplace_back("GLO", "CASCS_IDX", 0, Lifetime::Timeframe); // found Cascades indices outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs + outputs.emplace_back("GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe); // found 3 body vertices indices outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body Decays outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body Decays refs diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx index 23eff571105e6..8c7f87a720925 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/StrangenessTrackingReaderSpec.cxx @@ -76,14 +76,14 @@ void StrangenessTrackingReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); LOG(info) << "Pushing " << mStrangeTrack.size() << " strange tracks at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0, Lifetime::Timeframe}, mStrangeTrack); + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0}, mStrangeTrack); if (mUseMC) { LOG(info) << "Pushing " << mStrangeTrackMC.size() << " strange tracks MC labels at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe}, mStrangeTrackMC); + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS_MC", 0}, mStrangeTrackMC); } - // pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}, mPV2V0Ref); + // pc.outputs().snapshot(Output{"GLO", "PVTX_V0REFS", 0}, mPV2V0Ref); if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx index 90db5d08acc58..7e3cdffd84a6d 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackCosmicsReaderSpec.cxx @@ -41,9 +41,9 @@ void TrackCosmicsReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " Cosmic Tracks at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0, Lifetime::Timeframe}, mTracks); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0}, mTracks); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0, Lifetime::Timeframe}, mLabels); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0}, mLabels); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx index 8ae55e478603e..c7fd0d543ecf6 100644 --- a/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/readers/src/TrackTPCITSReaderSpec.cxx @@ -68,12 +68,12 @@ void TrackTPCITSReader::run(ProcessingContext& pc) mTree->GetEntry(ent); LOG(info) << "Pushing " << mTracks.size() << " TPC-ITS matches at entry " << ent; - pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mTracks); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}, mABTrkClusRefs); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}, mABTrkClIDs); + pc.outputs().snapshot(Output{"GLO", "TPCITS", 0}, mTracks); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0}, mABTrkClusRefs); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0}, mABTrkClIDs); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}, mLabels); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}, mLabelsAB); + pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0}, mLabels); + pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0}, mLabelsAB); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index fd66a3adfa3ac..34c41ec234dc5 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -62,7 +62,12 @@ namespace globaltracking class CosmicsMatchingSpec : public Task { public: - CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} + CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } ~CosmicsMatchingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -87,6 +92,7 @@ void CosmicsMatchingSpec::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); mMatching.setUseMC(mUseMC); + mTPCCorrMapsLoader.init(ic); // } @@ -98,9 +104,9 @@ void CosmicsMatchingSpec::run(ProcessingContext& pc) updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions mMatching.process(recoData); - pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0, Lifetime::Timeframe}, mMatching.getCosmicTracks()); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC", 0}, mMatching.getCosmicTracks()); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0, Lifetime::Timeframe}, mMatching.getCosmicTracksLbl()); + pc.outputs().snapshot(Output{"GLO", "COSMICTRC_MC", 0}, mMatching.getCosmicTracksLbl()); } mTimer.Stop(); } @@ -109,7 +115,7 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -128,10 +134,10 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) } bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { - mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } + mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -171,9 +177,13 @@ void CosmicsMatchingSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) +DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; + Options opts{ + {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, + {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; + auto dataRequest = std::make_shared(); dataRequest->requestTracks(src, useMC); @@ -193,16 +203,14 @@ DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); return DataProcessorSpec{ "cosmics-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, - Options{ - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, - {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx index 7ce95a1dfd3f8..e896c097cd03d 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingAssessmentSpec.cxx @@ -28,23 +28,27 @@ namespace o2 namespace globaltracking { +//_____________________________________________________________ +void GlobalFwdAssessmentSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + auto field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + const double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT + auto Bz = field->getBz(centerMFT); + mGloFwdAssessment->setBz(Bz); + return; + } + return; +} + //_____________________________________________________________ void GlobalFwdAssessmentSpec::init(InitContext& ic) { mGloFwdAssessment = std::make_unique(mUseMC); - + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); if (mMIDFilterDisabled) { mGloFwdAssessment->disableMIDFilter(); } - auto filename = o2::base::NameConf::getGRPFileName(); - const auto grp = o2::parameters::GRPObject::loadFrom(filename.c_str()); - if (grp) { - o2::base::Propagator::initFieldFromGRP(grp); - auto field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT - auto Bz = field->getBz(centerMFT); - mGloFwdAssessment->setBz(Bz); - } for (int sw = 0; sw < NStopWatches; sw++) { mTimer[sw].Stop(); @@ -58,6 +62,7 @@ void GlobalFwdAssessmentSpec::init(InitContext& ic) //_____________________________________________________________ void GlobalFwdAssessmentSpec::run(o2::framework::ProcessingContext& pc) { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTimer[SWQCAsync].Start(false); mGloFwdAssessment->runBasicQC(pc); @@ -104,7 +109,7 @@ void GlobalFwdAssessmentSpec::sendOutput(DataAllocator& output) TObjArray objar; mGloFwdAssessment->getHistos(objar); - output.snapshot(Output{"GLO", "FWDASSESSMENT", 0, Lifetime::Sporadic}, objar); + output.snapshot(Output{"GLO", "FWDASSESSMENT", 0}, objar); TFile* f = new TFile(Form("GlobalForwardAssessment.root"), "RECREATE"); objar.Write(); @@ -127,13 +132,22 @@ DataProcessorSpec getGlobaFwdAssessmentSpec(bool useMC, bool processGen, bool mi inputs.emplace_back("fwdtrklabels", "GLO", "GLFWD_MC", 0, Lifetime::Timeframe); } + auto ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); // query only once all objects except mag.field + outputs.emplace_back("GLO", "FWDASSESSMENT", 0, Lifetime::Sporadic); return DataProcessorSpec{ "glofwd-assessment", inputs, outputs, - AlgorithmSpec{adaptFromTask(useMC, processGen, midFilterDisabled, finalizeAnalysis)}, + AlgorithmSpec{adaptFromTask(useMC, processGen, ggRequest, midFilterDisabled, finalizeAnalysis)}, Options{{}}}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx index 683917d51b355..03dc823c62c42 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx @@ -35,6 +35,7 @@ #include "TGeoGlobalMagField.h" #include "Field/MagneticField.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "MCHTracking/TrackExtrap.h" using namespace o2::framework; using MCLabelsTr = gsl::span; @@ -91,18 +92,18 @@ void GlobalFwdMatchingDPL::run(ProcessingContext& pc) const auto& matchingParam = GlobalFwdMatchingParam::Instance(); if (matchingParam.saveMode == kSaveTrainingData) { - pc.outputs().snapshot(Output{"GLO", "GLFWDMFT", 0, Lifetime::Timeframe}, mMatching.getMFTMatchingPlaneParams()); - pc.outputs().snapshot(Output{"GLO", "GLFWDMCH", 0, Lifetime::Timeframe}, mMatching.getMCHMatchingPlaneParams()); - pc.outputs().snapshot(Output{"GLO", "GLFWDINF", 0, Lifetime::Timeframe}, mMatching.getMFTMCHMatchInfo()); + pc.outputs().snapshot(Output{"GLO", "GLFWDMFT", 0}, mMatching.getMFTMatchingPlaneParams()); + pc.outputs().snapshot(Output{"GLO", "GLFWDMCH", 0}, mMatching.getMCHMatchingPlaneParams()); + pc.outputs().snapshot(Output{"GLO", "GLFWDINF", 0}, mMatching.getMFTMCHMatchInfo()); } else { - pc.outputs().snapshot(Output{"GLO", "GLFWD", 0, Lifetime::Timeframe}, mMatching.getMatchedFwdTracks()); + pc.outputs().snapshot(Output{"GLO", "GLFWD", 0}, mMatching.getMatchedFwdTracks()); } if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0, Lifetime::Timeframe}, mMatching.getMatchLabels()); + pc.outputs().snapshot(Output{"GLO", "GLFWD_MC", 0}, mMatching.getMatchLabels()); } if (mMatchRootOutput) { - pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0, Lifetime::Timeframe}, mMatching.getMFTMCHMatchInfo()); + pc.outputs().snapshot(Output{"GLO", "MTC_MFTMCH", 0}, mMatching.getMFTMCHMatchInfo()); } mTimer.Stop(); } @@ -116,6 +117,9 @@ void GlobalFwdMatchingDPL::endOfStream(EndOfStreamContext& ec) void GlobalFwdMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher("GLO", "GRPMAGFIELD", 0)) { + o2::mch::TrackExtrap::setField(); + } return; } if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { @@ -153,6 +157,10 @@ void GlobalFwdMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } else { mMatching.setMFTROFrameLengthInBC(alpParams.roFrameLengthInBC); // MFT ROFrame duration in \mus } + if (alpParams.roFrameBiasInBC != 0) { + mMatching.setMFTROFrameBiasInBC(alpParams.roFrameBiasInBC); // MFT ROFrame bias in BCs wrt orbit start + LOG(info) << "Setting MFT ROF bias to " << alpParams.roFrameBiasInBC << " BCs"; + } mMatching.init(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx new file mode 100644 index 0000000000000..b0562fd4f3a1e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx @@ -0,0 +1,181 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatcherSpec.cxx + +#include +#include +#include "TStopwatch.h" +#include "Framework/ConfigParamRegistry.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsBase/Propagator.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsParameters/GRPObject.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "DetectorsBase/GRPGeomHelper.h" + +// from Tracks +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/GlobalTrackAccessor.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" + +// from HMPID +#include "DataFormatsHMP/Cluster.h" +#include "GlobalTracking/MatchHMP.h" +#include "GlobalTrackingWorkflow/HMPMatcherSpec.h" + +using namespace o2::framework; +// using MCLabelsTr = gsl::span; +// using GID = o2::dataformats::GlobalTrackID; +// using DetID = o2::detectors::DetID; + +using evIdx = o2::dataformats::EvIndex; +using MatchOutputType = std::vector; +using GID = o2::dataformats::GlobalTrackID; + +namespace o2 +{ +namespace globaltracking +{ + +class HMPMatcherSpec : public Task +{ + public: + HMPMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} + ~HMPMatcherSpec() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + + private: + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC = false; // true + bool mUseFIT = false; + bool mDoTPCRefit = false; + bool mStrict = false; + MatchHMP mMatcher; ///< Cluster finder + TStopwatch mTimer; +}; + +void HMPMatcherSpec::init(InitContext& ic) +{ + mTimer.Stop(); + mTimer.Reset(); + //-------- init geometry and field --------// + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); +} + +void HMPMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +void HMPMatcherSpec::run(ProcessingContext& pc) +{ + mTimer.Start(false); + + RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + + auto creationTime = DataRefUtils::getHeader(pc.inputs().getFirstValid(true))->creation; + + // LOG(debug) << "isTrackSourceLoaded: TPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); LOG(debug) << "isTrackSourceLoaded: ITSTPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + + LOG(debug) << "isTrackSourceLoaded: TPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); + + //: + LOG(debug) << "isTrackSourceLoaded: TPCTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTOF); + + LOG(debug) << "isTrackSourceLoaded: ITSTPC -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + LOG(debug) << "isTrackSourceLoaded: TPCTRD -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRD); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTRD -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRD); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTOF); + LOG(debug) << "isTrackSourceLoaded: TPCTRDTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRDTOF); + LOG(debug) << "isTrackSourceLoaded: ITSTPCTRDTOF -> " << recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRDTOF); + + bool isTPCused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPC); + bool isITSTPCused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPC); + bool isTPCTRDused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRD); + + bool isTPCTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTOF); + + bool isITSTPCTRDused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRD); + bool isITSTPCTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTOF); + bool isTPCTRDTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::TPCTRDTOF); + bool isITSTPCTRDTOFused = recoData.isTrackSourceLoaded(o2::dataformats::GlobalTrackID::Source::ITSTPCTRDTOF); + // uint32_t ss = o2::globaltracking::getSubSpec(mStrict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + + mMatcher.run(recoData); + + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MATCHES", 0}, mMatcher.getMatchedTrackVector(o2::globaltracking::MatchHMP::trackType::CONSTR)); + if (mUseMC) { + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MCLABELS", 0}, mMatcher.getMatchedHMPLabelsVector(o2::globaltracking::MatchHMP::trackType::CONSTR)); + } + + mTimer.Stop(); +} + +void HMPMatcherSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(debug, "HMP matching total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getHMPMatcherSpec(GID::mask_t src, bool useMC, float extratolerancetrd, float extratolerancetof) +{ + // uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + auto dataRequest = std::make_shared(); + /* if (strict) { + dataRequest->setMatchingInputStrict(); + }*/ + + dataRequest->requestTracks(src, useMC); + dataRequest->requestClusters(GID::getSourceMask(GID::HMP), useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + std::vector outputs; + + outputs.emplace_back(o2::header::gDataOriginHMP, "MATCHES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginHMP, "MCLABELS", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "hmp-matcher", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} + +} // namespace globaltracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index 36e3266c59514..d71a4fad7ab78 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -15,6 +15,7 @@ #include #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsCalibration/MeanVertexObject.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -81,6 +82,7 @@ void PrimaryVertexingSpec::init(InitContext& ic) throw std::runtime_error(fmt::format("directory {} for raw data dumps does not exist", dumpDir)); } mVertexer.setPoolDumpDirectory(dumpDir); + mVertexer.setTrackSources(mTrackSrc); } void PrimaryVertexingSpec::run(ProcessingContext& pc) @@ -102,14 +104,20 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) std::vector gids; auto maxTrackTimeError = PVertexerParams::Instance().maxTimeErrorMUS; auto trackMaxX = PVertexerParams::Instance().trackMaxX; + auto minIBHits = PVertexerParams::Instance().minIBHits; auto halfROFITS = 0.5 * mITSROFrameLengthMUS + mITSROFBiasMUS; auto hw2ErrITS = 2.f / std::sqrt(12.f) * mITSROFrameLengthMUS; // conversion from half-width to error for ITS - auto creator = [maxTrackTimeError, hw2ErrITS, halfROFITS, trackMaxX, &tracks, &gids](auto& _tr, GTrackID _origID, float t0, float terr) { + auto creator = [maxTrackTimeError, hw2ErrITS, halfROFITS, trackMaxX, minIBHits, &tracks, &gids, &recoData](auto& _tr, GTrackID _origID, float t0, float terr) { if constexpr (isBarrelTrack()) { if (!_origID.includesDet(DetID::ITS) || _tr.getX() > trackMaxX) { return true; // just in case this selection was not done on RecoContainer filling level } + auto itsID = recoData.getITSContributorGID(_origID); + if ((itsID.getSource() == GTrackID::ITS && o2::math_utils::numberOfBitsSet(recoData.getITSTrack(itsID).getPattern() & 7) < minIBHits) || + (itsID.getSource() == GTrackID::ITSAB && o2::math_utils::numberOfBitsSet(recoData.getITSABRef(itsID).pattern & 7) < minIBHits)) { // do not accept ITSAB tracklets + return true; + } if constexpr (isITSTrack()) { t0 += halfROFITS; // ITS time is supplied in \mus as beginning of ROF terr *= hw2ErrITS; // error is supplied as a half-ROF duration, convert to \mus @@ -129,33 +137,66 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) recoData.fillTrackMCLabels(gids, tracksMCInfo); } mVertexer.setStartIR(recoData.startIR); - std::vector ft0Data; + static std::vector ft0Data; if (mValidateWithIR) { // select BCs for validation + ft0Data.clear(); const o2::ft0::InteractionTag& ft0Params = o2::ft0::InteractionTag::Instance(); auto ft0all = recoData.getFT0RecPoints(); for (const auto& ftRP : ft0all) { if (ft0Params.isSelected(ftRP)) { - ft0Data.push_back(ftRP.getInteractionRecord()); + ft0Data.emplace_back(InteractionCandidate{ftRP.getInteractionRecord(), + float(ftRP.getInteractionRecord().differenceInBC(recoData.startIR) * o2::constants::lhc::LHCBunchSpacingMUS), + float(ftRP.getTrigger().getAmplA() + ftRP.getTrigger().getAmplC()), + GTrackID::FT0}); } } } mVertexer.process(tracks, gids, ft0Data, vertices, vertexTrackIDs, v2tRefs, tracksMCInfo, lblVtx); + + // flag vertices using UPC ITS mode + auto itsrofs = recoData.getITSTracksROFRecords(); + std::vector itsTrUPC(recoData.getITSTracks().size()); + for (auto& rof : itsrofs) { + if (rof.getFlag(o2::itsmft::ROFRecord::VtxUPCMode)) { + for (int i = rof.getFirstEntry(); i < rof.getFirstEntry() + rof.getNEntries(); i++) { + itsTrUPC[i] = true; + } + } + } + int nv = vertices.size(); + for (int iv = 0; iv < nv; iv++) { + int idMin = v2tRefs[iv].getFirstEntry(), idMax = idMin + v2tRefs[iv].getEntries(); + int nits = 0, nitsUPC = 0; + for (int id = idMin; id < idMax; id++) { + auto gid = recoData.getITSContributorGID(vertexTrackIDs[id]); + if (gid.getSource() == GIndex::ITS) { + nits++; + if (itsTrUPC[gid.getIndex()]) { + nitsUPC++; + } + } + } + if (nitsUPC > nits / 2) { + vertices[iv].setFlags(PVertex::UPCMode); + } + } } - pc.outputs().snapshot(Output{"GLO", "PVTX", 0, Lifetime::Timeframe}, vertices); - pc.outputs().snapshot(Output{"GLO", "PVTX_CONTIDREFS", 0, Lifetime::Timeframe}, v2tRefs); - pc.outputs().snapshot(Output{"GLO", "PVTX_CONTID", 0, Lifetime::Timeframe}, vertexTrackIDs); + pc.outputs().snapshot(Output{"GLO", "PVTX", 0}, vertices); + pc.outputs().snapshot(Output{"GLO", "PVTX_CONTIDREFS"}, v2tRefs); + pc.outputs().snapshot(Output{"GLO", "PVTX_CONTID", 0}, vertexTrackIDs); if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0, Lifetime::Timeframe}, lblVtx); + pc.outputs().snapshot(Output{"GLO", "PVTX_MCTR", 0}, lblVtx); } mTimer.Stop(); - LOGP(info, "Found {} PVs, Time CPU/Real:{:.3f}/{:.3f} (DBScan: {:.4f}, Finder:{:.4f}, Rej.Debris:{:.4f}, Reattach:{:.4f}) | {} trials for {} TZ-clusters, max.trials: {}, Slowest TZ-cluster: {} ms of mult {}", + LOGP(info, "Found {} PVs, Time CPU/Real:{:.3f}/{:.3f} (DBScan: {:.4f}, Finder:{:.4f}, MADSel:{:.4f}, Rej.Debris:{:.4f}, Reattach:{:.4f}) | {} trials for {} TZ-clusters, max.trials: {}, Slowest TZ-cluster: {} ms of mult {} | NInitial:{}, Rejections: NoFilledBC:{}, NoIntCand:{}, Debris:{}, Quality:{}, ITSOnly:{}", vertices.size(), mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0, - mVertexer.getTimeDBScan().CpuTime(), mVertexer.getTimeVertexing().CpuTime(), mVertexer.getTimeDebris().CpuTime(), mVertexer.getTimeReAttach().CpuTime(), - mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), - mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult()); + mVertexer.getTimeDBScan().CpuTime(), mVertexer.getTimeVertexing().CpuTime(), mVertexer.getTimeMADSel().CpuTime(), mVertexer.getTimeDebris().CpuTime(), + mVertexer.getTimeReAttach().CpuTime(), mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), + mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult(), mVertexer.getNIniFound(), + mVertexer.getNKilledBCValid(), mVertexer.getNKilledIntCand(), mVertexer.getNKilledDebris(), mVertexer.getNKilledQuality(), mVertexer.getNKilledITSOnly()); } void PrimaryVertexingSpec::endOfStream(EndOfStreamContext& ec) @@ -212,7 +253,7 @@ void PrimaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) pc.inputs().get("meanvtx"); } -DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC) +DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool validateWithFT0, bool useMC, bool useGeom) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -230,12 +271,12 @@ DataProcessorSpec getPrimaryVertexingSpec(GTrackID::mask_t src, bool skip, bool outputs.emplace_back("GLO", "PVTX_MCTR", 0, Lifetime::Timeframe); } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - true, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); diff --git a/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx new file mode 100644 index 0000000000000..d53bbf9a30690 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx @@ -0,0 +1,101 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ReaderDriverSpec.cxx + +#include +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "Framework/RateLimiter.h" +#include "Framework/RawDeviceService.h" +#include "GlobalTrackingWorkflow/ReaderDriverSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "CommonUtils/StringUtils.h" +#include "CommonDataFormat/IRFrame.h" +#include "TFile.h" +#include "TTree.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace globaltracking +{ +using HBFINI = o2::raw::HBFUtilsInitializer; + +class ReadeDriverSpec : public o2::framework::Task +{ + public: + ReadeDriverSpec(size_t minSHM) : mMinSHM(minSHM) {} + ~ReadeDriverSpec() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + int mTFRateLimit = -999; + int mNTF = 0; + size_t mMinSHM = 0; +}; + +void ReadeDriverSpec::init(InitContext& ic) +{ + mNTF = ic.options().get("max-tf"); +} + +void ReadeDriverSpec::run(ProcessingContext& pc) +{ + if (mTFRateLimit == -999) { + mTFRateLimit = std::stoi(pc.services().get().device()->fConfig->GetValue("timeframes-rate-limit")); + } + static RateLimiter limiter; + static int count = 0; + if (!count) { + if (HBFINI::NTFs < 0) { + LOGP(fatal, "Number of TFs to process was not initizalized in the HBFUtilsInitializer"); + } + mNTF = (mNTF > 0 && mNTF < HBFINI::NTFs) ? mNTF : HBFINI::NTFs; + } else { // check only for count > 0 + limiter.check(pc, mTFRateLimit, mMinSHM); + } + auto& v = pc.outputs().make>(Output{"GLO", "READER_DRIVER", 0}); + if (!HBFINI::IRFrameSel.getMin().isDummy() && !HBFINI::IRFrameSel.getMax().isDummy()) { + v.push_back(HBFINI::IRFrameSel); + LOGP(debug, "OUTVEC {}/{}", v.back().getMin().asString(), v.back().getMax().asString()); + } + count++; + if ((HBFINI::LastIRFrameIndex == -1 && count >= mNTF) || (v.size() && v.back().isLast())) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +DataProcessorSpec getReaderDriverSpec(const std::string& metricChannel, size_t minSHM) +{ + std::vector outputSpec; + std::vector options; + options.emplace_back(ConfigParamSpec{"max-tf", o2::framework::VariantType::Int, -1, {"max TFs to process (<1 : no limits)"}}); + if (!metricChannel.empty()) { + options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, metricChannel, {"Out-of-band channel config for TF throttling"}}); + } + return DataProcessorSpec{ + HBFINI::ReaderDriverDevice, + Inputs{}, + Outputs{{"GLO", "READER_DRIVER", 0, Lifetime::Timeframe}}, + AlgorithmSpec{adaptFromTask(minSHM)}, + options}; +} + +} // namespace globaltracking +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx index 5a5821ffd4a9c..7f961536cf1f2 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexWriterSpec.cxx @@ -19,7 +19,7 @@ #include "CommonDataFormat/RangeReference.h" #include "ReconstructionDataFormats/V0.h" #include "ReconstructionDataFormats/Cascade.h" -#include "ReconstructionDataFormats/DecayNbody.h" +#include "ReconstructionDataFormats/Decay3Body.h" using namespace o2::framework; @@ -29,8 +29,11 @@ namespace vertexing { using RRef = o2::dataformats::RangeReference; using V0 = o2::dataformats::V0; +using V0Index = o2::dataformats::V0Index; using Cascade = o2::dataformats::Cascade; -using DecayNbody = o2::dataformats::DecayNbody; +using CascadeIndex = o2::dataformats::CascadeIndex; +using Decay3Body = o2::dataformats::Decay3Body; +using Decay3BodyIndex = o2::dataformats::Decay3BodyIndex; template using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; @@ -39,34 +42,42 @@ using namespace o2::header; DataProcessorSpec getSecondaryVertexWriterSpec() { - auto loggerV = [](std::vector const& v) { + auto loggerV = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " v0s"; }; - auto loggerC = [](std::vector const& v) { + auto loggerC = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " cascades"; }; - auto loggerD = [](std::vector const& v) { + auto loggerD = [](std::vector const& v) { LOG(info) << "SecondaryVertexWriter pulled " << v.size() << " decays3body"; }; - auto inpV0ID = InputSpec{"v0s", "GLO", "V0S", 0}; - auto inpV0RefID = InputSpec{"pv2v0ref", "GLO", "PVTX_V0REFS", 0}; - auto inpCascID = InputSpec{"cascs", "GLO", "CASCS", 0}; - auto inpCascRefID = InputSpec{"pv2cascref", "GLO", "PVTX_CASCREFS", 0}; - auto inp3BodyID = InputSpec{"decays3body", "GLO", "DECAYS3BODY", 0}; - auto inp3BodyRefID = InputSpec{"pv23bodyref", "GLO", "PVTX_3BODYREFS", 0}; + auto inpV0 = InputSpec{"v0s", "GLO", "V0S", 0}; + auto inpV0ID = InputSpec{"v0sIdx", "GLO", "V0S_IDX", 0}; + auto inpV0Ref = InputSpec{"pv2v0ref", "GLO", "PVTX_V0REFS", 0}; + auto inpCasc = InputSpec{"cascs", "GLO", "CASCS", 0}; + auto inpCascID = InputSpec{"cascsIdx", "GLO", "CASCS_IDX", 0}; + auto inpCascRef = InputSpec{"pv2cascref", "GLO", "PVTX_CASCREFS", 0}; + auto inp3Body = InputSpec{"decays3body", "GLO", "DECAYS3BODY", 0}; + auto inp3BodyID = InputSpec{"decays3bodyIdx", "GLO", "DECAYS3BODY_IDX", 0}; + auto inp3BodyRef = InputSpec{"pv23bodyref", "GLO", "PVTX_3BODYREFS", 0}; return MakeRootTreeWriterSpec("secondary-vertex-writer", "o2_secondary_vertex.root", MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with Secondary Vertices"}, - BranchDefinition>{inpV0ID, "V0s", loggerV}, - BranchDefinition>{inpV0RefID, "PV2V0Refs"}, - BranchDefinition>{inpCascID, "Cascades", loggerC}, - BranchDefinition>{inpCascRefID, "PV2CascRefs"}, - BranchDefinition>{inp3BodyID, "Decays3Body", loggerD}, - BranchDefinition>{inp3BodyRefID, "PV23BodyRefs"})(); + BranchDefinition>{inpV0ID, "V0sID", loggerV}, + BranchDefinition>{inpV0, "V0s"}, + BranchDefinition>{inpV0Ref, "PV2V0Refs"}, + + BranchDefinition>{inpCascID, "CascadesID", loggerC}, + BranchDefinition>{inpCasc, "Cascades"}, + BranchDefinition>{inpCascRef, "PV2CascRefs"}, + + BranchDefinition>{inp3BodyID, "Decays3BodyID", loggerD}, + BranchDefinition>{inp3Body, "Decays3Body"}, + BranchDefinition>{inp3BodyRef, "PV23BodyRefs"})(); } } // namespace vertexing diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index d82bc5c320577..ea566f15a0b59 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -12,7 +12,9 @@ /// @file SecondaryVertexingSpec.cxx #include -#include "ReconstructionDataFormats/DecayNbody.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "Framework/CCDBParamSpec.h" +#include "ReconstructionDataFormats/Decay3Body.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -24,12 +26,15 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "CommonUtils/NameConf.h" #include "DetectorsVertexing/SVertexer.h" +#include "StrangenessTracking/StrangenessTracker.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/GlobalParams.h" #include "TStopwatch.h" #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DeviceSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; @@ -39,7 +44,7 @@ using VRef = o2::dataformats::VtxTrackRef; using PVertex = const o2::dataformats::PrimaryVertex; using V0 = o2::dataformats::V0; using Cascade = o2::dataformats::Cascade; -using DecayNbody = o2::dataformats::DecayNbody; +using Decay3Body = o2::dataformats::Decay3Body; using RRef = o2::dataformats::RangeReference; using DataRequest = o2::globaltracking::DataRequest; @@ -53,7 +58,12 @@ namespace o2d = o2::dataformats; class SecondaryVertexingSpec : public Task { public: - SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, bool enabCasc, bool enable3body) : mDataRequest(dr), mGGCCDBRequest(gr), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body) {} + SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -66,9 +76,14 @@ class SecondaryVertexingSpec : public Task std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + GTrackID::mask_t mSrc{}; + bool mEnableCCDBParams = false; bool mEnableCascades = false; bool mEnable3BodyVertices = false; + bool mEnableStrangenessTracking = false; + bool mUseMC = false; o2::vertexing::SVertexer mVertexer; + o2::strangeness_tracking::StrangenessTracker mStrTracker; TStopwatch mTimer; }; @@ -81,30 +96,38 @@ void SecondaryVertexingSpec::init(InitContext& ic) mVertexer.setEnableCascades(mEnableCascades); mVertexer.setEnable3BodyDecays(mEnable3BodyVertices); mVertexer.setNThreads(ic.options().get("threads")); + mVertexer.setUseMC(mUseMC); + if (mEnableStrangenessTracking) { + mStrTracker.setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); + mStrTracker.setConfigParams(&o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance()); + mStrTracker.setupThreads(ic.options().get("threads")); + mStrTracker.setupFitters(); + mStrTracker.setMCTruthOn(mUseMC); + mVertexer.setStrangenessTracker(&mStrTracker); + } + if (mSrc[GTrackID::TPC]) { + mTPCCorrMapsLoader.init(ic); + } } void SecondaryVertexingSpec::run(ProcessingContext& pc) { double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); mTimer.Start(false); + static std::array fitCalls{}; o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); - auto& v0s = pc.outputs().make>(Output{"GLO", "V0S", 0, Lifetime::Timeframe}); - auto& v0Refs = pc.outputs().make>(Output{"GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe}); - auto& cascs = pc.outputs().make>(Output{"GLO", "CASCS", 0, Lifetime::Timeframe}); - auto& cascRefs = pc.outputs().make>(Output{"GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe}); - auto& vtx3body = pc.outputs().make>(Output{"GLO", "DECAYS3BODY", 0, Lifetime::Timeframe}); - auto& vtx3bodyRefs = pc.outputs().make>(Output{"GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe}); - - mVertexer.process(recoData); - mVertexer.extractSecondaryVertices(v0s, v0Refs, cascs, cascRefs, vtx3body, vtx3bodyRefs); + mVertexer.process(recoData, pc); mTimer.Stop(); - LOG(info) << "Found " << v0s.size() << " V0s, " << cascs.size() << " cascades, and " << vtx3body.size() << " 3-body decays; timing: CPU: " - << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0 << " s"; + auto calls = mVertexer.getNFitterCalls(); + LOGP(info, "Found {} V0s ({} fits), {} cascades ({} fits), {} 3-body decays ({} fits), {} strange tracks. Timing: CPU: {:.2f} Real: {:.2f} s", + mVertexer.getNV0s(), calls[0] - fitCalls[0], mVertexer.getNCascades(), calls[1] - fitCalls[1], mVertexer.getN3Bodies(), calls[2] - fitCalls[2], mVertexer.getNStrangeTracks(), + mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0); + fitCalls = calls; } void SecondaryVertexingSpec::endOfStream(EndOfStreamContext& ec) @@ -124,75 +147,172 @@ void SecondaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mStrTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVertexer.setMeanVertex((const o2::dataformats::MeanVertexObject*)obj); + return; + } + if (matcher == ConcreteDataMatcher("GLO", "SVPARAM", 0)) { + LOG(info) << "SVertexer Params updated from ccdb"; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeomtetryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mStrTracker.setClusterDictionaryIT3((const o2::its3::TopologyDictionary*)obj); + return; + } +#endif } void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); - mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + if (mSrc[GTrackID::TPC]) { + mTPCVDriftHelper.extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); + } static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; mVertexer.init(); + if (mEnableCCDBParams) { + // for reading the calib objects from the CCDB + pc.inputs().get("SVParam"); + } if (pc.services().get().inputTimesliceId == 0) { - SVertexerParams::Instance().printKeyValues(); + // setting and or overwriting the configurable params + SVertexerParams::Instance().printKeyValues(true, true); + } + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } + if (mEnableStrangenessTracking) { + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } + +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { // hack to trigger loading dictionary + pc.inputs().get("cldict"); } +#endif } // we may have other params which need to be queried regularly - bool updateMaps = false; - if (mTPCCorrMapsLoader.isUpdated()) { + if (mSrc[GTrackID::TPC]) { + bool updateMaps = false; + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } mVertexer.setTPCCorrMaps(&mTPCCorrMapsLoader); - mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; - } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + } } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + if (mEnableStrangenessTracking) { + if (o2::base::Propagator::Instance()->getNominalBz() != mStrTracker.getBz()) { + mStrTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mStrTracker.setupFitters(); + } } + + pc.inputs().get("meanvtx"); } -DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body) +DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, + bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; + Options opts{ + {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, + {"threads", VariantType::Int, 1, {"Number of threads"}}}; auto dataRequest = std::make_shared(); - - bool useMC = false; + if (enableCCDBParams) { + dataRequest->inputs.emplace_back("SVParam", "GLO", "SVPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/SVertexerParam")); + } + GTrackID::mask_t srcClus{}; + if (enableStrangenesTracking) { + src |= (srcClus = GTrackID::getSourceMask(GTrackID::ITS)); + } + if (GTrackID::includesDet(o2::detectors::DetID::TPC, src) && !src[GTrackID::TPC]) { + LOGP(warn, "Tracks involving TPC were requested w/o requesting TPC-only tracks, simplified selection will be applied"); + } + if (src[GTrackID::TPC]) { + srcClus |= GTrackID::getSourceMask(GTrackID::TPC); + } + if (srcClus.any()) { + dataRequest->requestClusters(srcClus, useMC); + } +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { // hack to trigger loading dictionary + dataRequest->inputs.emplace_back("cldict", "IT3", "CLUSDICT", Lifetime::Condition, ccdbParamSpec("IT3/Calib/ClusterDictionary")); + } +#endif dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry + dataRequest->requestPrimaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom || enableStrangenesTracking ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); - o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + if (!useGeom && enableStrangenesTracking) { + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } + if (src[GTrackID::TPC]) { + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + } + outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices + outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s + outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs + + outputs.emplace_back("GLO", "CASCS_IDX", 0, Lifetime::Timeframe); // found Cascades indices + outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades + outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs + + outputs.emplace_back("GLO", "DECAYS3BODY_IDX", 0, Lifetime::Timeframe); // found 3 body vertices indices + outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body vertices + outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body vertices refs - outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s - outputs.emplace_back("GLO", "PVTX_V0REFS", 0, Lifetime::Timeframe); // prim.vertex -> V0s refs - outputs.emplace_back("GLO", "CASCS", 0, Lifetime::Timeframe); // found Cascades - outputs.emplace_back("GLO", "PVTX_CASCREFS", 0, Lifetime::Timeframe); // prim.vertex -> Cascades refs - outputs.emplace_back("GLO", "DECAYS3BODY", 0, Lifetime::Timeframe); // found 3 body vertices - outputs.emplace_back("GLO", "PVTX_3BODYREFS", 0, Lifetime::Timeframe); // prim.vertex -> 3 body vertices refs + if (enableStrangenesTracking) { + outputs.emplace_back("GLO", "STRANGETRACKS", 0, Lifetime::Timeframe); // found strange track + outputs.emplace_back("GLO", "CLUSUPDATES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back("GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe); + LOG(info) << "Strangeness tracker will use MC"; + } + } return DataProcessorSpec{ "secondary-vertexing", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, enableCasc, enable3body)}, - Options{{"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}, - {"threads", VariantType::Int, 1, {"Number of threads"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, + opts}; } } // namespace vertexing diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx index ed23c20ec850c..e313940b0a91e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx @@ -17,10 +17,8 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "StrangenessTracking/StrangenessTrackingConfigParam.h" #include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsParameters/GRPObject.h" @@ -56,6 +54,7 @@ void StrangenessTrackerSpec::init(framework::InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mTracker.setCorrType(o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); mTracker.setConfigParams(&StrangenessTrackingParamConfig::Instance()); + mTracker.setupThreads(1); mTracker.setupFitters(); LOG(info) << "Initialized strangeness tracker..."; @@ -71,15 +70,14 @@ void StrangenessTrackerSpec::run(framework::ProcessingContext& pc) updateTimeDependentParams(pc); auto geom = o2::its::GeometryTGeo::Instance(); - mTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); mTracker.loadData(recoData); mTracker.prepareITStracks(); mTracker.process(); - pc.outputs().snapshot(Output{"STK", "STRTRACKS", 0, Lifetime::Timeframe}, mTracker.getStrangeTrackVec()); - pc.outputs().snapshot(Output{"STK", "CLUSUPDATES", 0, Lifetime::Timeframe}, mTracker.getClusAttachments()); + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS", 0}, mTracker.getStrangeTrackVec()); + pc.outputs().snapshot(Output{"GLO", "CLUSUPDATES", 0}, mTracker.getClusAttachments()); if (mUseMC) { - pc.outputs().snapshot(Output{"STK", "STRK_MC", 0, Lifetime::Timeframe}, mTracker.getStrangeTrackLabels()); + pc.outputs().snapshot(Output{"GLO", "STRANGETRACKS_MC", 0}, mTracker.getStrangeTrackLabels()); } mTimer.Stop(); @@ -93,9 +91,16 @@ void StrangenessTrackerSpec::updateTimeDependentParams(ProcessingContext& pc) if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; // pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); } + if (o2::base::Propagator::Instance()->getNominalBz() != mTracker.getBz()) { + mTracker.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mTracker.setupFitters(); + } mTracker.setMCTruthOn(mUseMC); } @@ -107,9 +112,21 @@ void StrangenessTrackerSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; - mTracker.setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + mTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); + return; + } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeomtetryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mTracker.setClusterDictionaryIT3((const o2::its3::TopologyDictionary*)obj); return; } +#endif } void StrangenessTrackerSpec::endOfStream(framework::EndOfStreamContext& ec) @@ -118,30 +135,31 @@ void StrangenessTrackerSpec::endOfStream(framework::EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC) +DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useGeom) { - // ITS auto dataRequest = std::make_shared(); dataRequest->requestITSClusters(useMC); dataRequest->requestTracks(src, useMC); - dataRequest->requestPrimaryVertertices(useMC); - dataRequest->requestSecondaryVertertices(useMC); - - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); - + if (!useGeom) { + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, framework::ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } std::vector outputs; - outputs.emplace_back("STK", "STRTRACKS", 0, Lifetime::Timeframe); - outputs.emplace_back("STK", "CLUSUPDATES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "STRANGETRACKS", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "CLUSUPDATES", 0, Lifetime::Timeframe); if (useMC) { - outputs.emplace_back("STK", "STRK_MC", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "STRANGETRACKS_MC", 0, Lifetime::Timeframe); LOG(info) << "Strangeness tracker will use MC"; } @@ -154,4 +172,4 @@ DataProcessorSpec getStrangenessTrackerSpec(o2::dataformats::GlobalTrackID::mask } } // namespace strangeness_tracking -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx index 124cbfa2befcb..613df6a803eee 100644 --- a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingWriterSpec.cxx @@ -41,9 +41,9 @@ DataProcessorSpec getStrangenessTrackingWriterSpec(bool useMC) LOG(info) << "StrangenessTracker writer pulled " << v.size() << " strange tracks"; }; - auto inpStTrkID = InputSpec{"strangetracks", "STK", "STRTRACKS", 0}; - auto inpClusAtt = InputSpec{"clusupdates", "STK", "CLUSUPDATES", 0}; - auto inpMCLab = InputSpec{"stkmclabels", "STK", "STRK_MC", 0}; + auto inpStTrkID = InputSpec{"strangetracks", "GLO", "STRANGETRACKS", 0}; + auto inpClusAtt = InputSpec{"clusupdates", "GLO", "CLUSUPDATES", 0}; + auto inpMCLab = InputSpec{"stkmclabels", "GLO", "STRANGETRACKS_MC", 0}; return MakeRootTreeWriterSpec("strangenesstracking-writer", "o2_strange_tracks.root", diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx index eb212f2507786..3d8b36043ddeb 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFEventTimeChecker.cxx @@ -266,10 +266,10 @@ void TOFEventTimeChecker::processEvent(std::vector& tracks) float betaexpPr = mL / mExpPr * cinv; //Mass - float mass = mP / beta * TMath::Sqrt(TMath::Abs(1 - beta * beta)); - float massexpPi = mP / betaexpPi * TMath::Sqrt(TMath::Abs(1 - betaexpPi * betaexpPi)); - float massexpKa = mP / betaexpKa * TMath::Sqrt(TMath::Abs(1 - betaexpKa * betaexpKa)); - float massexpPr = mP / betaexpPr * TMath::Sqrt(TMath::Abs(1 - betaexpPr * betaexpPr)); + float mass = mP / beta * TMath::Sqrt(std::abs(1 - beta * beta)); + float massexpPi = mP / betaexpPi * TMath::Sqrt(std::abs(1 - betaexpPi * betaexpPi)); + float massexpKa = mP / betaexpKa * TMath::Sqrt(std::abs(1 - betaexpKa * betaexpKa)); + float massexpPr = mP / betaexpPr * TMath::Sqrt(std::abs(1 - betaexpPr * betaexpPr)); if (massexpPi < 0.13) { // remove wrong track lengths continue; diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 1ef21e4e575c0..3f6e79e433635 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -58,7 +58,12 @@ namespace globaltracking class TOFMatcherSpec : public Task { public: - TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable) {} + TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } ~TOFMatcherSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -77,6 +82,7 @@ class TOFMatcherSpec : public Task bool mStrict = false; bool mPushMatchable = false; float mExtraTolTRD = 0.; + int mNlanes = 1; MatchTOF mMatcher; ///< Cluster finder TStopwatch mTimer; }; @@ -89,16 +95,17 @@ void TOFMatcherSpec::init(InitContext& ic) if (mStrict) { mMatcher.setHighPurity(); } + mTPCCorrMapsLoader.init(ic); mMatcher.storeMatchable(mPushMatchable); - mMatcher.setExtraTimeToleranceTRD(mExtraTolTRD); + mMatcher.setNlanes(mNlanes); } void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once const auto bcs = o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getBunchFilling().getFilledBCs(); @@ -111,10 +118,10 @@ void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) // we may have other params which need to be queried regularly bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { - mMatcher.setTPCCorrMaps(&mTPCCorrMapsLoader); mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } + mMatcher.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -165,67 +172,61 @@ void TOFMatcherSpec::run(ProcessingContext& pc) mMatcher.setTS(creationTime); - mMatcher.run(recoData); + mMatcher.run(recoData, pc.services().get().firstTForbit); + static pmr::vector dummyMCLab; if (isTPCused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC)); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPC", ss, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC)); - } - + auto& mtcInfo = pc.outputs().make>(Output{o2::header::gDataOriginTOF, "MTC_TPC", ss}); + auto& mclabels = mUseMC ? pc.outputs().make>(Output{o2::header::gDataOriginTOF, "MCMTC_TPC", ss}) : dummyMCLab; + auto& tracksTPCTOF = pc.outputs().make>(OutputRef{"tpctofTracks", ss}); auto nmatch = mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC).size(); - if (mDoTPCRefit) { - LOG(debug) << "Refitting " << nmatch << " matched TPC tracks with TOF time info"; - } else { - LOG(debug) << "Shifting Z for " << nmatch << " matched TPC tracks according to TOF time info"; - } - auto& tracksTPCTOF = pc.outputs().make>(OutputRef{"tpctofTracks", ss}, nmatch); - mMatcher.makeConstrainedTPCTracks(tracksTPCTOF); + LOG(debug) << (mDoTPCRefit ? "Refitting " : "Shifting Z for ") << nmatch << " matched TPC tracks with TOF time info"; + mMatcher.makeConstrainedTPCTracks(mtcInfo, mclabels, tracksTPCTOF); } if (isITSTPCused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPC", 0}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPC)); } } if (isTPCTRDused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPCTRD", ss, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPCTRD", ss}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPCTRD", ss, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_TPCTRD", ss}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPCTRD)); } } if (isITSTPCTRDused) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPCTRD", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPCTRD", 0}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPCTRD", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMTC_ITSTPCTRD", 0}, mMatcher.getMatchedTOFLabelsVector(o2::dataformats::MatchInfoTOFReco::TrackType::ITSTPCTRD)); } } // TODO: TRD-matched tracks - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0}, mMatcher.getCalibVector()); if (mPushMatchable) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_0", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(0)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_1", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(1)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_2", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(2)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_3", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(3)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_4", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(4)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_5", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(5)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_6", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(6)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_7", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(7)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_8", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(8)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_9", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(9)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_10", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(10)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_11", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(11)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_12", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(12)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_13", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(13)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_14", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(14)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_15", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(15)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_16", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(16)); - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0, Lifetime::Timeframe}, mMatcher.getMatchedTracksPair(17)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_0", 0}, mMatcher.getMatchedTracksPair(0)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_1", 0}, mMatcher.getMatchedTracksPair(1)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_2", 0}, mMatcher.getMatchedTracksPair(2)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_3", 0}, mMatcher.getMatchedTracksPair(3)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_4", 0}, mMatcher.getMatchedTracksPair(4)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_5", 0}, mMatcher.getMatchedTracksPair(5)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_6", 0}, mMatcher.getMatchedTracksPair(6)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_7", 0}, mMatcher.getMatchedTracksPair(7)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_8", 0}, mMatcher.getMatchedTracksPair(8)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_9", 0}, mMatcher.getMatchedTracksPair(9)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_10", 0}, mMatcher.getMatchedTracksPair(10)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_11", 0}, mMatcher.getMatchedTracksPair(11)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_12", 0}, mMatcher.getMatchedTracksPair(12)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_13", 0}, mMatcher.getMatchedTracksPair(13)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_14", 0}, mMatcher.getMatchedTracksPair(14)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_15", 0}, mMatcher.getMatchedTracksPair(15)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_16", 0}, mMatcher.getMatchedTracksPair(16)); + pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0}, mMatcher.getMatchedTracksPair(17)); } mTimer.Stop(); @@ -237,17 +238,21 @@ void TOFMatcherSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable) +DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes) { uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); + Options opts; auto dataRequest = std::make_shared(); if (strict) { dataRequest->setMatchingInputStrict(); } dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(GID::getSourceMask(GID::TOF), useMC); + if (tpcRefit && src[GID::TPC]) { + dataRequest->requestClusters(GID::getSourceMask(GID::TPC), false); + } if (useFIT) { - dataRequest->requestClusters(GID::getSourceMask(GID::FT0), false); + dataRequest->requestFT0RecPoints(false); } auto ggRequest = std::make_shared(false, // orbitResetTime @@ -259,7 +264,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); std::vector outputs; if (GID::includesSource(GID::TPC, src)) { outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe); @@ -313,8 +318,8 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo "tof-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, useFIT, tpcRefit, strict, pushMatchable)}, - Options{}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 1f1a296f448cb..14af8c12794cc 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -14,11 +14,14 @@ #include #include "GlobalTracking/MatchTPCITS.h" +#include "GlobalTracking/MatchTPCITSParams.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsTPC/Constants.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Framework/DataRefUtils.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include #include "TStopwatch.h" #include "Framework/ConfigParamRegistry.h" @@ -35,7 +38,9 @@ #include "DataFormatsTPC/WorkflowHelper.h" #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GlobalParams.h" #include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSBase/GeometryTGeo.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DataFormatsParameters/GRPECSObject.h" @@ -47,6 +52,10 @@ #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#ifdef ENABLE_UPGRADES +#include "ITS3Reconstruction/TopologyDictionary.h" +#endif + using namespace o2::framework; using MCLabelsCl = o2::dataformats::MCTruthContainer; using MCLabelsTr = gsl::span; @@ -60,8 +69,14 @@ namespace globaltracking class TPCITSMatchingDPL : public Task { public: - TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) {} + TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, + bool useFT0, bool calib, bool skipTPCOnly, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -86,10 +101,14 @@ void TPCITSMatchingDPL::init(InitContext& ic) { mTimer.Stop(); mTimer.Reset(); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + mMatching.setDebugTreeFileName(maxLanes == 1 ? "dbg_TPCITSmatch.root" : fmt::format("dbg_TPCITSmatch_{}.root", lane)); o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setNThreads(std::max(1, ic.options().get("nthreads"))); mMatching.setUseBCFilling(!ic.options().get("ignore-bc-check")); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); + mTPCCorrMapsLoader.init(ic); } void TPCITSMatchingDPL::run(ProcessingContext& pc) @@ -99,19 +118,18 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions - mMatching.run(recoData); + static pmr::vector dummyMCLab, dummyMCLabAB; + static pmr::vector> dummyCalib; - pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mMatching.getMatchedTracks()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}, mMatching.getABTrackletRefs()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}, mMatching.getABTrackletClusterIDs()); - if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}, mMatching.getMatchLabels()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}, mMatching.getABTrackletLabels()); - } + auto& matchedTracks = pc.outputs().make>(Output{"GLO", "TPCITS", 0}); + auto& ABTrackletRefs = pc.outputs().make>(Output{"GLO", "TPCITSAB_REFS", 0}); + auto& ABTrackletClusterIDs = pc.outputs().make>(Output{"GLO", "TPCITSAB_CLID", 0}); + auto& matchLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITS_MC", 0}) : dummyMCLab; + auto& ABTrackletLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITSAB_MC", 0}) : dummyMCLabAB; + auto& calib = mCalibMode ? pc.outputs().make>>(Output{"GLO", "TPCITS_VDTGL", 0}) : dummyCalib; + + mMatching.run(recoData, matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); - if (mCalibMode) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_VDTGL", 0, Lifetime::Timeframe}, mMatching.getTglITSTPC()); - } mTimer.Stop(); } @@ -120,6 +138,7 @@ void TPCITSMatchingDPL::endOfStream(EndOfStreamContext& ec) mMatching.end(); LOGF(info, "TPC-ITS matching total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + mMatching.reportTiming(); } void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -133,8 +152,12 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { return; } + if (matcher == ConcreteDataMatcher("GLO", "ITSTPCPARAM", 0)) { + LOG(info) << "ITS-TPC Matching params updated from ccdb"; + return; + } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated"; + LOG(info) << "its cluster dictionary updated"; mMatching.setITSDictionary((const o2::itsmft::TopologyDictionary*)obj); return; } @@ -144,16 +167,29 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) par.printKeyValues(); return; } + if (matcher == ConcreteDataMatcher("ITS", "GEOMTGEO", 0)) { + LOG(info) << "ITS GeometryTGeo loaded from ccdb"; + o2::its::GeometryTGeo::adopt((o2::its::GeometryTGeo*)obj); + return; + } +#ifdef ENABLE_UPGRADES + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mMatching.setIT3Dictionary((const o2::its3::TopologyDictionary*)obj); + return; + } +#endif } void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; + pc.inputs().get("MatchParam"); // Note: ITS/CLUSDICT and ITS/ALPIDEPARAM are requested/loaded by the recocontainer const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); @@ -165,6 +201,7 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) mMatching.setITSTimeBiasInBC(alpParams.roFrameBiasInBC); mMatching.setSkipTPCOnly(mSkipTPCOnly); mMatching.setITSTriggered(!o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); + mMatching.setNHBPerTF(o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF()); mMatching.setMCTruthOn(mUseMC); mMatching.setUseFT0(mUseFT0); mMatching.setVDriftCalib(mCalibMode); @@ -173,15 +210,23 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } else { mMatching.setCosmics(true); } + if (pc.inputs().getPos("itsTGeo") >= 0) { + pc.inputs().get("itsTGeo"); + } mMatching.init(); + // check consistency of material correction options + if (mMatching.getUseMatCorrFlag() == o2::base::Propagator::MatCorrType::USEMatCorrTGeo && !o2::base::GeometryManager::isGeometryLoaded()) { + LOGP(fatal, "USEMatCorrTGeo cannot work w/o full geometry request in the GRPGeomHelper"); + } } // we may have other params which need to be queried regularly bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { - mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } + mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); + if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -196,17 +241,27 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } } -DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) +DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; auto dataRequest = std::make_shared(); if ((src & GTrackID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any()) { // preliminary stage of extended workflow ? dataRequest->setMatchingInputStrict(); } + dataRequest->inputs.emplace_back("MatchParam", "GLO", "ITSTPCPARAM", 0, Lifetime::Condition, ccdbParamSpec("GLO/Config/ITSTPCParam")); dataRequest->requestTracks(src, useMC); dataRequest->requestTPCClusters(false); - dataRequest->requestITSClusters(useMC); // Only ITS clusters labels are needed for the afterburner +// Only ITS clusters labels are needed for the afterburner and request possibly for ITS3 +#ifdef ENABLE_UPGRADES + if (o2::GlobalParams::Instance().withITS3) { + dataRequest->requestIT3Clusters(useMC); + } else { + dataRequest->requestITSClusters(useMC); + } +#else + dataRequest->requestITSClusters(useMC); +#endif if (useFT0) { dataRequest->requestFT0RecPoints(false); } @@ -224,26 +279,32 @@ DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool } // Note: ITS/CLUSDICT and ITS/ALPIDEPARAM are requested/loaded by the recocontainer - auto ggRequest = std::make_shared(true, // orbitResetTime - true, // GRPECS=true - true, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + useGeom ? o2::base::GRPGeomRequest::Aligned : o2::base::GRPGeomRequest::None, // geometry dataRequest->inputs, true); // query only once all objects except mag.field + if (!useGeom) { // load ITS GeomTGeo since the full geometry is not loaded + ggRequest->addInput({"itsTGeo", "ITS", "GEOMTGEO", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/Geometry")}, dataRequest->inputs); + } + + Options opts{ + {"nthreads", VariantType::Int, 1, {"Number of afterburner threads"}}, + {"ignore-bc-check", VariantType::Bool, false, {"Do not check match candidate against BC filling"}}, + {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); return DataProcessorSpec{ "itstpc-track-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useFT0, calib, skipTPCOnly, useMC)}, - Options{ - {"nthreads", VariantType::Int, 1, {"Number of afterburner threads"}}, - {"ignore-bc-check", VariantType::Bool, false, {"Do not check match candidate against BC filling"}}, - {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useFT0, calib, skipTPCOnly, useMC)}, + opts}; } } // namespace globaltracking diff --git a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx index 281fd06dea7eb..f24e7c13e336f 100644 --- a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx @@ -78,8 +78,8 @@ void VertexTrackMatcherSpec::run(ProcessingContext& pc) mMatcher.process(recoData, trackIndex, vtxRefs); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe}, trackIndex); - pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe}, vtxRefs); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTC", 0}, trackIndex); + pc.outputs().snapshot(Output{"GLO", "PVTX_TRMTCREFS", 0}, vtxRefs); mTimer.Stop(); LOG(info) << "Made " << trackIndex.size() << " track associations for " << recoData.getPrimaryVertices().size() @@ -150,7 +150,7 @@ DataProcessorSpec getVertexTrackMatcherSpec(GTrackID::mask_t src) dataRequest->requestTracks(src, false); dataRequest->requestClusters(src & GTrackID::getSourcesMask("EMC,PHS,CPV"), false); - dataRequest->requestPrimaryVerterticesTMP(false); + dataRequest->requestPrimaryVerticesTMP(false); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index d3f7aafe202e5..e4082bdd14d86 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -17,6 +17,7 @@ #include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" @@ -30,6 +31,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -50,6 +52,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -74,19 +77,35 @@ void customize(std::vector& policies) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; - GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,ITS-TPC-TOF"); + GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TRD,TPC-TOF,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2match-cosmics-workflow_configuration.ini"); - + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); auto useMC = !configcontext.options().get("disable-mc"); auto disableRootOut = configcontext.options().get("disable-root-output"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); + if (GID::includesDet(DetID::TPC, src)) { + src |= GID::getSourceMask(GID::TPC); + } + if (GID::includesDet(DetID::TRD, src)) { + src |= GID::getSourceMask(GID::TRD); + } + if (GID::includesDet(DetID::TOF, src)) { + src |= GID::getSourceMask(GID::TOF); + } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } + GID::mask_t srcCl = src; GID::mask_t dummy; - specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC)); + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC, sclOpt)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC, dummy); // clusters MC is not needed diff --git a/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx new file mode 100644 index 0000000000000..2d748ae3cffb3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/hmp-matcher-workflow.cxx @@ -0,0 +1,144 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "ITSWorkflow/TrackReaderSpec.h" +#include "TPCReaderWorkflow/TrackReaderSpec.h" + +#include "GlobalTrackingWorkflow/HMPMatcherSpec.h" +#include "HMPIDWorkflow/HMPMatchedWriterSpec.h" + +#include "HMPIDWorkflow/ClustersReaderSpec.h" + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" +#include "Algorithm/RangeTokenizer.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +// #include "TOFBase/Utils.h" +#include "Steer/MCKinematicsReader.h" +#include "TSystem.h" +#include "DetectorsBase/DPLWorkflowUtils.h" + +#include "GlobalTracking/MatchHMP.h" + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using GID = o2::dataformats::GlobalTrackID; +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:HMP|hmp).*[W,w]riter.*")); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use: allowed ITS-TPC,TPC-TRD,TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF (all)"}}, + {"trd-extra-tolerance", o2::framework::VariantType::Float, 0.0f, {"Extra time tolerance for TRD tracks in microsec"}}, + {"tof-extra-tolerance", o2::framework::VariantType::Float, 0.0f, {"Extra time tolerance for TRD tracks in microsec"}}, + {"combine-devices", o2::framework::VariantType::Bool, false, {"merge DPL source/writer devices"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + // write the configuration used for the workflow + + auto useMC = !configcontext.options().get("disable-mc"); + auto extratolerancetrd = configcontext.options().get("trd-extra-tolerance"); + auto extratolerancetof = configcontext.options().get("tof-extra-tolerance"); + auto disableRootIn = configcontext.options().get("disable-root-input"); + auto disableRootOut = configcontext.options().get("disable-root-output"); + + bool writematching = 0; + bool writecalib = 0; + + LOG(debug) << "HMP MATCHER WORKFLOW configuration"; + LOG(debug) << "HMP disable-mc = " << configcontext.options().get("disable-mc"); + LOG(debug) << "HMP disable-root-input = " << disableRootIn; + LOG(debug) << "HMP disable-root-output = " << disableRootOut; + + GID::mask_t alowedSources = GID::getSourcesMask("ITS-TPC,TPC-TRD,TPC-TOF,ITS-TPC-TRD,ITS-TPC-TOF,TPC-TRD-TOF,ITS-TPC-TRD-TOF"); + + GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); + + GID::mask_t mcmaskcl; + GID::mask_t nonemask = GID::getSourcesMask(GID::NONE); + GID::mask_t clustermask = GID::getSourcesMask("HMP"); + + if (useMC) { + mcmaskcl |= GID::getSourceMask(GID::HMP); + } + + WorkflowSpec inputspecs; + o2::globaltracking::InputHelper::addInputSpecs(configcontext, inputspecs, clustermask, src, src, useMC, mcmaskcl, GID::getSourcesMask(GID::ALL)); + if (configcontext.options().get("combine-devices")) { + std::vector unmerged; + specs.push_back(specCombiner("HMP-readers", inputspecs, unmerged)); + if (unmerged.size() > 0) { + LOG(fatal) << "Unexpected DPL device merge problem"; + } + } else { + for (auto& s : inputspecs) { + specs.push_back(s); + } + } + + specs.emplace_back(o2::globaltracking::getHMPMatcherSpec(src, useMC, extratolerancetrd, extratolerancetof)); + + if (!disableRootOut) { + std::vector writers; + writers.emplace_back(o2::hmpid::getHMPMatchedWriterSpec(useMC, "o2match_hmp.root")); //, false, (int)o2::globaltracking::MatchHMP::trackType::CONSTR, false)); + + if (configcontext.options().get("combine-devices")) { + std::vector unmerged; + specs.push_back(specCombiner("HMP-writers", writers, unmerged)); + if (unmerged.size() > 0) { + LOG(fatal) << "Unexpected DPL device merge problem"; + } + } else { + for (auto& s : writers) { + specs.push_back(s); + } + } + } + + // configure dpl timer to inject correct firstTFOrbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx index 0fb10c9530200..d0a0b829c03bc 100644 --- a/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/primary-vertexing-workflow.cxx @@ -57,6 +57,7 @@ void customize(std::vector& workflowOptions) {"skip", VariantType::Bool, false, {"pass-through mode (skip vertexing)"}}, {"validate-with-ft0", o2::framework::VariantType::Bool, false, {"use FT0 time for vertex validation"}}, {"vertex-track-matching-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use in vertex-track associations or \"none\" to disable matching"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"load full geometry"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); @@ -83,6 +84,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto disableRootOut = configcontext.options().get("disable-root-output"); auto validateWithFT0 = configcontext.options().get("validate-with-ft0"); auto skip = configcontext.options().get("skip"); + auto useGeom = configcontext.options().get("use-full-geometry"); GID::mask_t srcPV = allowedSourcesPV & GID::getSourcesMask(configcontext.options().get("vertexing-sources")); GID::mask_t srcVT = allowedSourcesVT & GID::getSourcesMask(configcontext.options().get("vertex-track-matching-sources")); @@ -92,7 +94,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t srcComb = srcPV | srcVT; GID::mask_t dummy, srcClus = GID::includesDet(DetID::TOF, srcComb) ? GID::getSourceMask(GID::TOF) : dummy; - specs.emplace_back(o2::vertexing::getPrimaryVertexingSpec(srcPV, skip, validateWithFT0, useMC)); + specs.emplace_back(o2::vertexing::getPrimaryVertexingSpec(srcPV, skip, validateWithFT0, useMC, useGeom)); specs.emplace_back(o2::vertexing::getVertexTrackMatcherSpec(srcVT)); auto srcMtc = srcComb; diff --git a/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx new file mode 100644 index 0000000000000..5deabc8e70344 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/src/reader-driver-workflow.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ChannelSpecHelpers.h" +#include "GlobalTrackingWorkflow/ReaderDriverSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}, + {"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root,upstream"); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); + std::string chanFmt = configcontext.options().get("metric-feedback-channel-format"); + std::string metricChannel{}; + if (rateLimitingIPCID > -1 && !chanFmt.empty()) { + metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); + } + size_t minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); + + WorkflowSpec specs; + specs.emplace_back(o2::globaltracking::getReaderDriverSpec(metricChannel, minSHM)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index cb851b8733404..0ac640cbad9fd 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -11,11 +11,13 @@ #include "GlobalTrackingWorkflow/SecondaryVertexingSpec.h" #include "GlobalTrackingWorkflow/SecondaryVertexWriterSpec.h" +#include "GlobalTrackingWorkflow/StrangenessTrackingWriterSpec.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "ITSWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -27,6 +29,7 @@ #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -50,11 +53,16 @@ void customize(std::vector& workflowOptions) std::vector options{ {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC (relevant for strangeness tracker only))"}}, {"vertexing-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use in vertexing"}}, {"disable-cascade-finder", o2::framework::VariantType::Bool, false, {"do not run cascade finder"}}, - {"enable-3body-finder", o2::framework::VariantType::Bool, false, {"run 3 body finder"}}, + {"disable-3body-finder", o2::framework::VariantType::Bool, false, {"do not run 3 body finder"}}, + {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"do not run strangeness tracker"}}, + {"disable-ccdb-params", o2::framework::VariantType::Bool, false, {"do not load the svertexer parameters from the ccdb"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -71,17 +79,30 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2secondary-vertexing-workflow_configuration.ini"); - bool useMC = false; + bool useMC = !configcontext.options().get("disable-mc"); auto disableRootOut = configcontext.options().get("disable-root-output"); + auto enableCCDBParams = !configcontext.options().get("disable-ccdb-params"); auto enableCasc = !configcontext.options().get("disable-cascade-finder"); - auto enable3body = configcontext.options().get("enable-3body-finder"); - + auto enable3body = !configcontext.options().get("disable-3body-finder"); + auto enableStrTr = !configcontext.options().get("disable-strangeness-tracker"); + auto useGeom = configcontext.options().get("use-full-geometry"); + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); GID::mask_t src = allowedSources & GID::getSourcesMask(configcontext.options().get("vertexing-sources")); GID::mask_t dummy, srcClus = GID::includesDet(DetID::TOF, src) ? GID::getSourceMask(GID::TOF) : dummy; // eventually, TPC clusters will be needed for refit - + if (enableStrTr) { + srcClus |= GID::getSourceMask(GID::ITS); + } + if (src[GID::TPC]) { + srcClus |= GID::getSourceMask(GID::TPC); + } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } WorkflowSpec specs; - - specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body)); + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom, sclOpt)); // only TOF clusters are needed if TOF is involved, no clusters MC needed WorkflowSpec inputspecs; @@ -106,6 +127,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (!disableRootOut) { specs.emplace_back(o2::vertexing::getSecondaryVertexWriterSpec()); + if (enableStrTr) { + specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackingWriterSpec(useMC)); + } } // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx index 51a1a0dd44e71..bdc1af958886c 100644 --- a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx @@ -42,7 +42,9 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -58,17 +60,22 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); auto useRootInput = !configcontext.options().get("disable-root-input"); + auto disableRootOut = configcontext.options().get("disable-root-output"); + auto useGeom = configcontext.options().get("use-full-geometry"); o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); o2::conf::ConfigurableParam::writeINI("o2strangeness_tracking_workflow_configuration.ini"); GID::mask_t itsSource = GID::getSourceMask(GID::ITS); // ITS tracks and clusters WorkflowSpec specs; - specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackerSpec(itsSource, useMC)); + specs.emplace_back(o2::strangeness_tracking::getStrangenessTrackerSpec(itsSource, useMC, useGeom)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, itsSource, itsSource, itsSource, useMC, itsSource); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // S-vertex is always needed - specs.emplace_back(getStrangenessTrackingWriterSpec(useMC)); + + if (!disableRootOut) { + specs.emplace_back(getStrangenessTrackingWriterSpec(useMC)); + } // configure dpl timer to inject correct firstTFOrbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx index 624e2602fda42..8843570fc3b52 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx @@ -26,9 +26,7 @@ void customize(std::vector& policies) void customize(std::vector& workflowOptions) { // option allowing to set parameters - std::vector options{ - ConfigParamSpec{"dataspec", VariantType::String, "tfidinfo:FLP/DISTSUBTIMEFRAME/0xccdb", {"spec from which the TFIDInfo will be extracted"}}, - ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + std::vector options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -37,7 +35,10 @@ void customize(std::vector& workflowOptions) #include "Framework/DataProcessingHeader.h" #include "Framework/Task.h" #include "CommonDataFormat/TFIDInfo.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "CommonUtils/NameConf.h" +#include "CommonConstants/LHCConstants.h" +#include "Framework/CCDBParamSpec.h" #include #include @@ -53,17 +54,31 @@ class TFIDInfoWriter : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // new run is starting + auto v = pc.inputs().get*>("orbitReset"); + mOrbitReset = (*v)[0]; + } o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mData.emplace_back()); } void endOfStream(EndOfStreamContext& ec) final { + o2::utils::TreeStreamRedirector pcstream; TFile fl(mOutFileName.c_str(), "recreate"); fl.WriteObjectAny(&mData, "std::vector", "tfidinfo"); - LOGP(info, "Wrote TFIDInfo vector with {} entries to {}", mData.size(), fl.GetName()); + pcstream.SetFile(&fl); + for (const auto& info : mData) { + long ts = (mOrbitReset + long(info.firstTForbit * o2::constants::lhc::LHCOrbitMUS)) / 1000; + pcstream << "tfidTree" + << "tfidinfo=" << info << "ts=" << ts << "\n"; + } + pcstream.Close(); + LOGP(info, "Wrote tfidinfo vector and tfidTree with {} entries to {}", mData.size(), fl.GetName()); } private: + long mOrbitReset = 0; std::string mOutFileName{}; std::vector mData; }; @@ -72,8 +87,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec wf; o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(DataProcessorSpec{"tfid-info-writer", o2::framework::select(cfgc.options().get("dataspec").c_str()), - std::vector{}, AlgorithmSpec{adaptFromTask()}, + wf.emplace_back(DataProcessorSpec{"tfid-info-writer", + {{"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}}, + std::vector{}, + AlgorithmSpec{adaptFromTask()}, Options{{"tfidinfo-file-name", VariantType::String, o2::base::NameConf::getTFIDInfoFileName(), {"output file for TFIDInfo"}}}}); return wf; } diff --git a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx index 909692e214780..9a95c83617210 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx @@ -32,6 +32,8 @@ #include "Steer/MCKinematicsReader.h" #include "TSystem.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -54,6 +56,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tof-lanes", o2::framework::VariantType::Int, 1, {"number of parallel lanes up to the matcher"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use: allowed TPC,ITS-TPC,TPC-TRD,ITS-TPC-TRD (all)"}}, @@ -63,9 +66,11 @@ void customize(std::vector& workflowOptions) {"output-type", o2::framework::VariantType::String, "matching-info", {"matching-info, calib-info"}}, {"enable-dia", o2::framework::VariantType::Bool, false, {"to require diagnostic freq and then write to calib outputs (obsolete since now default)"}}, {"trd-extra-tolerance", o2::framework::VariantType::Float, 500.0f, {"Extra time tolerance for TRD tracks in ns"}}, + {"refit-tpc-tof", o2::framework::VariantType::Bool, false, {"Refit unconstrained TPC tracks matched to TOF (if false - just move)"}}, {"write-matchable", o2::framework::VariantType::Bool, false, {"write all matchable pairs in a file (o2matchable_tof.root)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-devices", o2::framework::VariantType::Bool, false, {"merge DPL source/writer devices"}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -92,10 +97,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto diagnostic = configcontext.options().get("enable-dia"); auto extratolerancetrd = configcontext.options().get("trd-extra-tolerance"); auto writeMatchable = configcontext.options().get("write-matchable"); - + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); bool writematching = 0; bool writecalib = 0; + bool refitTPCTOF = configcontext.options().get("refit-tpc-tof"); auto outputType = configcontext.options().get("output-type"); + auto nLanes = configcontext.options().get("tof-lanes"); if (outputType.rfind("matching-info") < outputType.size()) { writematching = 1; } @@ -107,9 +114,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } - if (!writecalib) { - useFIT = false; - } + // if (!writecalib) { + // useFIT = false; + // } LOG(debug) << "TOF MATCHER WORKFLOW configuration"; LOG(debug) << "TOF track inputs = " << configcontext.options().get("track-sources"); @@ -122,6 +129,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(debug) << "TOF matching in strict mode = " << strict; LOG(debug) << "TOF extra time tolerance for TRD tracks = " << extratolerancetrd; LOG(debug) << "Store all matchables = " << writeMatchable; + LOG(debug) << "TOF Nlanes for matcher = " << nLanes; //GID::mask_t alowedSources = GID::getSourcesMask("TPC,ITS-TPC"); GID::mask_t alowedSources = GID::getSourcesMask("TPC,ITS-TPC,TPC-TRD,ITS-TPC-TRD"); @@ -137,7 +145,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (useFIT) { clustermask |= GID::getSourceMask(GID::FT0); } - + if (src[GID::TPC] && refitTPCTOF) { // load clusters + clustermask |= GID::getSourceMask(GID::TPC); + } + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } if (useMC) { mcmaskcl |= GID::getSourceMask(GID::TOF); } @@ -155,8 +168,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) specs.push_back(s); } } - - specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, false, strict, extratolerancetrd, writeMatchable)); // doTPCrefit not yet supported (need to load TPC clusters?) + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, sclOpt, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) if (!disableRootOut) { std::vector writers; diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 09a3c3e865ed0..810c7c564b4a8 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "TPCWorkflow/ClusterSharingMapSpec.h" +#include "TPCWorkflow/TPCScalerSpec.h" #include "GlobalTrackingWorkflow/TPCITSMatchingSpec.h" #include "GlobalTrackingWorkflow/TrackWriterTPCITSSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" @@ -23,6 +24,7 @@ #include "Framework/CallbacksPolicy.h" #include "Framework/ConfigContext.h" #include "Framework/CompletionPolicyHelpers.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -42,7 +44,9 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, "TPC", {"comma-separated list of sources to use: TPC,TPC-TOF,TPC-TRD,TPC-TRD-TOF"}}, {"produce-calibration-data", o2::framework::VariantType::Bool, false, {"produce output for TPC vdrift calibration"}}, + {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -69,20 +73,27 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2matchtpcits-workflow_configuration.ini"); - GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TPC-TOF"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); bool needStrictTRDTOF = (src & GID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any(); + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); + auto useGeom = configcontext.options().get("use-full-geometry"); auto useFT0 = configcontext.options().get("use-ft0"); if (useFT0) { src |= GID::getSourceMask(GID::FT0); } auto useMC = !configcontext.options().get("disable-mc"); auto calib = configcontext.options().get("produce-calibration-data"); - auto srcL = src | GID::getSourcesMask("ITS,TPC"); // ITS is neadded always, TPC must be loaded even if bare TPC tracks are not used in matching + auto srcL = src | GID::getSourcesMask("ITS,TPC"); // ITS is needed always, TPC must be loaded even if bare TPC tracks are not used in matching + if (sclOpt.requestCTPLumi) { + srcL = srcL | GID::getSourcesMask("CTP"); + } o2::framework::WorkflowSpec specs; - specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useMC)); + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC, sclOpt)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::globaltracking::getTrackWriterTPCITSSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index bfa253dd5980a..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -9,19 +9,47 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +#add_compile_options(-O0 -g -fPIC) + o2_add_library(GlobalTrackingStudy + TARGETVARNAME targetName SOURCES src/TPCTrackStudy.cxx src/TrackingStudy.cxx + src/SVStudy.cxx + src/TrackMCStudy.cxx src/TPCDataFilter.cxx src/ITSOffsStudy.cxx src/DumpTracks.cxx + src/V0Ext.cxx + src/TrackInfoExt.cxx + src/TrackMCStudyConfig.cxx + src/TrackMCStudyTypes.cxx + src/TPCClusSelector.cxx + src/CheckResid.cxx + src/CheckResidConfig.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers O2::DataFormatsGlobalTracking O2::DetectorsVertexing + O2::TPCWorkflow O2::SimulationDataFormat) +o2_target_root_dictionary(GlobalTrackingStudy + HEADERS include/GlobalTrackingStudy/V0Ext.h + include/GlobalTrackingStudy/TrackInfoExt.h + include/GlobalTrackingStudy/TrackMCStudyConfig.h + include/GlobalTrackingStudy/TrackMCStudyTypes.h + include/GlobalTrackingStudy/CheckResidTypes.h + include/GlobalTrackingStudy/CheckResidConfig.h + LINKDEF src/GlobalTrackingStudyLinkDef.h +) + +o2_add_executable(study-workflow + COMPONENT_NAME sv + SOURCES src/sv-study-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + o2_add_executable(study-workflow COMPONENT_NAME tpc-track SOURCES src/tpc-track-study-workflow.cxx @@ -32,6 +60,11 @@ o2_add_executable(study-workflow SOURCES src/tracking-study-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) +o2_add_executable(study-workflow + COMPONENT_NAME trackMC + SOURCES src/trackMCStudy-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + o2_add_executable(filter-workflow COMPONENT_NAME tpc-data SOURCES src/tpc-data-filter-workflow.cxx @@ -46,3 +79,13 @@ o2_add_executable(dump-workfow COMPONENT_NAME bc-tracks SOURCES src/track-dump-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +o2_add_executable(resid-workfow + COMPONENT_NAME check + SOURCES src/check-resid-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h new file mode 100644 index 0000000000000..a78fa5e8d41da --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_H +#define O2_CHECK_RESID_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +// #include "TPCCalibration/CorrectionMapsLoader.h" + +namespace o2::checkresid +{ +/// create a processor spec +o2::framework::DataProcessorSpec getCheckResidSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/); + +} // namespace o2::checkresid + +#endif // O2_CHECK_RESID_H diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h new file mode 100644 index 0000000000000..2a07eaf87930f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -0,0 +1,41 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_CONFIG_H +#define O2_CHECK_RESID_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::checkresid +{ +struct CheckResidConfig : o2::conf::ConfigurableParamHelper { + int minPVContributors = 10; + int minTPCCl = 60; + int minITSCl = 7; + float minPt = 0.4f; + float maxPt = 100.f; + float rCompIBOB = 12.f; + + bool pvcontribOnly = true; + bool addPVAsCluster = true; + bool useStableRef = true; + bool doIBOB = true; + bool doResid = true; + + bool refitPV = true; + float refitPVMV = false; + float refitPVIniScale = 100.f; + + O2ParamDef(CheckResidConfig, "checkresid"); +}; +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h new file mode 100644 index 0000000000000..ebb6a7aabe9fa --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h @@ -0,0 +1,42 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_TYPES_H +#define O2_CHECK_RESID_TYPES_H + +#include "ReconstructionDataFormats/Track.h" + +namespace o2::checkresid +{ +struct Point { + float dy = 0.f; + float dz = 0.f; + float sig2y = 0.f; + float sig2z = 0.f; + float phi = 0.f; + float z = 0.f; + int16_t sens = -1; + int8_t lr = -1; // -1 = vtx + ClassDefNV(Point, 1) +}; + +struct Track { + o2::dataformats::GlobalTrackID gid{}; + o2::track::TrackPar track; + o2::track::TrackParCov trIBOut; + o2::track::TrackParCov trOBInw; + std::vector points; + ClassDefNV(Track, 1) +}; + +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h new file mode 100644 index 0000000000000..d54513cb07a60 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_SV_STUDY_H +#define O2_SV_STUDY_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/Track.h" +#include "MathUtils/detail/Bracket.h" +#include "DataFormatsTPC/ClusterNative.h" + +namespace o2::svstudy +{ +/// create a processor spec +o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcCls, bool useMC); + +} // namespace o2::svstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h new file mode 100644 index 0000000000000..c1765558458c2 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCClusSelector.h @@ -0,0 +1,92 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// helper class for TPC clusters selection + +#ifndef ALICEO2_TPCCLUSSELECTOR_H +#define ALICEO2_TPCCLUSSELECTOR_H + +#include +#include +#include + +namespace o2::tpc +{ +class ClusterNativeAccess; + +class TPCClusSelector +{ + // helper to select TPC cluster matching to certain timebin and optionally pads range + // example of usage: + /* + TPCClusSelector clSel; + o2::tpc::ClusterNativeHelper::Reader tcpClusterReader; + tcpClusterReader.init(native_clusters_file.c_str()); + o2::tpc::ClusterNativeAccess tpcClusterIdxStruct; + std::unique_ptr tpcClusterBuffer; ///< buffer for clusters in tpcClusterIdxStruct + o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer tpcClusterMCBuffer; ///< buffer for mc labels + + tcpClusterReader.read(iTF); + tcpClusterReader.fillIndex(tpcClusterIdxStruct, tpcClusterBuffer, tpcClusterMCBuffer); + + clSel.fill(tpcClusterIdxStruct); // Create sorted index + // to get i-th cluster in orderer timebins: + const auto& clus = tpcClusterIdxStruct.clusters[sector][row][ clSel.getIndex(sector, row, i)]; + + // to get sorted indices range of clusters in the tbmin:tbmax range + auto rng = clSel.findClustersRange(sector, row, tbmin, tbmax, tpcClusterIdxStruct); + if (rng.first>rng.second) { // nothing is found } + const auto& cln = tpcClusterIdxStruct.clusters[sector][row][clSel.getIndex(sector, row, rng.first )]; /... + + // to get number of clusters in tbmin:tbmax, padmin:padmax range (and optionally get the list) + std::vector cllist; // optional list + int nfnd = clSel.findClustersEntries(sector, row, tbmin, tbmax, padmin, padmax, tpcClusterIdxStruct, &cllist); + for (int i=0;i findClustersRange(int sec, int row, float tbmin, float tbmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct); + int findClustersEntries(int sec, int row, float tbmin, float tbmax, float padmin, float padmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct, std::vector* clIDDirect = nullptr); + void fill(const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct); + + int getNThreads() const { return mNThreads; } + void setNThreads(int n); + + private: + struct Sector { + static constexpr int NRows = 152; + std::array, NRows> rows; + void clear() + { + for (auto& r : rows) + r.clear(); + } + }; + + static constexpr int NSectors = 36; + std::array mSectors{}; + int mNThreads = 1; + + ClassDefNV(TPCClusSelector, 1); +}; + +} // namespace o2::tpc + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h index f6396f682ac3b..47385f400ec01 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h @@ -19,10 +19,15 @@ #include "MathUtils/detail/Bracket.h" #include "DataFormatsTPC/ClusterNative.h" +namespace o2::tpc +{ +struct CorrectionMapsLoaderGloOpts; +} + namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); +o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h new file mode 100644 index 0000000000000..e33a0def63842 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -0,0 +1,83 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended Track info (for debugging) + +#ifndef ALICEO2_TRINFOEXT_H +#define ALICEO2_TRINFOEXT_H + +#include "ReconstructionDataFormats/MatchInfoTOF.h" +#include "ReconstructionDataFormats/DCA.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" + +namespace o2 +{ +namespace dataformats +{ + +struct TrackInfoExt { + enum { TPCA = 0, + TPCC = 1, + kBitMask = 0xffff }; + o2::track::TrackParCov track; + DCA dca{}; + DCA dcaTPC{}; + VtxTrackIndex gid; + MatchInfoTOF infoTOF; + std::array innerTPCPos{}; // innermost cluster position at assigned time + std::array innerTPCPos0{}; // innermost cluster position at nominal time0 + float ttime = 0; + float ttimeE = 0; + float xmin = 0; + float chi2TPC = 0.f; + float chi2ITSTPC = 0.f; + float q2ptITS = 0.f; + float q2ptTPC = 0.f; + float q2ptITSTPC = 0.f; + float q2ptITSTPCTRD = 0.f; + uint16_t nClTPC = 0; + uint16_t nClTPCShared = 0; + uint16_t flags = 0; + uint8_t pattITS = 0; + uint8_t nClITS = 0; + uint8_t rowMinTPC = 0; + uint8_t padFromEdge = -1; + uint8_t rowMaxTPC = 0; + uint8_t rowCountTPC = 0; + size_t hashIU = 0; + void setTPCA() { setBit(int(TPCA)); } + void setTPCC() { setBit(int(TPCC)); } + void setTPCAC() { setBit(int(TPCC)); } + + bool isTPCA() const { return isBitSet(int(TPCA)); } + bool isTPCC() const { return isBitSet(int(TPCC)); } + bool isTPCAC() const { return isBitSet(int(TPCA)) && isBitSet(int(TPCC)); } + + float getTPCInX() const { return innerTPCPos[0]; } + float getTPCInY() const { return innerTPCPos[1]; } + float getTPCInZ() const { return innerTPCPos[2]; } + float getTPCInX0() const { return innerTPCPos0[0]; } + float getTPCInY0() const { return innerTPCPos0[1]; } + float getTPCInZ0() const { return innerTPCPos0[2]; } + + void setBits(std::uint16_t b) { flags = b; } + void setBit(int bit) { flags |= kBitMask & (0x1 << bit); } + void resetBit(int bit) { flags &= ~(kBitMask & (0x1 << bit)); } + bool isBitSet(int bit) const { return flags & (kBitMask & (0x1 << bit)); } + + ClassDefNV(TrackInfoExt, 8); +}; + +} // namespace dataformats +} // namespace o2 + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h new file mode 100644 index 0000000000000..d1326a47ac909 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h @@ -0,0 +1,28 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_H +#define O2_TRACKING_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "TPCCalibration/CorrectionMapsLoader.h" + +namespace o2::trackstudy +{ + +/// create a processor spec +o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV); + +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h new file mode 100644 index 0000000000000..ed78ba2a710ec --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_CONFIG_H +#define O2_TRACKING_STUDY_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::trackstudy +{ +struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper { + float minPt = 0.05; + float maxTgl = 1.5; + float minPtMC = 0.05; + float maxTglMC = 1.5; + float maxRMC = 33.; + float maxPosTglMC = 2.; + float maxPVZOffset = 15.; + float decayMotherMaxT = 1.0f; // max TOF in ns for mother particles to study + bool requireITSorTPCTrackRefs = true; + bool requireTopBottomRefs = false; + bool storeTPCTrackRefs = false; + bool storeITSInfo = true; + int minTPCRefsToExtractClRes = 2; + int nOccBinsDrift = 10; // number of bins for TPC max drift time, where we integrate the occupancies + int nTBPerOccBin = 48; // number of TB per occ bin + float rejectClustersResStat = 0.1; + float maxTPCRefExtrap = 2; // max dX to extrapolate the track ref when extrapolating track true posions + int minITSClForITSoutput = 7; // create special ITS otput only for long enough tracks + int decayPDG[5] = {310, 3122, 411, 421, -1}; // decays to study, must end by -1 + O2ParamDef(TrackMCStudyConfig, "trmcconf"); +}; +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h new file mode 100644 index 0000000000000..f5846bcce5a49 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h @@ -0,0 +1,324 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_TYPES_H +#define O2_TRACKING_STUDY_TYPES_H +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/TimeStamp.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "SimulationDataFormat/TrackReference.h" +#include +#include + +namespace o2::trackstudy +{ +struct MCTrackInfo { + + inline float getMCTimeMUS() const { return bcInTF * o2::constants::lhc::LHCBunchSpacingMUS; } + inline bool hasITSHitOnLr(int i) const { return (pattITSCl & ((0x1 << i) & 0x7f)) != 0; } + int getNITSClusCont() const; + int getNITSClusForAB() const; + int getLowestITSLayer() const; + int getHighestITSLayer() const; + std::vector occTPCV{}; + std::vector trackRefsTPC{}; + o2::track::TrackPar track{}; + o2::MCCompLabel label{}; + float occTPC = -1.f; + int occITS = -1.f; + int bcInTF = -1; + int pdg = 0; + int pdgParent = 0; + int parentEntry = -1; + int16_t nTPCCl = 0; + int16_t nTPCClShared = 0; + int8_t parentDecID = -1; + uint8_t minTPCRow = -1; + uint8_t maxTPCRow = 0; + uint8_t nUsedPadRows = 0; + uint8_t maxTPCRowInner = 0; // highest row in the sector containing the lowest one + uint8_t minTPCRowSect = -1; + uint8_t maxTPCRowSect = -1; + int8_t nITSCl = 0; + int8_t pattITSCl = 0; + uint8_t flags = 0; + + enum Flags : uint32_t { Primary = 0, + AddedAtRecStage = 2, + BitMask = 0xff }; + + bool isPrimary() const { return isBitSet(Primary); } + bool isAddedAtRecStage() const { return isBitSet(AddedAtRecStage); } + void setPrimary() { setBit(Primary); } + void setAddedAtRecStage() { setBit(AddedAtRecStage); } + + uint8_t getBits() const { return flags; } + bool isBitSet(int bit) const { return flags & (0xff & (0x1 << bit)); } + void setBits(std::uint8_t b) { flags = b; } + void setBit(int bit) { flags |= BitMask & (0x1 << bit); } + void resetBit(int bit) { flags &= ~(BitMask & (0x1 << bit)); } + + o2::track::TrackPar getTrackParTPC(float b, float x = 90) const; + float getTrackParTPCPar(int i, float b, float x = 90) const; + float getTrackParTPCPhiSec(float b, float x = 90) const; + + ClassDefNV(MCTrackInfo, 8); +}; + +struct RecTrack { + enum FakeFlag { + FakeITS = 0x1 << 0, + FakeTPC = 0x1 << 1, + FakeTRD = 0x1 << 2, + FakeTOF = 0x1 << 3, + FakeITSTPC = 0x1 << 4, + FakeITSTPCTRD = 0x1 << 5, + HASACSides = 0x1 << 6, + FakeGLO = 0x1 << 7 + }; + o2::track::TrackParCov track{}; + o2::dataformats::VtxTrackIndex gid{}; + o2::dataformats::TimeStampWithError ts{}; + o2::MCEventLabel pvLabel{}; + short pvID = -1; + uint8_t nClTPCShared = 0; + uint8_t flags = 0; + uint8_t nClITS = 0; + uint8_t nClTPC = 0; + uint8_t pattITS = 0; + int8_t lowestPadRow = -1; + int8_t padFromEdge = -1; + uint8_t rowMaxTPC = 0; + uint8_t rowCountTPC = 0; + + bool isFakeGLO() const { return flags & FakeGLO; } + bool isFakeITS() const { return flags & FakeITS; } + bool isFakeTPC() const { return flags & FakeTPC; } + bool isFakeTRD() const { return flags & FakeTRD; } + bool isFakeTOF() const { return flags & FakeTOF; } + bool isFakeITSTPC() const { return flags & FakeITSTPC; } + bool hasACSides() const { return flags & HASACSides; } + + ClassDefNV(RecTrack, 3); +}; + +struct TrackPairInfo { + RecTrack tr0; + RecTrack tr1; + uint8_t nshTPC = 0; + uint8_t nshTPCRow = 0; + + int getComb() const { return tr0.track.getSign() != tr1.track.getSign() ? 0 : (tr0.track.getSign() > 0 ? 1 : 2); } + float getDPhi() const + { + float dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + return dphi; + } + float getDTgl() const { return tr0.track.getTgl() - tr1.track.getTgl(); } + + ClassDefNV(TrackPairInfo, 1) +}; + +struct TrackFamily { // set of tracks related to the same MC label + MCTrackInfo mcTrackInfo{}; + std::vector recTracks{}; + o2::track::TrackParCov trackITSProp{}; + o2::track::TrackParCov trackTPCProp{}; + int8_t entITS = -1; + int8_t entTPC = -1; + int8_t entITSTPC = -1; + int8_t entITSFound = -1; // ITS track for this MC track, regardless if it was matched to TPC of another track + int8_t flags = 0; + float tpcT0 = -999.; + + bool contains(const o2::dataformats::VtxTrackIndex& ref) const + { + for (const auto& tr : recTracks) { + if (ref == tr.gid) { + return true; + } + } + return false; + } + const RecTrack& getTrackWithITS() const { return entITS < 0 ? dummyRecTrack : recTracks[entITS]; } + const RecTrack& getTrackWithTPC() const { return entTPC < 0 ? dummyRecTrack : recTracks[entTPC]; } + const RecTrack& getTrackWithITSTPC() const { return entITSTPC < 0 ? dummyRecTrack : recTracks[entITSTPC]; } + const RecTrack& getTrackWithITSFound() const { return entITSFound < 0 ? dummyRecTrack : recTracks[entITSFound]; } + const RecTrack& getLongestTPCTrack() const + { + int n = getLongestTPCTrackEntry(); + return n < 0 ? dummyRecTrack : recTracks[n]; + } + int getLongestTPCTrackEntry() const; + int getNTPCClones() const; + static RecTrack dummyRecTrack; // + + ClassDefNV(TrackFamily, 1); +}; + +struct ClResTPCCont { + // contributor to TPC Cluster + std::array xyz{}; + std::array below{}; + std::array above{}; + float snp = 0.; + float tgl = 0.; + float q2pt = 0.; + bool corrAttach = false; + + int getNExt() const { return (below[0] > 1.) + (above[0] > 1.); } + + float getClX() const { return xyz[0]; } + float getClY() const { return xyz[1]; } + float getClZ() const { return xyz[2]; } + + float getDY() const { return xyz[1] - getYRef(); } + float getDZ() const { return xyz[2] - getZRef(); } + + float getYRef() const + { + float y = 0; + int n = 0; + if (below[0] > 1.) { + y += below[1]; + n++; + } + if (above[0] > 1.) { + y += above[1]; + n++; + } + return n == 1 ? y : 0.5 * y; + } + + float getZRef() const + { + float z = 0; + int n = 0; + if (below[0] > 1.) { + z += below[2]; + n++; + } + if (above[0] > 1.) { + z += above[2]; + n++; + } + return n == 1 ? z : 0.5 * z; + } + + float getDXMin() const + { + float adxA = 1e9, adxB = 1e9; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[1] - below[0]; + } + return std::abs(adxA) < std::abs(adxB) ? adxA : adxB; + } + + float getDXMax() const + { + float adxA = 0, adxB = 0; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[0] - below[0]; + } + return std::abs(adxA) > std::abs(adxB) ? adxA : adxB; + } + + float getEY() const { return getNExt() > 1 ? below[1] - above[1] : -999; } + float getEZ() const { return getNExt() > 1 ? below[2] - above[2] : -999; } + + ClassDefNV(ClResTPCCont, 1); +}; + +struct ClResTPC { + uint8_t sect = 0; + uint8_t row = 0; + uint8_t ncont = 0; + uint8_t flags = 0; + uint8_t sigmaTimePacked; + uint8_t sigmaPadPacked; + float qmax = 0; + float qtot = 0; + float occ = 0; + float occBin = 0; + float getSigmaPad() const { return float(sigmaPadPacked) * (1.f / 32); } + float getSigmaTime() const { return float(sigmaTimePacked) * (1.f / 32); } + + std::vector contTracks; + int getNCont() const { return contTracks.size(); } + + float getDY(int i) const { return i < getNCont() ? contTracks[i].getDY() : -999.; } + float getDZ(int i) const { return i < getNCont() ? contTracks[i].getDZ() : -999.; } + float getYRef(int i) const { return i < getNCont() ? contTracks[i].getYRef() : -999.; } + float getZRef(int i) const { return i < getNCont() ? contTracks[i].getZRef() : -999.; } + float getDXMin(int i) const { return i < getNCont() ? contTracks[i].getDXMin() : -999.; } + float getDXMax(int i) const { return i < getNCont() ? contTracks[i].getDXMax() : -999.; } + float getEY(int i) const { return i < getNCont() ? contTracks[i].getEY() : -999.; } + float getEZ(int i) const { return i < getNCont() ? contTracks[i].getEZ() : -999.; } + + void sortCont() + { + std::sort(contTracks.begin(), contTracks.end(), [](const ClResTPCCont& a, const ClResTPCCont& b) { + float dya = a.getDY(), dyb = b.getDY(), dza = a.getDZ(), dzb = b.getDZ(); + return dya * dya + dza * dza < dyb * dyb + dzb * dzb; + }); + } + + ClassDefNV(ClResTPC, 2); +}; + +struct ITSHitInfo { + o2::BaseCluster clus{}; + o2::TrackReference tref{}; + float trefXT = 0; // track ref tracking frame coordinates + float trefYT = 0; + float chipX = 0; + float chipAlpha = 0; + ClassDefNV(ITSHitInfo, 1); +}; + +struct RecPV { + o2::dataformats::PrimaryVertex pv{}; + o2::MCEventLabel mcEvLbl{}; + ClassDefNV(RecPV, 1); +}; + +struct MCVertex { + float getX() const { return pos[0]; } + float getY() const { return pos[1]; } + float getZ() const { return pos[2]; } + + std::array pos{0., 0., -1999.f}; + float ts = 0; + int nTrackSel = 0; // number of selected MC charged tracks + int ID = -1; + std::vector recVtx{}; + std::vector occTPCV{}; + ClassDefNV(MCVertex, 2); +}; + +} // namespace o2::trackstudy +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h index b3a55416f4818..7a15c191cbeed 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h @@ -18,11 +18,12 @@ #include "ReconstructionDataFormats/Track.h" #include "MathUtils/detail/Bracket.h" #include "DataFormatsTPC/ClusterNative.h" +#include "TPCCalibration/CorrectionMapsLoader.h" namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h new file mode 100644 index 0000000000000..b1a9f6923f04d --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended V0 info (for debugging) + +#ifndef ALICEO2_V0EXT_H +#define ALICEO2_V0EXT_H + +#include "ReconstructionDataFormats/V0.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2::dataformats +{ + +struct ProngInfoExt { + o2::track::TrackParCov trackTPC; + int nClTPC = 0; + int nClITS = 0; + int pattITS = 0; + float chi2ITSTPC = 0.f; + uint8_t lowestRow = -1; + uint8_t padFromEdge = -1; + int8_t corrGlo = -1; + int8_t corrITSTPC = -1; + int8_t corrITS = -1; + int8_t corrTPC = -1; + ClassDefNV(ProngInfoExt, 3); +}; + +struct V0Ext { + V0 v0; + V0Index v0ID; + std::array prInfo{}; + const ProngInfoExt& getPrInfo(int i) const { return prInfo[i]; } + int mcPID = -1; + ClassDefNV(V0Ext, 2); +}; + +} // namespace o2::dataformats + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx new file mode 100644 index 0000000000000..e6584a7055446 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -0,0 +1,567 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResid.h" +#include "GlobalTrackingStudy/CheckResidTypes.h" +#include "GlobalTrackingStudy/CheckResidConfig.h" +#include +#include "ReconstructionDataFormats/Track.h" +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "CommonUtils/NameConf.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "DetectorsVertexing/PVertexer.h" +#ifdef WITH_OPENMP +#include +#endif + +// Attention: in case the residuals are checked with geometry different from the one used for initial reconstruction, +// pass a --configKeyValues option for vertex refit as: +// ;pvertexer.useMeanVertexConstraint=false;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; +// In any case, it is better to pass ;pvertexer.useMeanVertexConstraint=false; + +namespace o2::checkresid +{ +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using timeEst = o2::dataformats::TimeStampWithError; + +class CheckResidSpec : public Task +{ + public: + CheckResidSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) + { + /* + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + */ + } + ~CheckResidSpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool refitPV(o2::dataformats::PrimaryVertex& pv, int vid); + bool refitITStrack(o2::track::TrackParCov& track, GTrackID gid); + bool processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack); + + o2::globaltracking::RecoContainer* mRecoData = nullptr; + int mNThreads = 1; + bool mMeanVertexUpdated = false; + float mITSROFrameLengthMUS = 0.f; + o2::dataformats::MeanVertexObject mMeanVtx{}; + std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + o2::vertexing::PVertexer mVertexer; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; ///< MC flag + std::unique_ptr mDBGOut; + GTrackID::mask_t mTracksSrc{}; +}; + +void CheckResidSpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + std::string dbgnm = maxLanes == 1 ? "checkResid.root" : fmt::format("checkResid_t{}.root", lane); + mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); + mNThreads = ic.options().get("nthreads"); +#ifndef WITH_OPENMP + if (mNThreads > 1) { + LOGP(warn, "No OpenMP"); + } + mNThreads = 1; +#endif + // mTPCCorrMapsLoader.init(ic); +} + +void CheckResidSpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + mRecoData = &recoData; + mRecoData->collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + mRecoData = &recoData; + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(); + mRecoData = nullptr; +} + +void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + pc.inputs().get("meanvtx"); + // mTPCVDriftHelper.extractCCDBInputs(pc); + // mTPCCorrMapsLoader.extractCCDBInputs(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + initOnceDone = true; + // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + if (!grp->isDetContinuousReadOut(DetID::ITS)) { + mITSROFrameLengthMUS = alpParams.roFrameLengthTrig / 1.e3; // ITS ROFrame duration in \mus + } else { + mITSROFrameLengthMUS = alpParams.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS ROFrame duration in \mus + } + auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + o2::conf::ConfigurableParam::updateFromString("pvertexer.useTimeInChi2=false;"); + mVertexer.init(); + } + if (mMeanVertexUpdated) { + mMeanVertexUpdated = false; + mVertexer.initMeanVertexConstraint(); + } + bool updateMaps = false; + /* + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + } + */ +} + +void CheckResidSpec::process() +{ + if (!mITSDict) { + LOGP(fatal, "ITS data is not loaded"); + } + const auto itsTracks = mRecoData->getITSTracks(); + // const auto itsLbls = mRecoData->getITSTracksMCLabels(); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto clusITS = mRecoData->getITSClusters(); + const auto patterns = mRecoData->getITSClustersPatterns(); + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + auto pattIt = patterns.begin(); + mITSClustersArray.clear(); + mITSClustersArray.reserve(clusITS.size()); + + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + + auto pvvec = mRecoData->getPrimaryVertices(); + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = mRecoData->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto prop = o2::base::Propagator::Instance(); + static int TFCount = 0; + int nv = vtxRefs.size() - 1; + std::vector> slots; + slots.resize(mNThreads); + int nvGood = 0, nvUse = 0, nvRefFail = 0; + long pvFitDuration{}; + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + auto pve = pvvec[iv]; + if (pve.getNContributors() < params.minPVContributors) { + continue; + } + nvGood++; + if (params.refitPV) { + LOGP(debug, "Refitting PV#{} of {} tracks", iv, pve.getNContributors()); + auto tStartPVF = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + bool res = refitPV(pve, iv); + pvFitDuration += std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - tStartPVF; + if (!res) { + nvRefFail++; + continue; + } + } + nvUse++; + for (int is = 0; is < GTrackID::NSources; is++) { + if (!mTracksSrc[is] || !mRecoData->isTrackSourceLoaded(is)) { + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + if (!dm[DetID::ITS]) { + continue; + } + if (dm[DetID::TPC] && params.minTPCCl > 0 && !mRecoData->isTrackSourceLoaded(GTrackID::TPC)) { + LOGP(fatal, "Cut on TPC tracks is requested by they are not loaded"); + } +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + bool pvCont = vid.isPVContributor(); + if (!pvCont && params.pvcontribOnly) { + continue; + } + if (dm[DetID::TPC] && params.minTPCCl > 0 && mRecoData->getTPCTrack(mRecoData->getTPCContributorGID(vid)).getNClusters() < params.minTPCCl) { + continue; + } + auto gidITS = mRecoData->getITSContributorGID(vid); + if (gidITS.getSource() != GTrackID::ITS) { + continue; + } + const auto& trc = mRecoData->getTrackParam(vid); + auto pt = trc.getPt(); + if (pt < params.minPt || pt > params.maxPt) { + continue; + } + const auto& itsTrack = mRecoData->getITSTrack(gidITS); + if (itsTrack.getNClusters() < params.minITSCl) { + continue; + } +#ifdef WITH_OPENMP + auto& accum = slots[omp_get_thread_num()]; +#else + auto& accum = slots[0]; +#endif + auto& resTrack = accum.emplace_back(); + resTrack.gid = vid; + if (!processITSTrack(itsTrack, pve, resTrack)) { + accum.pop_back(); + continue; + } + } + } + } + // output + for (const auto& accum : slots) { + for (const auto& tr : accum) { + (*mDBGOut) << "res" << "tr=" << tr << "\n"; + } + } + LOGP(info, "processed {} PVs out of {} good vertices (out of {} in total), PV refits took {} mus, {} refits failed", nvUse, nvGood, nv, pvFitDuration, nvRefFail); + TFCount++; +} + +bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack) +{ + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + auto trFitInw = iTrack.getParamOut(); // seed for inward refit + auto trFitOut = iTrack.getParamIn(); // seed for outward refit + auto prop = o2::base::Propagator::Instance(); + auto geom = o2::its::GeometryTGeo::Instance(); + float pvAlpha = 0; + float bz = prop->getNominalBz(); + std::array*, 8> clArr{}; + const auto& params = CheckResidConfig::Instance(); + std::array extrapOut, extrapInw; // 2-way Kalman extrapolations, vertex + 7 layers + + auto rotateTrack = [bz](o2::track::TrackParCov& tr, float alpha, o2::track::TrackPar* refLin) { + return refLin ? tr.rotate(alpha, *refLin, bz) : tr.rotate(alpha); + }; + + auto accountCluster = [&](int i, std::array& extrapDest, o2::track::TrackParCov& tr, o2::track::TrackPar* refLin) { + if (clArr[i]) { // update with cluster + if (!rotateTrack(tr, i == 0 ? pvAlpha : geom->getSensorRefAlpha(clArr[i]->getSensorID()), refLin) || + !prop->propagateTo(tr, refLin, clArr[i]->getX(), true)) { + return 0; + } + extrapDest[i] = tr; // before update + if (!tr.update(*clArr[i])) { + return 0; + } + } else { + extrapDest[i].invalidate(); + return -1; + } + return 1; + }; + + auto inv2d = [](float s00, float s11, float s01) -> std::array { + auto det = s00 * s11 - s01 * s01; + if (det < 1e-16) { + return {0.f, 0.f, 0.f}; + } + det = 1.f / det; + return {s11 * det, s00 * det, -s01 * det}; + }; + + resTrack.points.clear(); + if (!prop->propagateToDCA(pv, trFitOut, bz)) { + LOGP(debug, "Failed to propagateToDCA, {}", trFitOut.asString()); + return false; + } + float cosAlp, sinAlp; + pvAlpha = trFitOut.getAlpha(); + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); // vertex position rotated to track frame + o2::BaseCluster bcPV; + if (params.addPVAsCluster) { + bcPV.setXYZ(pv.getX() * cosAlp + pv.getY() * sinAlp, -pv.getX() * sinAlp + pv.getY() * cosAlp, pv.getZ()); + bcPV.setSigmaY2(0.5 * (pv.getSigmaX2() + pv.getSigmaY2())); + bcPV.setSigmaZ2(pv.getSigmaZ2()); + bcPV.setSensorID(-1); + clArr[0] = &bcPV; + } + // collect all track clusters to array, placing them to layer+1 slot + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { // clusters are ordered from the outermost to the innermost + const auto& curClu = mITSClustersArray[itsClRefs[iTrack.getClusterEntry(i)]]; + + int llr = geom->getLayer(curClu.getSensorID()); + if (clArr[1 + llr]) { + LOGP(error, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), curClu.getSensorID()); + } + clArr[1 + geom->getLayer(curClu.getSensorID())] = &curClu; + } + o2::track::TrackPar refLinInw0, refLinOut0, *refLinOut = nullptr, *refLinInw = nullptr; + o2::track::TrackPar refLinIBOut0, refLinOBInw0, *refLinOBInw = nullptr, *refLinIBOut = nullptr; + if (params.useStableRef) { + refLinOut = &(refLinOut0 = trFitOut); + refLinInw = &(refLinInw0 = trFitInw); + } + trFitOut.resetCovariance(); + trFitOut.setCov(trFitOut.getQ2Pt() * trFitOut.getQ2Pt() * trFitOut.getCov()[14], 14); + trFitInw.resetCovariance(); + trFitInw.setCov(trFitInw.getQ2Pt() * trFitInw.getQ2Pt() * trFitInw.getCov()[14], 14); + // fit in inward and outward direction + for (int i = 0; i <= 7; i++) { + int resOut, resInw; + // process resOut in ascending order (0-->7) and resInw in descending order (7-->0) + if (!(resOut = accountCluster(i, extrapOut, trFitOut, refLinOut)) || !(resInw = accountCluster(7 - i, extrapInw, trFitInw, refLinInw))) { + return false; + } + // at layer 3, find the IB track (trIBOut) and the OB track (trOBInw) + // propagate both trcaks to a common radius, RCompIBOB (12cm), and rotates + // them to the same reference frame for comparison + if (i == 3 && resOut == 1 && resInw == 1 && params.doIBOB && nCl == 7) { + resTrack.trIBOut = trFitOut; // outward track updated at outermost IB layer + resTrack.trOBInw = trFitInw; // inward track updated at innermost OB layer + o2::track::TrackPar refLinIBOut0, refLinIBIn0; + if (refLinOut) { + refLinIBOut = &(refLinIBOut0 = refLinOut0); + refLinOBInw = &(refLinOBInw0 = refLinInw0); + } + float xRref; + if (!resTrack.trOBInw.getXatLabR(params.rCompIBOB, xRref, bz) || + !prop->propagateTo(resTrack.trOBInw, refLinOBInw, xRref, true) || + !rotateTrack(resTrack.trOBInw, resTrack.trOBInw.getPhiPos(), refLinOBInw) || // propagate OB track to ref R and rotate + !rotateTrack(resTrack.trIBOut, resTrack.trOBInw.getAlpha(), refLinIBOut) || + !prop->propagateTo(resTrack.trIBOut, refLinIBOut, resTrack.trOBInw.getX(), true)) { // rotate OB track to same frame and propagate to same X + // if any propagation or rotation steps fail, invalidate both tracks + return false; + } + } + } + + bool innerDone = false; + if (params.doResid) { + for (int i = 0; i <= 7; i++) { + if (clArr[i]) { + // calculate interpolation as a weighted mean of inward/outward extrapolations to this layer + const auto &tInw = extrapInw[i], &tOut = extrapOut[i]; + auto wInw = inv2d(tInw.getSigmaY2(), tInw.getSigmaZ2(), tInw.getSigmaZY()); + auto wOut = inv2d(tOut.getSigmaY2(), tOut.getSigmaZ2(), tOut.getSigmaZY()); + if (wInw[0] == 0.f || wOut[0] == 0.f) { + return -1; + } + std::array wTot = {wInw[0] + wOut[0], wInw[1] + wOut[1], wInw[2] + wOut[2]}; + auto cTot = inv2d(wTot[0], wTot[1], wTot[2]); + auto ywi = wInw[0] * tInw.getY() + wInw[2] * tInw.getZ() + wOut[0] * tOut.getY() + wOut[2] * tOut.getZ(); + auto zwi = wInw[2] * tInw.getY() + wInw[1] * tInw.getZ() + wOut[2] * tOut.getY() + wOut[1] * tOut.getZ(); + auto yw = ywi * cTot[0] + zwi * cTot[2]; + auto zw = ywi * cTot[2] + zwi * cTot[1]; + // posCl.push_back(clArr[i]->getXYZGlo(*o2::its::GeometryTGeo::Instance())); + auto phi = i == 0 ? tInw.getPhi() : tInw.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + resTrack.points.emplace_back(clArr[i]->getY() - yw, clArr[i]->getZ() - zw, cTot[0] + clArr[i]->getSigmaY2(), cTot[1] + clArr[i]->getSigmaZ2(), phi, clArr[i]->getZ(), clArr[i]->getSensorID(), i - 1); + if (!innerDone) { + resTrack.track = tInw; + innerDone = true; + } + } else { + LOGP(debug, "No cluster on lr {}", i); + } + } + } + return true; +} + +bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) +{ + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + std::vector tracks; + std::vector useTrack; + std::vector gidsITS; + int ntr = pv.getNContributors(), ntrIni = ntr; + tracks.reserve(ntr); + useTrack.reserve(ntr); + gidsITS.reserve(ntr); + const auto& vtref = mRecoData->getPrimaryVertexMatchedTrackRefs()[vid]; + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); + int itr = vtref.getFirstEntry(), itLim = itr + vtref.getEntries(); + for (; itr < itLim; itr++) { + auto vid = trackIndex[itr]; + if (vid.isPVContributor()) { + tracks.emplace_back().setPID(mRecoData->getTrackParam(vid).getPID()); + gidsITS.push_back(mRecoData->getITSContributorGID(vid)); + } + } + ntr = tracks.size(); + useTrack.resize(ntr); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int itr = 0; itr < ntr; itr++) { + if (!(useTrack[itr] = refitITStrack(tracks[itr], gidsITS[itr]))) { + tracks[itr] = mRecoData->getTrackParam(gidsITS[itr]); // this track will not be used but participates in prepareVertexRefit + } + } + ntr = 0; + for (auto v : useTrack) { + ntr++; + } + if (ntr < params.minPVContributors || !mVertexer.prepareVertexRefit(tracks, pv)) { + LOGP(warn, "Abandon vertex refit: NcontribNew = {} vs NcontribOld = {}", ntr, ntrIni); + return false; + } + LOGP(debug, "Original vtx: Nc:{} {}, chi2={}", pv.getNContributors(), pv.asString(), pv.getChi2()); + auto pvSave = pv; + pv = mVertexer.refitVertexFull(useTrack, pv); + LOGP(debug, "Refitted vtx: Nc:{} {}, chi2={}", ntr, pv.asString(), pv.getChi2()); + if (pv.getChi2() < 0.f) { + LOGP(warn, "Failed to refit PV {}", pvSave.asString()); + return false; + } + return true; +} + +bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) +{ + // destination tack might have non-default PID assigned + const auto& trkITS = mRecoData->getITSTrack(gid); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto& params = CheckResidConfig::Instance(); + auto pid = track.getPID(); + track = trkITS.getParamOut(); + track.setPID(pid); + auto nCl = trkITS.getNumberOfClusters(); + auto geom = o2::its::GeometryTGeo::Instance(); + auto prop = o2::base::Propagator::Instance(); + float bz = prop->getNominalBz(); + o2::track::TrackPar refLin{track}; + + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[itsClRefs[trkITS.getClusterEntry(iCl)]]; + auto alpha = geom->getSensorRefAlpha(cls.getSensorID()); + if (!(params.useStableRef ? track.rotate(alpha, refLin, bz) : track.rotate(alpha)) || + !prop->propagateTo(track, params.useStableRef ? &refLin : nullptr, cls.getX(), true)) { + LOGP(debug, "refitITStrack failed on propagation to cl#{}, alpha={}, x={} | {}", iCl, alpha, cls.getX(), track.asString()); + return false; + } + if (!track.update(cls)) { + LOGP(debug, "refitITStrack failed on update with cl#{}, | {}", iCl, track.asString()); + return false; + } + } + return true; +} + +void CheckResidSpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void CheckResidSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + /* + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } + */ + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + mMeanVertexUpdated = true; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getCheckResidSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + Options opts{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + }; + // o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + // o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + + return DataProcessorSpec{ + "check-resid", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC /*, sclOpts*/)}, + opts}; +} + +} // namespace o2::checkresid diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx new file mode 100644 index 0000000000000..a754d1196017f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResidConfig.h" + +O2ParamImpl(o2::checkresid::CheckResidConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx index ac459d7f7ec16..d02f1df3903ec 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx @@ -48,7 +48,7 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class DumpTracksSpec : public Task +class DumpTracksSpec final : public Task { public: DumpTracksSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) @@ -163,8 +163,8 @@ void DumpTracksSpec::process(o2::globaltracking::RecoContainer& recoData) auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs - float itsBias = 0.5 * mITSROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; // ITS time is supplied in \mus as beginning of ROF - float mftBias = 0.5 * mMFTROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; // MFT time is supplied in \mus as beginning of ROF + float itsBias = 0.5 * mITSROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS time is supplied in \mus as beginning of ROF + float mftBias = 0.5 * mMFTROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // MFT time is supplied in \mus as beginning of ROF GTrackID::Source prevSrc = GTrackID::Source(-1); auto creator = [this, &selBCTF, itsBias, mftBias, &recoData, &prevSrc](auto& _tr, GTrackID _origID, float t0, float terr) { @@ -297,7 +297,7 @@ DataProcessorSpec getDumpTracksSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true false, // GRPLHCIF diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index c85dd2d378ccb..416820fc9aebb 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -15,4 +15,38 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::dataformats::ProngInfoExt + ; +#pragma link C++ class o2::dataformats::V0Ext + ; +#pragma link C++ class o2::dataformats::TrackInfoExt + ; +#pragma link C++ class std::vector < o2::dataformats::TrackInfoExt> + ; +#pragma link C++ class std::vector < o2::dataformats::ProngInfoExt> + ; +#pragma link C++ class std::vector < o2::dataformats::V0Ext> + ; +#pragma link C++ class o2::trackstudy::TrackMCStudyConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trackstudy::TrackMCStudyConfig> + ; +#pragma link C++ class o2::trackstudy::RecTrack + ; +#pragma link C++ class std::vector < o2::trackstudy::RecTrack> + ; +#pragma link C++ class o2::trackstudy::TrackFamily + ; +#pragma link C++ class std::vector < o2::trackstudy::TrackFamily> + ; +#pragma link C++ class o2::trackstudy::MCTrackInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::MCTrackInfo> + ; +#pragma link C++ class o2::trackstudy::RecPV + ; +#pragma link C++ class std::vector < o2::trackstudy::RecPV> + ; +#pragma link C++ class o2::trackstudy::MCVertex + ; +#pragma link C++ class std::vector < o2::trackstudy::MCVertex> + ; +#pragma link C++ class o2::trackstudy::ClResTPC + ; +#pragma link C++ class o2::trackstudy::ClResTPCCont + ; +#pragma link C++ class std::vector < o2::trackstudy::ClResTPCCont> + ; +#pragma link C++ class o2::trackstudy::TrackPairInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::TrackPairInfo> + ; +#pragma ling C++ class o2::tpc::TPCClusSelector + ; + +#pragma link C++ class o2::trackstudy::ITSHitInfo + ; +#pragma link C++ class std::vector < o2::trackstudy::ITSHitInfo> + ; + +#pragma link C++ class o2::checkresid::Point + ; +#pragma link C++ class std::vector < o2::checkresid::Point> + ; +#pragma link C++ class o2::checkresid::Track + ; +#pragma link C++ class o2::checkresid::CheckResidConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::checkresid::CheckResidConfig> + ; + #endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx index ae225eaade590..9456c7740c977 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/ITSOffsStudy.cxx @@ -119,6 +119,7 @@ void ITSOffsStudy::process(o2::globaltracking::RecoContainer& recoData) } ambEntry = 1; } + auto tofMatch = recoData.getTOFMatch(vid); auto refs = recoData.getSingleDetectorRefs(vid); if (!refs[GTrackID::ITS].isIndexSet()) { // might be an afterburner track continue; @@ -133,6 +134,9 @@ void ITSOffsStudy::process(o2::globaltracking::RecoContainer& recoData) (*mDBGOut) << "itstof" << "gid=" << vid << "ttof=" << timeTOFMUS << "tits=" << tsROF << "itsROFID=" << itsTr2ROFID[itsTrackID] << "\n"; mDTHisto->Fill(timeTOFMUS - tsROF); + const auto& trc = recoData.getTrackParam(tofMatch.getTrackRef()); + (*mDBGOut) << "dttof" + << "refgid=" << tofMatch.getTrackRef() << "dtime=" << tofMatch.getDeltaT() << "phi=" << trc.getPhi() << "tgl=" << trc.getTgl() << "q2t=" << trc.getQ2Pt() << "\n"; } } } @@ -158,7 +162,7 @@ DataProcessorSpec getITSOffsStudy(GTrackID::mask_t srcTracks, GTrackID::mask_t s bool useMC = false; dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); return DataProcessorSpec{ "its-offset-study", diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx new file mode 100644 index 0000000000000..0129d19b02346 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -0,0 +1,450 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/MCTrack.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsFT0/RecPoints.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TrackingStudy.h" +#include "TPCBase/ParameterElectronics.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/DCA.h" +#include "Steer/MCKinematicsReader.h" +#include "DCAFitter/DCAFitterN.h" +#include "MathUtils/fit.h" +#include "GlobalTrackingStudy/V0Ext.h" +#include "GPUO2InterfaceConfiguration.h" +// #include "GPUSettingsO2.h" +#include "GPUParam.h" +#include "GPUParam.inc" +#include "GPUTPCGeometry.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUO2InterfaceUtils.h" + +namespace o2::svstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; +using V0ID = o2::dataformats::V0Index; + +using timeEst = o2::dataformats::TimeStampWithError; + +class SVStudySpec final : public Task +{ + public: + SVStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useTPCCl, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseTPCCl(useTPCCl), mUseMC(useMC) {} + ~SVStudySpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(o2::globaltracking::RecoContainer& recoData); + o2::dataformats::V0Ext processV0(int iv, o2::globaltracking::RecoContainer& recoData); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool refitV0(const V0ID& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData); + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; ///< MC flag + std::unique_ptr mDBGOut; + float mSelK0 = -1; + bool mRefit = false; + bool mUseTPCCl = false; + float mMaxEta = 0.8; + float mBz = 0; + int mNHBPerTF = 0; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv; + float mTPCTBinMUSInv = 0.f; + GTrackID::mask_t mTracksSrc{}; + o2::vertexing::DCAFitterN<2> mFitterV0; + std::vector mTBinClOccAft, mTBinClOccBef; + std::unique_ptr mcReader; // reader of MC information + std::shared_ptr mParam = nullptr; +}; + +void SVStudySpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mDBGOut = std::make_unique("svStudy.root", "recreate"); + mRefit = ic.options().get("refit"); + mSelK0 = ic.options().get("sel-k0"); + mMaxEta = ic.options().get("max-eta"); + if (mUseMC) { + mcReader = std::make_unique("collisioncontext.root"); + } +} + +void SVStudySpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + + size_t occupancyMapSizeBytes = o2::gpu::GPUO2InterfaceRefit::fillOccupancyMapGetSize(mNHBPerTF, mParam.get()); + gsl::span TPCRefitterOccMap = recoData.occupancyMapTPC; + o2::gpu::GPUO2InterfaceUtils::paramUseExternalOccupancyMap(mParam.get(), mNHBPerTF, TPCRefitterOccMap.data(), occupancyMapSizeBytes); + + mTBinClOccBef.resize(1); + mTBinClOccAft.resize(1); + if (recoData.inputsTPCclusters && mUseTPCCl) { + mNTPCOccBinLength = mParam->rec.tpc.occupancyMapTimeBins; + mTBinClOccBef.clear(); + mTBinClOccAft.clear(); + // prepare TPC occupancy data + if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOccAft.resize(nTPCOccBins); + mTBinClOccBef.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + std::vector mltHistTB(nTPCOccBins); + for (int i = 0; i < nTPCOccBins; i++) { + mltHistTB[i] = mParam->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mltHistTB[i + sumBins]; + } + mTBinClOccAft[i] = sm; + } + sm = 0; + for (int i = 0; i < nTPCOccBins; i++) { + sm += mltHistTB[i]; + if (i - sumBins > 0) { + sm -= mltHistTB[i - sumBins]; + } + mTBinClOccBef[i] = sm; + } + } + } + + process(recoData); +} + +void SVStudySpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer + mFitterV0.setBz(mBz); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setPropagateToPCA(false); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + if (!mParam) { + // for occupancy estimator + mParam = o2::gpu::GPUO2InterfaceUtils::getFullParamShared(0.f, mNHBPerTF); + } + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUSInv = 1. / elParam.ZbinWidth; // 1./TPC bin in microseconds + } + mBz = o2::base::Propagator::Instance()->getNominalBz(); + mFitterV0.setBz(mBz); +} + +o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoContainer& recoData) +{ + o2::dataformats::V0Ext v0ext; + auto invalidate = [&v0ext]() { v0ext.v0ID.setVertexID(-1); }; + + auto v0s = recoData.getV0s(); + auto v0IDs = recoData.getV0sIdx(); + static int tfID = 0; + + const auto& v0id = v0IDs[iv]; + if (mRefit && !refitV0(v0id, v0ext.v0, recoData)) { + invalidate(); + return v0ext; + } + const auto& v0sel = mRefit ? v0ext.v0 : v0s[iv]; + if (mMaxEta < std::abs(v0sel.getEta())) { + invalidate(); + return v0ext; + } + if (mSelK0 > 0 && std::abs(std::sqrt(v0sel.calcMass2AsK0()) - 0.497) > mSelK0) { + invalidate(); + return v0ext; + } + if (!mRefit) { + v0ext.v0 = v0sel; + } + v0ext.v0ID = v0id; + const auto clRefs = recoData.getTPCTracksClusterRefs(); + o2::MCCompLabel lb[2]; + const o2::MCTrack* mcTrks[2]; + for (int ip = 0; ip < 2; ip++) { + auto& prInfo = v0ext.prInfo[ip]; + auto gid = v0ext.v0ID.getProngID(ip); + auto gidset = recoData.getSingleDetectorRefs(gid); + lb[ip] = recoData.getTrackMCLabel(gid); + if (lb[ip].isValid()) { + prInfo.corrGlo = !lb[ip].isFake(); + } + // get TPC tracks, if any + if (gidset[GTrackID::TPC].isSourceSet()) { + const auto& tpcTr = recoData.getTPCTrack(gidset[GTrackID::TPC]); + prInfo.trackTPC = tpcTr; + prInfo.nClTPC = tpcTr.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::TPC]); + if (lb[ip].isValid()) { + prInfo.corrTPC = !lb[ip].isFake(); + } + if (mParam && mUseTPCCl) { + uint8_t clSect = 0, clRow = 0; + uint32_t clIdx = 0; + tpcTr.getClusterReference(clRefs, tpcTr.getNClusterReferences() - 1, clSect, clRow, clIdx); + const auto& clus = recoData.getTPCClusters().clusters[clSect][clRow][clIdx]; + prInfo.lowestRow = clRow; + int npads = o2::gpu::GPUTPCGeometry::NPads(clRow); + prInfo.padFromEdge = uint8_t(clus.getPad()); + if (prInfo.padFromEdge > npads / 2) { + prInfo.padFromEdge = npads - 1 - prInfo.padFromEdge; + } + } + } + // get ITS tracks, if any + if (gid.includesDet(DetID::ITS)) { + auto gidITS = recoData.getITSContributorGID(gid); + if (gidset[GTrackID::ITS].isSourceSet()) { + const auto& itsTr = recoData.getITSTrack(gidset[GTrackID::ITS]); + prInfo.nClITS = itsTr.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITS]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); + } + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + prInfo.pattITS |= 0x1 << il; + } + } + } else { + const auto& itsTrf = recoData.getITSABRefs()[gidset[GTrackID::ITSAB]]; + prInfo.nClITS = itsTrf.getNClusters(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSAB]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); + } + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + prInfo.pattITS |= 0x1 << il; + } + } + prInfo.pattITS |= 0x1 << 31; // flag AB + } + if (gidset[GTrackID::ITSTPC].isSourceSet()) { + auto mtc = recoData.getTPCITSTrack(gidset[GTrackID::ITSTPC]); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); + prInfo.chi2ITSTPC = mtc.getChi2Match(); + if (lb[ip].isValid()) { + prInfo.corrITSTPC = !lb[ip].isFake(); + } + } + } + if (mUseMC && lb[ip].isValid()) { // temp store of mctrks + mcTrks[ip] = mcReader->getTrack(lb[ip]); + } + } + if (mUseMC && (mcTrks[0] != nullptr) && (mcTrks[1] != nullptr)) { + // check majority vote on mother particle otherwise leave pdg -1 + if (lb[0].getSourceID() == lb[1].getSourceID() && lb[0].getEventID() == lb[1].getEventID() && + mcTrks[0]->getMotherTrackId() == mcTrks[1]->getMotherTrackId() && mcTrks[0]->getMotherTrackId() >= 0) { + const auto mother = mcReader->getTrack(lb[0].getSourceID(), lb[0].getEventID(), mcTrks[0]->getMotherTrackId()); + v0ext.mcPID = mother->GetPdgCode(); + } + } + return v0ext; +} + +void SVStudySpec::process(o2::globaltracking::RecoContainer& recoData) +{ + auto v0IDs = recoData.getV0sIdx(); + auto nv0 = v0IDs.size(); + if (nv0 > recoData.getV0s().size()) { + mRefit = true; + } + std::map> pv2sv; + static int tfID = 0; + for (int iv = 0; iv < nv0; iv++) { + const auto v0id = v0IDs[iv]; + pv2sv[v0id.getVertexID()].push_back(iv); + } + std::vector v0extVec; + for (auto it : pv2sv) { + int pvID = it.first; + auto& vv = it.second; + if (pvID < 0 || vv.size() == 0) { + continue; + } + v0extVec.clear(); + for (int iv0 : vv) { + auto v0ext = processV0(iv0, recoData); + if (v0ext.v0ID.getVertexID() < 0) { + continue; + } + v0extVec.push_back(v0ext); + } + if (v0extVec.size()) { + const auto& pv = recoData.getPrimaryVertex(pvID); + float tpcOccBef = 0., tpcOccAft = 0.; + int tb = pv.getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv; + tpcOccBef = tb < 0 ? mTBinClOccBef[0] : (tb >= mTBinClOccBef.size() ? mTBinClOccBef.back() : mTBinClOccBef[tb]); + tpcOccAft = tb < 0 ? mTBinClOccAft[0] : (tb >= mTBinClOccAft.size() ? mTBinClOccAft.back() : mTBinClOccAft[tb]); + + (*mDBGOut) << "v0" + << "orbit=" << recoData.startIR.orbit << "tfID=" << tfID << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft + << "v0Ext=" << v0extVec + << "pv=" << pv + << "\n"; + } + } + tfID++; +} + +bool SVStudySpec::refitV0(const V0ID& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData) +{ + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == GTrackID::TPC) || (id.getProngID(1).getSource() == GTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +void SVStudySpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void SVStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getSVStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcCls, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcCls, false); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + auto ggRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + dataRequest->inputs, + true); + bool useTPCcl = srcCls[GTrackID::TPC]; + return DataProcessorSpec{ + "sv-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useTPCcl, useMC)}, + Options{ + {"refit", VariantType::Bool, false, {"refit SVertices"}}, + {"sel-k0", VariantType::Float, -1.f, {"If positive, select K0s with this mass margin"}}, + {"max-eta", VariantType::Float, 1.2f, {"Cut on track eta"}}, + }}; +} + +} // namespace o2::svstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx new file mode 100644 index 0000000000000..e5b28fb0fd62b --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCClusSelector.cxx @@ -0,0 +1,117 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// helper class for TPC clusters selection +#include "GlobalTrackingStudy/TPCClusSelector.h" +#include "DataFormatsTPC/ClusterNativeHelper.h" +#include "Framework/Logger.h" +#include +#ifdef WITH_OPENMP +#include +#endif + +using namespace o2::tpc; + +void TPCClusSelector::setNThreads(int n) +{ +#ifndef WITH_OPENMP + if (n > 1) { + LOGP(warn, "No OpenMP"); + } + n = 1; +#endif + mNThreads = n; +} + +std::pair TPCClusSelector::findClustersRange(int sec, int row, float tbmin, float tbmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct) +{ + // find sorted indices of clusters in the [tbmin:tbmax] range, if not found, return {-1,-2} + const auto& vidx = mSectors[sec].rows[row]; + const auto* clarr = tpcClusterIdxStruct.clusters[sec][row]; + // use binary search to find 1st cluster with time >= tb + int ncl = vidx.size(), left = 0, right = ncl; + while (left < right) { + int mid = left + (right - left) / 2; + if (clarr[vidx[mid]].getTime() < tbmin) { + left = mid + 1; + } else { + right = mid; + } + } + if (left == ncl || clarr[vidx[left]].getTime() > tbmax) { + return {-1, -2}; // all clusters have time < tbmin or no clusters in the range [tbmin:tbmax] + } + int idmin = left, idmax = left, idtst = idmin; + // look at smaller times + while (++idtst < ncl && clarr[vidx[idtst]].getTime() <= tbmax) { + idmax = idtst; + } + return {idmin, idmax}; +} + +int TPCClusSelector::findClustersEntries(int sec, int row, float tbmin, float tbmax, float padmin, float padmax, const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct, std::vector* clIDDirect) +{ + // find direct cluster indices for tbmin:tbmas / padmin/padmax range, fill clIDDirect vector if provided + const auto& vidx = mSectors[sec].rows[row]; + const auto* clarr = tpcClusterIdxStruct.clusters[sec][row]; + // use binary search to find 1st cluster with time >= tb + int ncl = vidx.size(), left = 0, right = ncl; + if (clIDDirect) { + clIDDirect->clear(); + } + while (left < right) { + int mid = left + (right - left) / 2; + if (clarr[vidx[mid]].getTime() < tbmin) { + left = mid + 1; + } else { + right = mid; + } + } + if (left == ncl || clarr[vidx[left]].getTime() > tbmax) { + return 0; // all clusters have time < tbmin or no clusters in the range [tbmin:tbmax] + } + int nclf = 0; + while (left < ncl) { + const auto& cl = clarr[vidx[left]]; + if (cl.getTime() > tbmax) { + break; + } + if (cl.getPad() >= padmin && cl.getPad() <= padmax) { + nclf++; + if (clIDDirect) { + clIDDirect->push_back(vidx[left]); + } + } + } + return nclf; +} + +void TPCClusSelector::fill(const o2::tpc::ClusterNativeAccess& tpcClusterIdxStruct) +{ + for (int is = 0; is < NSectors; is++) { + auto& sect = mSectors[is]; +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int ir = 0; ir < Sector::NRows; ir++) { + size_t ncl = tpcClusterIdxStruct.nClusters[is][ir]; + if (ncl >= 0xffff) { + LOGP(error, "Row {} of sector {} has {} clusters, truncating to {}", ir, is, ncl, int(0xffff)); + ncl = 0xffff; + } + auto& rowidx = sect.rows[ir]; + rowidx.resize(ncl); + std::iota(rowidx.begin(), rowidx.end(), 0); + const auto* clus = tpcClusterIdxStruct.clusters[is][ir]; // C array of clusters + std::sort(rowidx.begin(), rowidx.end(), [&](size_t a, size_t b) { return clus[a].getTime() < clus[b].getTime(); }); + } + } +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx index ed06e014184b6..7a7f9056b89af 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCDataFilter.cxx @@ -107,10 +107,10 @@ void TPCDataFilter::run(ProcessingContext& pc) void TPCDataFilter::sendOutput(ProcessingContext& pc) { - pc.outputs().snapshot(Output{"TPC", "TRACKSF", 0, Lifetime::Timeframe}, mTracksFiltered); - pc.outputs().snapshot(Output{"TPC", "CLUSREFSF", 0, Lifetime::Timeframe}, mTrackClusIdxFiltered); + pc.outputs().snapshot(Output{"TPC", "TRACKSF", 0}, mTracksFiltered); + pc.outputs().snapshot(Output{"TPC", "CLUSREFSF", 0}, mTrackClusIdxFiltered); if (mUseMC) { - pc.outputs().snapshot(Output{"TPC", "TRACKSMCLBLF", 0, Lifetime::Timeframe}, mTPCTrkLabelsFiltered); + pc.outputs().snapshot(Output{"TPC", "TRACKSMCLBLF", 0}, mTPCTrkLabelsFiltered); } o2::tpc::TPCSectorHeader clusterOutputSectorHeader{0}; @@ -118,7 +118,7 @@ void TPCDataFilter::sendOutput(ProcessingContext& pc) for (int i = 0; i < o2::tpc::constants::MAXSECTOR; i++) { clusterOutputSectorHeader.sectorBits = (1ul << i); o2::header::DataHeader::SubSpecificationType subspec = i; - char* buffer = pc.outputs().make({o2::header::gDataOriginTPC, "CLUSTERNATIVEF", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, + char* buffer = pc.outputs().make({o2::header::gDataOriginTPC, "CLUSTERNATIVEF", subspec, {clusterOutputSectorHeader}}, mClusFiltered.nClustersSector[i] * sizeof(*mClusFiltered.clustersLinear) + sizeof(o2::tpc::ClusterCountIndex)) .data(); o2::tpc::ClusterCountIndex* outIndex = reinterpret_cast(buffer); @@ -138,7 +138,7 @@ void TPCDataFilter::sendOutput(ProcessingContext& pc) } o2::dataformats::ConstMCLabelContainer contflat; cont.flatten_to(contflat); - pc.outputs().snapshot({o2::header::gDataOriginTPC, "CLNATIVEMCLBLF", subspec, Lifetime::Timeframe, {clusterOutputSectorHeader}}, contflat); + pc.outputs().snapshot({o2::header::gDataOriginTPC, "CLNATIVEMCLBLF", subspec, {clusterOutputSectorHeader}}, contflat); } } } @@ -297,7 +297,7 @@ DataProcessorSpec getTPCDataFilter(GTrackID::mask_t srcTracks, GTrackID::mask_t dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); return DataProcessorSpec{ "tpc-data-filter", diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 8cac4d059fdaa..05e6a122adec9 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -23,6 +23,7 @@ #include "CommonUtils/NameConf.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/ControlService.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TPCTrackStudy.h" @@ -46,12 +47,15 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TPCTrackStudySpec : public Task +class TPCTrackStudySpec final : public Task { public: - TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TPCTrackStudySpec() final = default; void init(InitContext& ic) final; @@ -67,8 +71,16 @@ class TPCTrackStudySpec : public Task o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; bool mUseMC{false}; ///< MC flag - float mRRef = 0.; + bool mUseGPUModel{false}; + float mXRef = 0.; + int mNMoves = 6; + int mTFStart = 0; + int mTFEnd = 999999999; + int mTFCount = -1; + bool mUseR = false; + bool mRepRef = false; std::unique_ptr mDBGOut; + std::unique_ptr mDBGOutCl; float mITSROFrameLengthMUS = 0.; GTrackID::mask_t mTracksSrc{}; o2::steer::MCKinematicsReader mcReader; // reader of MC information @@ -77,6 +89,7 @@ class TPCTrackStudySpec : public Task gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span gsl::span mTPCTracksArray; ///< input TPC tracks span gsl::span mTPCRefitterShMap; ///< externally set TPC clusters sharing map + gsl::span mTPCRefitterOccMap; ///< externally set TPC clusters occupancy map const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices gsl::span mTPCTrkLabels; ///< input TPC Track MC labels std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction @@ -85,26 +98,48 @@ class TPCTrackStudySpec : public Task void TPCTrackStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mRRef = ic.options().get("target-radius"); - if (mRRef < 0.) { - mRRef = 0.; + mXRef = ic.options().get("target-x"); + mNMoves = std::max(2, ic.options().get("n-moves")); + mUseR = ic.options().get("use-r-as-x"); + mRepRef = ic.options().get("repeat-ini-ref"); + mUseGPUModel = ic.options().get("use-gpu-fitter"); + mTFStart = ic.options().get("tf-start"); + mTFEnd = ic.options().get("tf-end"); + if (mXRef < 0.) { + mXRef = 0.; } + mTPCCorrMapsLoader.init(ic); mDBGOut = std::make_unique("tpc-trackStudy.root", "recreate"); + if (ic.options().get("dump-clusters")) { + mDBGOutCl = std::make_unique("tpc-trackStudy-cl.root", "recreate"); + } } void TPCTrackStudySpec::run(ProcessingContext& pc) { + mTFCount++; + if (mTFCount < mTFStart || mTFCount > mTFEnd) { + LOGP(info, "Skipping TF {}", mTFCount); + return; + } + o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions process(recoData); + + if (mTFCount > mTFEnd) { + LOGP(info, "Stopping processing after TF {}", mTFCount); + pc.services().get().endOfStream(); + return; + } } void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); mTPCVDriftHelper.extractCCDBInputs(pc); - o2::tpc::CorrectionMapsLoader::extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -132,79 +167,156 @@ void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) { static long counter = -1; - counter++; auto prop = o2::base::Propagator::Instance(); + mTPCTracksArray = recoData.getTPCTracks(); + mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs(); + mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex; + mTPCRefitterShMap = recoData.clusterShMapTPC; + mTPCRefitterOccMap = recoData.occupancyMapTPC; + + std::vector intRecs; if (mUseMC) { // extract MC tracks const o2::steer::DigitizationContext* digCont = nullptr; if (!mcReader.initFromDigitContext("collisioncontext.root")) { throw std::invalid_argument("initialization of MCKinematicsReader failed"); } digCont = mcReader.getDigitizationContext(); - const auto& intRecs = digCont->getEventRecords(); - - mTPCTracksArray = recoData.getTPCTracks(); - mTPCTrackClusIdx = recoData.getTPCTracksClusterRefs(); - mTPCClusterIdxStruct = &recoData.inputsTPCclusters->clusterIndex; - mTPCRefitterShMap = recoData.clusterShMapTPC; + intRecs = digCont->getEventRecords(); mTPCTrkLabels = recoData.getTPCTracksMCLabels(); + } + if (mTPCTracksArray.size()) { + LOGP(info, "Found {} TPC tracks", mTPCTracksArray.size()); + mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); + mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 + } + float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin + float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); + std::vector clSector, clRow; + std::vector clX, clY, clZ; - mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), mTPCRefitterShMap.data(), nullptr, o2::base::Propagator::Instance()); + auto dumpClusters = [this] { + static int tf = 0; + const auto* corrMap = this->mTPCCorrMapsLoader.getCorrMap(); + for (int sector = 0; sector < 36; sector++) { + float alp = ((sector % 18) * 20 + 10) * TMath::DegToRad(); + float sn = TMath::Sin(alp), cs = TMath::Cos(alp); + for (int row = 0; row < 152; row++) { + for (int ic = 0; ic < this->mTPCClusterIdxStruct->nClusters[sector][row]; ic++) { + const auto cl = this->mTPCClusterIdxStruct->clusters[sector][row][ic]; + float x, y, z, xG, yG; + corrMap->TransformIdeal(sector, row, cl.getPad(), cl.getTime(), x, y, z, 0); + o2::math_utils::detail::rotateZ(x, y, xG, yG, sn, cs); + LOGP(debug, "tf:{} s:{} r:{} p:{} t:{} qm:{} qt:{} f:{} x:{} y:{} z:{}", tf, sector, row, cl.getPad(), cl.getTime(), cl.getQmax(), cl.getQtot(), cl.getFlags(), x, y, z); + (*mDBGOutCl) << "tpccl" + << "tf=" << tf << "sect=" << sector << "row=" << row << "pad=" << cl.getPad() << "time=" << cl.getTime() << "qmax=" << cl.getQmax() << "qtot=" << cl.getQtot() + << "sigT=" << cl.getSigmaTime() << "sigP=" << cl.getSigmaPad() + << "flags=" << cl.getFlags() + << "x=" << x << "y=" << y << "z=" << z << "xg=" << xG << "yg=" << yG + << "\n"; + } + } + } + tf++; + }; - float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin - float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); - float RRef2 = mRRef * mRRef; - const o2::MCTrack* mcTrack = nullptr; - std::vector clSector, clRow; - std::vector clIniX, clIniY, clIniZ, clMovX, clMovY, clMovZ; - for (size_t itr = 0; itr < mTPCTracksArray.size(); itr++) { - auto tr = mTPCTracksArray[itr]; // create track copy + if (mDBGOutCl) { + dumpClusters(); + } - // create refitted copy - auto trf = tr.getOuterParam(); // we refit inward + for (size_t itr = 0; itr < mTPCTracksArray.size(); itr++) { + auto tr = mTPCTracksArray[itr]; // create track copy + int side = 0; + if (tr.hasBothSidesClusters()) { + continue; + } + side = tr.hasASideClustersOnly() ? 1 : -1; + //========================================================================= + // create refitted copy + auto trackRefit = [itr, this](o2::track::TrackParCov& trc, float t) -> bool { float chi2Out = 0; - // impose MC time in TPC timebin and refit inward after resetted covariance - int retVal = mTPCRefitter->RefitTrackAsTrackParCov(trf, mTPCTracksArray[itr].getClusterRef(), tr.getTime0(), &chi2Out, false, true); + int retVal = mUseGPUModel ? this->mTPCRefitter->RefitTrackAsGPU(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true) : this->mTPCRefitter->RefitTrackAsTrackParCov(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true); if (retVal < 0) { - LOGP(warn, "Refit failed ({}) with originaltime0: {} : track#{}[{}]", retVal, tr.getTime0(), counter, ((const o2::track::TrackPar&)tr.getOuterParam()).asString()); - continue; + LOGP(warn, "Refit failed ({}) with time={}: track#{}[{}]", retVal, t, counter, trc.asString()); + return false; } - // propagate original track - if (!tr.rotate(o2::math_utils::sector2Angle(o2::math_utils::angle2Sector(tr.getPhiPos())))) { - continue; + return true; + }; + + auto trackProp = [&tr, itr, prop, this](o2::track::TrackParCov& trc) -> bool { + if (!trc.rotate(tr.getAlpha())) { + LOGP(warn, "Rotation to original track alpha {} failed, track#{}[{}]", tr.getAlpha(), counter, trc.asString()); + return false; } - float curR2 = tr.getX() * tr.getX() + tr.getY() * tr.getY(); - if (curR2 > RRef2) { // try to propagate as close as possible to target radius - float xtgt = 0; - if (!tr.getXatLabR(mRRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { - xtgt = 0; - } - prop->PropagateToXBxByBz(tr, xtgt); // propagation will not necessarilly converge, but we don't care - if (!tr.rotate(o2::math_utils::sector2Angle(o2::math_utils::angle2Sector(tr.getPhiPos())))) { - continue; - } + float xtgt = this->mXRef; + if (mUseR && !trc.getXatLabR(this->mXRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { + xtgt = 0; + return false; } - // propagate original/refitted track - if (!trf.rotate(tr.getAlpha())) { - continue; + if (!prop->PropagateToXBxByBz(trc, xtgt)) { + LOGP(warn, "Propagation to X={} failed, track#{}[{}]", xtgt, counter, trc.asString()); + return false; } - curR2 = trf.getX() * trf.getX() + trf.getY() * trf.getY(); - if (curR2 > RRef2) { // try to propagate as close as possible to target radius - float xtgt = 0; - if (!trf.getXatLabR(mRRef, xtgt, prop->getNominalBz(), o2::track::DirInward)) { - xtgt = 0; - } - prop->PropagateToXBxByBz(trf, xtgt); // propagation will not necessarilly converge, but we don't care - // propagate to the same alpha/X as the original track - if (!trf.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trf, tr.getX())) { - continue; - } + return true; + }; + + auto prepClus = [this, &tr, &clSector, &clRow, &clX, &clY, &clZ](float t) { // extract cluster info + clSector.clear(); + clRow.clear(); + clX.clear(); + clY.clear(); + clZ.clear(); + int count = tr.getNClusters(); + const o2::tpc::ClusterNative* cl = nullptr; + for (int ic = count; ic--;) { + uint8_t sector, row; + cl = &tr.getCluster(this->mTPCTrackClusIdx, ic, *this->mTPCClusterIdxStruct, sector, row); + clSector.push_back(sector); + clRow.push_back(row); + float x, y, z; + mTPCCorrMapsLoader.Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, t); // nominal time of the track + clX.push_back(x); + clY.push_back(y); + clZ.push_back(z); } + }; + + //========================================================================= + + auto trf = tr.getOuterParam(); // we refit inward original track + if (!trackRefit(trf, tr.getTime0()) || !trackProp(trf)) { + continue; + } + + // propagate original track + if (!trackProp(tr)) { + continue; + } + prepClus(tr.getTime0()); // original clusters + counter++; + // store results + (*mDBGOut) << "tpcIni" + << "counter=" << counter + << "iniTrack=" << tr + << "iniTrackRef=" << trf + << "side=" << side + << "time=" << tr.getTime0() + << "clSector=" << clSector + << "clRow=" << clRow + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ + << "\n"; + + float dz = 0; + + while (mUseMC) { // impose MC time in TPC timebin and refit inward after resetted covariance // extract MC truth + const o2::MCTrack* mcTrack = nullptr; auto lbl = mTPCTrkLabels[itr]; if (!lbl.isValid() || !(mcTrack = mcReader.getTrack(lbl))) { - continue; + break; } long bc = intRecs[lbl.getEventID()].toLong(); // bunch crossing of the interaction float bcTB = bc / 8. + tpcTBBias; // the same in TPC timebins, accounting for the TPC time bias @@ -213,79 +325,78 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) pxyz{(float)mcTrack->GetStartVertexMomentumX(), (float)mcTrack->GetStartVertexMomentumY(), (float)mcTrack->GetStartVertexMomentumZ()}; TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrack->GetPdgCode()); if (!pPDG) { - continue; + break; } o2::track::TrackPar mctrO2(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); // // propagate it to the alpha/X of the reconstructed track if (!mctrO2.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(mctrO2, tr.getX())) { - continue; + break; } - // // now create a properly refitted track with correct time and distortions correction - auto trackRefit = tr.getOuterParam(); // we refit inward - chi2Out = 0; - // impose MC time in TPC timebin and refit inward after resetted covariance - retVal = mTPCRefitter->RefitTrackAsTrackParCov(trackRefit, mTPCTracksArray[itr].getClusterRef(), bcTB, &chi2Out, false, true); - if (retVal < 0) { - LOGP(warn, "Refit failed for #{} ({}), imposed time0: {} conventional time0: {} [{}]", counter, retVal, bcTB, tr.getTime0(), ((o2::track::TrackPar&)trackRefit).asString()); - continue; + { + auto trfm = tr.getOuterParam(); // we refit inward + // impose MC time in TPC timebin and refit inward after resetted covariance + if (!trackRefit(trfm, bcTB) || !trfm.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trfm, tr.getX())) { + LOGP(warn, "Failed to propagate MC-time refitted track#{} [{}] to X/alpha of original track [{}]", counter, trfm.asString(), tr.asString()); + break; + } + // estimate Z shift in case of no-distortions + dz = (tr.getTime0() - bcTB) * vdriftTB; + if (tr.hasCSideClustersOnly()) { + dz = -dz; + } + // + prepClus(bcTB); // clusters for MC time + (*mDBGOut) << "tpcMC" + << "counter=" << counter + << "movTrackRef=" << trfm + << "mcTrack=" << mctrO2 + << "side=" << side + << "imposedTB=" << bcTB + << "dz=" << dz + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ + << "\n"; } - // propagate the refitted track to the same X/alpha as original track - if (!trackRefit.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trackRefit, tr.getX())) { - LOGP(warn, "Failed to propagate refitted track#{} [{}] to X/alpha of original track [{}]", counter, trackRefit.asString(), tr.asString()); + break; + } + // refit and store the same track for a few compatible times + float tmin = tr.getTime0() - tr.getDeltaTBwd(); + float tmax = tr.getTime0() + tr.getDeltaTFwd(); + for (int it = 0; it < mNMoves; it++) { + float tb = tmin + it * (tmax - tmin) / (mNMoves - 1); + auto trfm = tr.getOuterParam(); // we refit inward + // impose time in TPC timebin and refit inward after resetted covariance + if (!trackRefit(trfm, tb) || !trfm.rotate(tr.getAlpha()) || !prop->PropagateToXBxByBz(trfm, tr.getX())) { + LOGP(warn, "Failed to propagate time={} refitted track#{} [{}] to X/alpha of original track [{}]", tb, counter, trfm.asString(), tr.asString()); continue; } // estimate Z shift in case of no-distortions - float dz = (tr.getTime0() - bcTB) * vdriftTB; + dz = (tr.getTime0() - tb) * vdriftTB; if (tr.hasCSideClustersOnly()) { dz = -dz; - } else if (tr.hasBothSidesClusters()) { - dz = 0; // CE crossing tracks should not be shifted } - // extract cluster info - clSector.clear(); - clRow.clear(); - clIniX.clear(); - clIniY.clear(); - clIniZ.clear(); - clMovX.clear(); - clMovY.clear(); - clMovZ.clear(); - int count = tr.getNClusters(); - const auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); - const o2::tpc::ClusterNative* cl = nullptr; - for (int ic = count; ic--;) { - uint8_t sector, row; - cl = &tr.getCluster(mTPCTrackClusIdx, ic, *mTPCClusterIdxStruct, sector, row); - clSector.push_back(sector); - clRow.push_back(row); - float x, y, z; - corrMap->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, tr.getTime0()); // nominal time of the track - clIniX.push_back(x); - clIniY.push_back(y); - clIniZ.push_back(z); - corrMap->Transform(sector, row, cl->getPad(), cl->getTime(), x, y, z, bcTB); // shifted time of the track - clMovX.push_back(x); - clMovY.push_back(y); - clMovZ.push_back(z); + // + int mnm = mNMoves - 1; + prepClus(tb); // clusters for MC time + (*mDBGOut) << "tpcMov" + << "counter=" << counter + << "copy=" << it + << "maxCopy=" << mnm + << "movTrackRef=" << trfm; + if (mRepRef) { + (*mDBGOut) << "tpcMov" + << "iniTrackRef=" << trf << "time=" << tr.getTime0(); } - - // store results - (*mDBGOut) << "tpc" - << "iniTrack=" << tr - << "iniTrackRef=" << trf - << "movTrackRef=" << trackRefit - << "mcTrack=" << mctrO2 - << "imposedTB=" << bcTB << "dz=" << dz - << "clSector=" << clSector - << "clRow=" << clRow - << "clIniX=" << clIniX - << "clIniY=" << clIniY - << "clIniZ=" << clIniZ - << "clMovX=" << clMovX - << "clMovY=" << clMovY - << "clMovZ=" << clMovZ + (*mDBGOut) << "tpcMov" + << "side=" << side + << "imposedTB=" << tb + << "dz=" << dz + << "clX=" << clX + << "clY=" << clY + << "clZ=" << clZ << "\n"; } } @@ -294,6 +405,7 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) void TPCTrackStudySpec::endOfStream(EndOfStreamContext& ec) { mDBGOut.reset(); + mDBGOutCl.reset(); } void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -309,9 +421,18 @@ void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; + Options opts{ + {"target-x", VariantType::Float, 70.f, {"Try to propagate to this radius"}}, + {"n-moves", VariantType::Int, 6, {"Number of moves in allow range"}}, + {"dump-clusters", VariantType::Bool, false, {"dump clusters"}}, + {"tf-start", VariantType::Int, 0, {"1st TF to process"}}, + {"tf-end", VariantType::Int, 999999999, {"last TF to process"}}, + {"use-gpu-fitter", VariantType::Bool, false, {"use GPU track model for refit instead of TrackParCov"}}, + {"repeat-ini-ref", VariantType::Bool, false, {"store ini-refit param with every moved track"}}, + {"use-r-as-x", VariantType::Bool, false, {"Use radius instead of target sector X"}}}; auto dataRequest = std::make_shared(); dataRequest->requestTracks(srcTracks, useMC); @@ -325,14 +446,14 @@ DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); return DataProcessorSpec{ "tpc-track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, - Options{{"target-radius", VariantType::Float, 70.f, {"Try to propagate to this radius"}}}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, srcTracks, useMC)}, + opts}; } } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx new file mode 100644 index 0000000000000..f1415e73c10b6 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackInfoExt.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended track info (for debugging) + +#include "GlobalTrackingStudy/TrackInfoExt.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx new file mode 100644 index 0000000000000..8f6604b029605 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -0,0 +1,1411 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "TPCCalibration/VDriftHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/O2DatabasePDG.h" +#include "SimulationDataFormat/TrackReference.h" +#include "CommonDataFormat/BunchFilling.h" +#include "CommonUtils/NameConf.h" +#include "DataFormatsFT0/RecPoints.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "FT0Reconstruction/InteractionTag.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "GlobalTrackingStudy/TrackMCStudy.h" +#include "GlobalTrackingStudy/TrackMCStudyConfig.h" +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" +#include "GlobalTracking/MatchTPCITSParams.h" +#include "TPCBase/ParameterElectronics.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "DataFormatsFT0/RecPoints.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/DCA.h" +#include "Steer/MCKinematicsReader.h" +#include "DCAFitter/DCAFitterN.h" +#include "DetectorsVertexing/SVertexerParams.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUParam.h" +#include "GPUParam.inc" +#include "MathUtils/fit.h" +#include +#include +#include +#include +#include +#include + +// workflow to study relation of reco tracks to MCTruth +// o2-trackmc-study-workflow --device-verbosity 3 -b --run + +namespace o2::trackstudy +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using VTIndexV = std::pair; +using GTrackID = o2::dataformats::GlobalTrackID; +using TBracket = o2::math_utils::Bracketf_t; + +using timeEst = o2::dataformats::TimeStampWithError; + +class TrackMCStudy final : public Task +{ + public: + TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } + ~TrackMCStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(const o2::globaltracking::RecoContainer& recoData); + + private: + void processTPCTrackRefs(); + void processITSTracks(const o2::globaltracking::RecoContainer& recoData); + void loadTPCOccMap(const o2::globaltracking::RecoContainer& recoData); + void fillMCClusterInfo(const o2::globaltracking::RecoContainer& recoData); + void prepareITSData(const o2::globaltracking::RecoContainer& recoData); + bool processMCParticle(int src, int ev, int trid); + bool addMCParticle(const MCTrack& mctr, const o2::MCCompLabel& lb, TParticlePDG* pPDG = nullptr); + bool acceptMCCharged(const MCTrack& tr, const o2::MCCompLabel& lb, int followDec = -1); + bool propagateToRefX(o2::track::TrackParCov& trcTPC, o2::track::TrackParCov& trcITS); + bool refitV0(int i, o2::dataformats::V0& v0, const o2::globaltracking::RecoContainer& recoData); + void updateTimeDependentParams(ProcessingContext& pc); + float getDCAYCut(float pt) const; + + const std::vector* mCurrMCTracks = nullptr; + TVector3 mCurrMCVertex; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mDBGOut; + std::vector mTBinClOcc; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting from the TB = i*mNTPCOccBinLength + std::vector mTBinClOccHist; //< original occupancy + std::vector mIntBC; ///< interaction global BC wrt TF start + std::vector mTPCOcc; ///< TPC occupancy for this interaction time + std::vector mITSOcc; //< N ITS clusters in the ROF containing collision + std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + + bool mCheckSV = false; //< check SV binding (apart from prongs availability) + bool mRecProcStage = false; //< flag that the MC particle was added only at the stage of reco tracks processing + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + float mNTPCOccBinLengthInv = -1.f; + int mVerbose = 0; + float mITSTimeBiasMUS = 0.f; + float mITSROFrameLengthMUS = 0.f; ///< ITS RO frame in mus + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + + int mNCheckDecays = 0; + + GTrackID::mask_t mTracksSrc{}; + o2::steer::MCKinematicsReader mcReader; // reader of MC information + std::vector mITSROF; + std::vector mITSROFBracket; + std::vector mDecProdLblPool; // labels of decay products to watch, added to MC map + std::vector mMCVtVec{}; + + struct DecayRef { + o2::MCCompLabel mother{}; + o2::track::TrackPar parent{}; + int pdg = 0; + int daughterFirst = -1; + int daughterLast = -1; + int foundSVID = -1; + }; + std::vector> mDecaysMaps; // for every parent particle to watch, store its label and entries of 1st/last decay product labels in mDecProdLblPool + std::unordered_map mSelMCTracks; + std::unordered_map> mSelTRefIdx; + std::vector mSelTRefs; + o2::vertexing::DCAFitterN<2> mFitterV0; + static constexpr float MaxSnp = 0.9; // max snp of ITS or TPC track at xRef to be matched +}; + +void TrackMCStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mcReader.initFromDigitContext("collisioncontext.root"); + + mDBGOut = std::make_unique("trackMCStudy.root", "recreate"); + mVerbose = ic.options().get("device-verbosity"); + + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + for (int id = 0; id < sizeof(params.decayPDG) / sizeof(int); id++) { + if (params.decayPDG[id] < 0) { + break; + } + mNCheckDecays++; + } + mDecaysMaps.resize(mNCheckDecays); + mTPCCorrMapsLoader.init(ic); +} + +void TrackMCStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + for (int i = 0; i < mNCheckDecays; i++) { + mDecaysMaps[i].clear(); + } + mDecProdLblPool.clear(); + mMCVtVec.clear(); + mCurrMCTracks = nullptr; + + recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + mRecProcStage = false; + process(recoData); +} + +void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); + bool updateMaps = false; + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + } + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + const auto& alpParamsITS = o2::itsmft::DPLAlpideParam::Instance(); + mITSROFrameLengthMUS = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS) ? alpParamsITS.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingMUS : alpParamsITS.roFrameLengthTrig * 1.e-3; + LOGP(info, "VertexTrackMatcher ITSROFrameLengthMUS:{}", mITSROFrameLengthMUS); + + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUS = elParam.ZbinWidth; + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); + if (mCheckSV) { + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + mFitterV0.setBz(o2::base::Propagator::Instance()->getNominalBz()); + mFitterV0.setUseAbsDCA(svparam.useAbsDCA); + mFitterV0.setPropagateToPCA(false); + mFitterV0.setMaxR(svparam.maxRIni); + mFitterV0.setMinParamChange(svparam.minParamChange); + mFitterV0.setMinRelChi2Change(svparam.minRelChi2Change); + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setMatCorrType(o2::base::Propagator::MatCorrType(svparam.matCorr)); + mFitterV0.setUsePropagator(svparam.usePropagator); + mFitterV0.setRefitWithMatCorr(svparam.refitWithMatCorr); + mFitterV0.setMaxStep(svparam.maxStep); + mFitterV0.setMaxSnp(svparam.maxSnp); + mFitterV0.setMinXSeed(svparam.minXSeed); + } + } +} + +void TrackMCStudy::process(const o2::globaltracking::RecoContainer& recoData) +{ + constexpr float SQRT12Inv = 0.288675f; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + auto pvvec = recoData.getPrimaryVertices(); + auto pvvecLbl = recoData.getPrimaryVertexMCLabels(); + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto prop = o2::base::Propagator::Instance(); + int nv = vtxRefs.size(); + float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin + float itsBias = 0.5 * mITSROFrameLengthMUS + o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingMUS; // ITS time is supplied in \mus as beginning of ROF + + prepareITSData(recoData); + loadTPCOccMap(recoData); + auto getITSPatt = [&](GTrackID gid, uint8_t& ncl) { + int8_t patt = 0; + if (gid.getSource() == VTIndex::ITSAB) { + const auto& itsTrf = recoData.getITSABRefs()[gid]; + ncl = itsTrf.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + patt |= 0x1 << il; + } + } + patt |= 0x1 << 7; + } else { + const auto& itsTr = recoData.getITSTrack(gid); + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + patt |= 0x1 << il; + ncl++; + } + } + } + return patt; + }; + + auto fillTPCClusterInfo = [&recoData](const o2::tpc::TrackTPC& trc, RecTrack& tref) { + if (recoData.inputsTPCclusters) { + uint8_t clSect = 0, clRow = 0, lowestR = -1; + uint32_t clIdx = 0; + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto tpcClusAcc = recoData.getTPCClusters(); + const auto shMap = recoData.clusterShMapTPC; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { // outside -> inside ordering, but on the sector boundaries backward jumps are possible + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + if (clRow < lowestR) { + tref.rowCountTPC++; + lowestR = clRow; + } + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; + if (shMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + tref.nClTPCShared++; + } + } + tref.lowestPadRow = lowestR; + const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; + int padFromEdge = int(clus.getPad()), npads = o2::gpu::GPUTPCGeometry::NPads(clRow); + if (padFromEdge > npads / 2) { + padFromEdge = npads - 1 - padFromEdge; + } + tref.padFromEdge = uint8_t(padFromEdge); + trc.getClusterReference(clRefs, 0, clSect, clRow, clIdx); + tref.rowMaxTPC = clRow; + } + }; + + auto flagTPCClusters = [&recoData](const o2::tpc::TrackTPC& trc, o2::MCCompLabel lbTrc) { + if (recoData.inputsTPCclusters) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto* TPCClMClab = recoData.inputsTPCclusters->clusterIndex.clustersMCTruth; + const auto& TPCClusterIdxStruct = recoData.inputsTPCclusters->clusterIndex; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { + uint8_t clSect = 0, clRow = 0; + uint32_t clIdx = 0; + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + auto labels = TPCClMClab->getLabels(clIdx + TPCClusterIdxStruct.clusterOffset[clSect][clRow]); + for (auto& lbl : labels) { + if (lbl == lbTrc) { + const_cast(lbl).setFakeFlag(true); // actually, in this way we are flagging that this cluster was correctly attached + break; + } + } + } + } + }; + + { + const auto* digconst = mcReader.getDigitizationContext(); + const auto& mcEvRecords = digconst->getEventRecords(false); + int ITSTimeBias = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; + int ITSROFLen = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + unsigned int rofCount = 0; + const auto ITSClusROFRec = recoData.getITSClustersROFRecords(); + for (const auto& mcIR : mcEvRecords) { + long tbc = mcIR.differenceInBC(recoData.startIR); + auto& mcVtx = mMCVtVec.emplace_back(); + mcVtx.ts = tbc * o2::constants::lhc::LHCBunchSpacingMUS + mcIR.getTimeOffsetWrtBC() * 1e-3; + mcVtx.ID = mIntBC.size(); + mIntBC.push_back(tbc); + int occBin = tbc / 8 * mNTPCOccBinLengthInv; + mTPCOcc.push_back(occBin < 0 ? mTBinClOcc[0] : (occBin >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[occBin])); + // fill ITS occupancy + long gbc = mcIR.toLong(); + while (rofCount < ITSClusROFRec.size()) { + long rofbcMin = ITSClusROFRec[rofCount].getBCData().toLong() + ITSTimeBias, rofbcMax = rofbcMin + ITSROFLen; + if (gbc < rofbcMin) { // IRs and ROFs are sorted, so this IR is prior of all ROFs + mITSOcc.push_back(0); + } else if (gbc < rofbcMax) { + mITSOcc.push_back(ITSClusROFRec[rofCount].getNEntries()); + } else { + rofCount++; // test next ROF + continue; + } + break; + } + if (mNTPCOccBinLengthInv > 0.f) { + mcVtx.occTPCV.resize(params.nOccBinsDrift); + int grp = TMath::Max(1, TMath::Nint(params.nTBPerOccBin * mNTPCOccBinLengthInv)); + for (int ib = 0; ib < params.nOccBinsDrift; ib++) { + float smb = 0; + int tbs = occBin + TMath::Nint(ib * params.nTBPerOccBin * mNTPCOccBinLengthInv); + for (int ig = 0; ig < grp; ig++) { + if (tbs >= 0 && tbs < int(mTBinClOccHist.size())) { + smb += mTBinClOccHist[tbs]; + } + tbs++; + } + mcVtx.occTPCV[ib] = smb; + } + } + if (rofCount >= ITSClusROFRec.size()) { + mITSOcc.push_back(0); // IR after the last ROF + } + } + } + // collect interesting MC particle (tracks and parents) + int curSrcMC = 0, curEvMC = 0; + for (curSrcMC = 0; curSrcMC < (int)mcReader.getNSources(); curSrcMC++) { + if (mVerbose > 1) { + LOGP(info, "Source {}", curSrcMC); + } + int nev = mcReader.getNEvents(curSrcMC); + bool okAccVtx = true; + if (nev != (int)mMCVtVec.size()) { + LOGP(debug, "source {} has {} events while {} MC vertices were booked", curSrcMC, nev, mMCVtVec.size()); + okAccVtx = false; + if (nev > (int)mMCVtVec.size()) { // QED + continue; + } + } + for (curEvMC = 0; curEvMC < nev; curEvMC++) { + if (mVerbose > 1) { + LOGP(info, "Event {}", curEvMC); + } + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); + const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); + if (okAccVtx) { + auto& pos = mMCVtVec[curEvMC].pos; + if (pos[2] < -999) { + pos[0] = mCurrMCVertex.X(); + pos[1] = mCurrMCVertex.Y(); + pos[2] = mCurrMCVertex.Z(); + } + } + for (int itr = 0; itr < mCurrMCTracks->size(); itr++) { + processMCParticle(curSrcMC, curEvMC, itr); + } + } + } + if (mVerbose > 0) { + for (int id = 0; id < mNCheckDecays; id++) { + LOGP(info, "Decay PDG={} : {} entries", params.decayPDG[id], mDecaysMaps[id].size()); + } + } + + // add reconstruction info to MC particles. If MC particle was not selected before but was reconstrected, account MC info + mRecProcStage = true; // MC particles accepted only at this stage will be flagged + for (int iv = 0; iv < nv; iv++) { + if (mVerbose > 1) { + LOGP(info, "processing PV {} of {}", iv, nv); + } + o2::MCEventLabel pvLbl; + int pvID = -1; + if (iv < (int)pvvecLbl.size()) { + pvLbl = pvvecLbl[iv]; + pvID = iv; + if (pvLbl.isSet() && pvLbl.getEventID() < mMCVtVec.size()) { + mMCVtVec[pvLbl.getEventID()].recVtx.emplace_back(RecPV{pvvec[iv], pvLbl}); + } + } + const auto& vtref = vtxRefs[iv]; + for (int is = GTrackID::NSources; is--;) { + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + if (!mTracksSrc[is] || !recoData.isTrackSourceLoaded(is) || !(dm[DetID::ITS] || dm[DetID::TPC])) { + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + const auto& trc = recoData.getTrackParam(vid); + if (trc.getPt() < params.minPt || std::abs(trc.getTgl()) > params.maxTgl) { + continue; + } + auto lbl = recoData.getTrackMCLabel(vid); + if (lbl.isValid()) { + lbl.setFakeFlag(false); + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // add the track which was not added during MC scan + if (lbl.getSourceID() != curSrcMC || lbl.getEventID() != curEvMC) { + curSrcMC = lbl.getSourceID(); + curEvMC = lbl.getEventID(); + mCurrMCTracks = &mcReader.getTracks(curSrcMC, curEvMC); + const_cast(mcReader.getMCEventHeader(curSrcMC, curEvMC)).GetVertex(mCurrMCVertex); + } + if (!acceptMCCharged((*mCurrMCTracks)[lbl.getTrackID()], lbl)) { + continue; + } + entry = mSelMCTracks.find(lbl); + } + auto& trackFamily = entry->second; + if (vid.isAmbiguous()) { // do not repeat ambiguous tracks + if (trackFamily.contains(vid)) { + continue; + } + } + auto& trf = trackFamily.recTracks.emplace_back(); + trf.gid = vid; // account(iv, vid); + trf.pvID = pvID; + trf.pvLabel = pvLbl; + while (dm[DetID::ITS] && dm[DetID::TPC]) { // this track should have both ITS and TPC parts, if ITS was mismatched, fill it to its proper MC track slot + auto gidSet = recoData.getSingleDetectorRefs(vid); + if (!gidSet[GTrackID::ITS].isSourceSet()) { + break; // AB track, nothing to check + } + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS == trackFamily.mcTrackInfo.label) { + break; // correct match, no need for special treatment + } + const auto& trcITSF = recoData.getTrackParam(gidSet[GTrackID::ITS]); + if (trcITSF.getPt() < params.minPt || std::abs(trcITSF.getTgl()) > params.maxTgl) { + break; // ignore this track + } + auto entryOfFake = mSelMCTracks.find(lblITS); + if (entryOfFake == mSelMCTracks.end()) { // this MC track was not selected + break; + } + auto& trackFamilyOfFake = entryOfFake->second; + auto& trfOfFake = trackFamilyOfFake.recTracks.emplace_back(); + trfOfFake.gid = gidSet[GTrackID::ITS]; // account(iv, vid); + break; + } + if (mVerbose > 1) { + LOGP(info, "Matched rec track {} to MC track {}", vid.asString(), entry->first.asString()); + } + } else { + continue; + } + } + } + } + + LOGP(info, "collected {} MC tracks", mSelMCTracks.size()); + if (params.minTPCRefsToExtractClRes > 0 || params.storeTPCTrackRefs) { // prepare MC trackrefs for TPC + processTPCTrackRefs(); + } + + int mcnt = 0; + for (auto& entry : mSelMCTracks) { + auto& trackFam = entry.second; + auto& tracks = trackFam.recTracks; + mcnt++; + if (tracks.empty()) { + continue; + } + if (mVerbose > 1) { + LOGP(info, "Processing MC track#{} {} -> {} reconstructed tracks", mcnt - 1, entry.first.asString(), tracks.size()); + } + // sort according to the gid complexity (in principle, should be already sorted due to the backwards loop over NSources above + std::sort(tracks.begin(), tracks.end(), [](const RecTrack& lhs, const RecTrack& rhs) { + const auto mskL = lhs.gid.getSourceDetectorsMask(); + const auto mskR = rhs.gid.getSourceDetectorsMask(); + bool itstpcL = mskL[DetID::ITS] && mskL[DetID::TPC], itstpcR = mskR[DetID::ITS] && mskR[DetID::TPC]; + if (itstpcL && !itstpcR) { // to avoid TPC/TRD or TPC/TOF shadowing ITS/TPC + return true; + } + return lhs.gid.getSource() > rhs.gid.getSource(); + }); + if (params.storeTPCTrackRefs) { + auto rft = mSelTRefIdx.find(entry.first); + if (rft != mSelTRefIdx.end()) { + auto rfent = rft->second; + for (int irf = rfent.first; irf < rfent.second; irf++) { + trackFam.mcTrackInfo.trackRefsTPC.push_back(mSelTRefs[irf]); + } + } + } + // fill track params + int tcnt = 0; + for (auto& tref : tracks) { + if (tref.gid.isSourceSet()) { + auto gidSet = recoData.getSingleDetectorRefs(tref.gid); + tref.track = recoData.getTrackParam(tref.gid); + if (recoData.getTrackMCLabel(tref.gid).isFake()) { + tref.flags |= RecTrack::FakeGLO; + } + auto msk = tref.gid.getSourceDetectorsMask(); + if (msk[DetID::ITS]) { + if (gidSet[GTrackID::ITS].isSourceSet()) { // has ITS track rather than AB tracklet + tref.pattITS = getITSPatt(gidSet[GTrackID::ITS], tref.nClITS); + if (trackFam.entITS < 0) { + trackFam.entITS = tcnt; + } + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS.isFake()) { + tref.flags |= RecTrack::FakeITS; + } + if (lblITS == trackFam.mcTrackInfo.label) { + trackFam.entITSFound = tcnt; + } + } else { // AB ITS tracklet + tref.pattITS = getITSPatt(gidSet[GTrackID::ITSAB], tref.nClITS); + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSAB]).isFake()) { + tref.flags |= RecTrack::FakeITS; + } + } + if (msk[DetID::TPC]) { + if (trackFam.entITSTPC < 0) { // has both ITS and TPC contribution + trackFam.entITSTPC = tcnt; + } + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { + tref.flags |= RecTrack::FakeITSTPC; + } + + if (msk[DetID::TRD]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRD]).isFake()) { + tref.flags |= RecTrack::FakeTRD; + } + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTRDTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } else { + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPCTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } + } + } + if (msk[DetID::TPC]) { + const auto& trtpc = recoData.getTPCTrack(gidSet[GTrackID::TPC]); + tref.nClTPC = trtpc.getNClusters(); + if (trtpc.hasBothSidesClusters()) { + tref.flags |= RecTrack::HASACSides; + } + fillTPCClusterInfo(trtpc, tref); + flagTPCClusters(trtpc, entry.first); + if (trackFam.entTPC < 0) { + trackFam.entTPC = tcnt; + trackFam.tpcT0 = trtpc.getTime0(); + } + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPC]).isFake()) { + tref.flags |= RecTrack::FakeTPC; + } + if (!msk[DetID::ITS]) { + if (msk[DetID::TRD]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTRD]).isFake()) { + tref.flags |= RecTrack::FakeTRD; + } + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTRDTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } else { + if (msk[DetID::TOF]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPCTOF]).isFake()) { + tref.flags |= RecTrack::FakeTOF; + } + } + } + } + } + float ts = 0, terr = 0; + if (tref.gid.getSource() != GTrackID::ITS) { + recoData.getTrackTime(tref.gid, ts, terr); + tref.ts = timeEst{ts, terr}; + } else { + const auto& itsBra = mITSROFBracket[mITSROF[tref.gid.getIndex()]]; + tref.ts = timeEst{itsBra.mean(), itsBra.delta() * SQRT12Inv}; + } + } else { + LOGP(info, "Invalid entry {} of {} getTrackMCLabel {}", tcnt, tracks.size(), tref.gid.asString()); + } + tcnt++; + } + if (trackFam.entITS > -1 && trackFam.entTPC > -1) { // ITS and TPC were found but matching failed + auto vidITS = recoData.getITSContributorGID(tracks[trackFam.entITS].gid); + auto vidTPC = recoData.getTPCContributorGID(tracks[trackFam.entTPC].gid); + auto trcTPC = recoData.getTrackParam(vidTPC); + auto trcITS = recoData.getTrackParamOut(vidITS); + if (propagateToRefX(trcTPC, trcITS)) { + trackFam.trackITSProp = trcITS; + trackFam.trackTPCProp = trcTPC; + } else { + trackFam.trackITSProp.invalidate(); + trackFam.trackTPCProp.invalidate(); + } + } else { + trackFam.trackITSProp.invalidate(); + trackFam.trackTPCProp.invalidate(); + } + } + + // SVertices (V0s) + if (mCheckSV) { + auto v0s = recoData.getV0sIdx(); + auto prpr = [](o2::trackstudy::TrackFamily& f) { + std::string s; + s += fmt::format(" par {} Ntpccl={} Nitscl={} ", f.mcTrackInfo.pdgParent, f.mcTrackInfo.nTPCCl, f.mcTrackInfo.nITSCl); + for (auto& t : f.recTracks) { + s += t.gid.asString(); + s += " "; + } + return s; + }; + for (int svID; svID < (int)v0s.size(); svID++) { + const auto& v0idx = v0s[svID]; + int nOKProngs = 0, realMCSVID = -1; + int8_t decTypeID = -1; + for (int ipr = 0; ipr < v0idx.getNProngs(); ipr++) { + auto mcl = recoData.getTrackMCLabel(v0idx.getProngID(ipr)); // was this MC particle selected? + auto itl = mSelMCTracks.find(mcl); + if (itl == mSelMCTracks.end()) { + nOKProngs = -1; // was not selected as interesting one, ignore + break; + } + auto& trackFamily = itl->second; + int decayParentIndex = trackFamily.mcTrackInfo.parentEntry; + if (decayParentIndex < 0) { // does not come from decay + break; + } + if (ipr == 0) { + realMCSVID = decayParentIndex; + decTypeID = trackFamily.mcTrackInfo.parentDecID; + nOKProngs = 1; + LOGP(debug, "Prong{} {} comes from {}/{}", ipr, prpr(trackFamily), decTypeID, realMCSVID); + continue; + } + if (realMCSVID != decayParentIndex || decTypeID != trackFamily.mcTrackInfo.parentDecID) { + break; + } + LOGP(debug, "Prong{} {} comes from {}/{}", ipr, prpr(trackFamily), decTypeID, realMCSVID); + nOKProngs++; + } + if (nOKProngs == v0idx.getNProngs()) { // all prongs are from the decay of MC parent which deemed to be interesting, flag it + LOGP(debug, "Decay {}/{} was found", decTypeID, realMCSVID); + mDecaysMaps[decTypeID][realMCSVID].foundSVID = svID; + } + } + } + + // collect ITS/TPC cluster info for selected MC particles + fillMCClusterInfo(recoData); + + // single tracks + for (auto& entry : mSelMCTracks) { + auto& trackFam = entry.second; + (*mDBGOut) << "tracks" << "tr=" << trackFam << "\n"; + } + + // decays + std::vector decFam; + for (int id = 0; id < mNCheckDecays; id++) { + std::string decTreeName = fmt::format("dec{}", params.decayPDG[id]); + for (const auto& dec : mDecaysMaps[id]) { + decFam.clear(); + bool skip = false; + for (int idd = dec.daughterFirst; idd <= dec.daughterLast; idd++) { + auto dtLbl = mDecProdLblPool[idd]; // daughter MC label + const auto& dtFamily = mSelMCTracks[dtLbl]; + if (dtFamily.mcTrackInfo.pdgParent != dec.pdg) { + LOGP(error, "{}-th decay (pdg={}): {} in {}:{} range refers to MC track with pdgParent = {}", id, params.decayPDG[id], idd, dec.daughterFirst, dec.daughterLast, dtFamily.mcTrackInfo.pdgParent); + skip = true; + break; + } + decFam.push_back(dtFamily); + } + if (!skip) { + o2::dataformats::V0 v0; + if (dec.foundSVID >= 0 && !refitV0(dec.foundSVID, v0, recoData)) { + v0.invalidate(); + } + (*mDBGOut) << decTreeName.c_str() << "pdgPar=" << dec.pdg << "trPar=" << dec.parent << "prod=" << decFam << "found=" << dec.foundSVID << "sv=" << v0 << "\n"; + } + } + } + + for (auto& mcVtx : mMCVtVec) { // sort rec.vertices in mult. order + std::sort(mcVtx.recVtx.begin(), mcVtx.recVtx.end(), [](const RecPV& lhs, const RecPV& rhs) { + return lhs.pv.getNContributors() > rhs.pv.getNContributors(); + }); + (*mDBGOut) << "mcVtxTree" << "mcVtx=" << mcVtx << "\n"; + } + + if (params.storeITSInfo) { + processITSTracks(recoData); + } +} + +void TrackMCStudy::processTPCTrackRefs() +{ + constexpr float alpsec[18] = {0.174533, 0.523599, 0.872665, 1.221730, 1.570796, 1.919862, 2.268928, 2.617994, 2.967060, 3.316126, 3.665191, 4.014257, 4.363323, 4.712389, 5.061455, 5.410521, 5.759587, 6.108652}; + constexpr float sinAlp[18] = {0.173648, 0.500000, 0.766044, 0.939693, 1.000000, 0.939693, 0.766044, 0.500000, 0.173648, -0.173648, -0.500000, -0.766044, -0.939693, -1.000000, -0.939693, -0.766044, -0.500000, -0.173648}; + constexpr float cosAlp[18] = {0.984808, 0.866025, 0.642788, 0.342020, 0.000000, -0.342020, -0.642788, -0.866025, -0.984808, -0.984808, -0.866025, -0.642788, -0.342020, -0.000000, 0.342020, 0.642788, 0.866025, 0.984808}; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + for (auto& entry : mSelMCTracks) { + auto lb = entry.first; + auto trspan = mcReader.getTrackRefs(lb.getSourceID(), lb.getEventID(), lb.getTrackID()); + int q = entry.second.mcTrackInfo.track.getCharge(); + if (q * q != 1) { + continue; + } + int ref0entry = mSelTRefs.size(), nrefsSel = 0; + for (const auto& trf : trspan) { + if (trf.getDetectorId() != 1) { // process TPC only + continue; + } + float pT = std::sqrt(trf.Px() * trf.Px() + trf.Py() * trf.Py()); + if (pT < 0.05) { + continue; + } + float secX, secY, phi = std::atan2(trf.Y(), trf.X()); + int sector = o2::math_utils::angle2Sector(phi); + o2::math_utils::rotateZInv(trf.X(), trf.Y(), secX, secY, sinAlp[sector], cosAlp[sector]); // sector coordinates + float phiPt = std::atan2(trf.Py(), trf.Px()); + o2::math_utils::bringTo02Pi(phiPt); + auto dphiPt = phiPt - alpsec[sector]; + if (dphiPt > o2::constants::math::PI) { // account for wraps + dphiPt -= o2::constants::math::TwoPI; + } else if (dphiPt < -o2::constants::math::PI) { + dphiPt += o2::constants::math::TwoPI; + } else if (std::abs(dphiPt) > o2::constants::math::PIHalf * 0.8) { + continue; // ignore backward going or parallel to padrows tracks + } + float tgL = trf.Pz() / pT; + std::array pars = {secY, trf.Z(), std::sin(dphiPt), tgL, q / pT}; + auto& refTrack = mSelTRefs.emplace_back(secX, alpsec[sector], pars); + refTrack.setUserField(uint16_t(sector)); + nrefsSel++; + } + if (nrefsSel < params.minTPCRefsToExtractClRes) { + mSelTRefs.resize(ref0entry); // discard unused tracks + continue; + } else { + mSelTRefIdx[lb] = std::make_pair(ref0entry, ref0entry + nrefsSel); + } + } +} + +void TrackMCStudy::fillMCClusterInfo(const o2::globaltracking::RecoContainer& recoData) +{ + // TPC clusters info + const auto& TPCClusterIdxStruct = recoData.inputsTPCclusters->clusterIndex; + const auto* TPCClMClab = recoData.inputsTPCclusters->clusterIndex.clustersMCTruth; + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + + ClResTPC clRes{}; + for (uint8_t row = 0; row < 152; row++) { // we need to go in increasing row, so this should be the outer loop + for (uint8_t sector = 0; sector < 36; sector++) { + unsigned int offs = TPCClusterIdxStruct.clusterOffset[sector][row]; + for (unsigned int icl0 = 0; icl0 < TPCClusterIdxStruct.nClusters[sector][row]; icl0++) { + const auto labels = TPCClMClab->getLabels(icl0 + offs); + int ncontLb = 0; // number of real contrubutors to this label (w/o noise) + for (const auto& lbl : labels) { + if (!lbl.isValid()) { + continue; + } + ncontLb++; + } + const auto& clus = TPCClusterIdxStruct.clusters[sector][row][icl0]; + int tbinH = int(clus.getTime() * mNTPCOccBinLengthInv); // time bin converted to slot of the occ. histo + clRes.contTracks.clear(); + bool doClusRes = (params.minTPCRefsToExtractClRes > 0) && (params.rejectClustersResStat <= 0. || gRandom->Rndm() < params.rejectClustersResStat); + for (auto lbl : labels) { + bool corrAttach = lbl.isFake(); // was this flagged in the flagTPCClusters called from process ? + lbl.setFakeFlag(false); + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // not selected + continue; + } + auto& mctr = entry->second.mcTrackInfo; + mctr.nTPCCl++; + if (row > mctr.maxTPCRow) { + mctr.maxTPCRow = row; + mctr.maxTPCRowSect = sector; + mctr.nUsedPadRows++; + } else if (row == 0 && mctr.nUsedPadRows == 0) { + mctr.nUsedPadRows++; + } + if (row < mctr.minTPCRow) { + mctr.minTPCRow = row; + mctr.minTPCRowSect = sector; + } + if (mctr.minTPCRowSect == sector && row > mctr.maxTPCRowInner) { + mctr.maxTPCRowInner = row; + } + if (ncontLb > 1) { + mctr.nTPCClShared++; + } + // try to extract ideal track position + if (doClusRes) { + auto entTRefIDsIt = mSelTRefIdx.find(lbl); + if (entTRefIDsIt == mSelTRefIdx.end()) { + continue; + } + float xc, yc, zc; + mTPCCorrMapsLoader.Transform(sector, row, clus.getPad(), clus.getTime(), xc, yc, zc, mctr.bcInTF / 8.); // nominal time of the track + + const auto& entTRefIDs = entTRefIDsIt->second; + // find bracketing TRef params + int entIDBelow = -1, entIDAbove = -1; + float xBelow = -1e6, xAbove = 1e6; + + for (int entID = entTRefIDs.first; entID < entTRefIDs.second; entID++) { + const auto& refTr = mSelTRefs[entID]; + if (refTr.getUserField() != sector % 18) { + continue; + } + if ((refTr.getX() < xc) && (refTr.getX() > xBelow) && (refTr.getX() > xc - params.maxTPCRefExtrap)) { + xBelow = refTr.getX(); + entIDBelow = entID; + } + if ((refTr.getX() > xc) && (refTr.getX() < xAbove) && (refTr.getX() < xc + params.maxTPCRefExtrap)) { + xAbove = refTr.getX(); + entIDAbove = entID; + } + } + if ((entIDBelow < 0 && entIDAbove < 0) || (params.requireTopBottomRefs && (entIDBelow < 0 || entIDAbove < 0))) { + continue; + } + auto prop = o2::base::Propagator::Instance(); + o2::track::TrackPar tparAbove, tparBelow; + bool okBelow = entIDBelow >= 0 && prop->PropagateToXBxByBz((tparBelow = mSelTRefs[entIDBelow]), xc, 0.99, 2.); + bool okAbove = entIDAbove >= 0 && prop->PropagateToXBxByBz((tparAbove = mSelTRefs[entIDAbove]), xc, 0.99, 2.); + if ((!okBelow && !okAbove) || (params.requireTopBottomRefs && (!okBelow || !okAbove))) { + continue; + } + + int nmeas = 0; + auto& clCont = clRes.contTracks.emplace_back(); + clCont.corrAttach = corrAttach; + if (okBelow) { + clCont.below = {mSelTRefs[entIDBelow].getX(), tparBelow.getY(), tparBelow.getZ()}; + clCont.snp += tparBelow.getSnp(); + clCont.tgl += tparBelow.getTgl(); + clCont.q2pt += tparBelow.getQ2Pt(); + nmeas++; + } + if (okAbove) { + clCont.above = {mSelTRefs[entIDAbove].getX(), tparAbove.getY(), tparAbove.getZ()}; + clCont.snp += tparAbove.getSnp(); + clCont.tgl += tparAbove.getTgl(); + clCont.q2pt += tparAbove.getQ2Pt(); + nmeas++; + } + if (nmeas) { + if (clRes.contTracks.size() == 1) { + int occBin = mctr.bcInTF / 8 * mNTPCOccBinLengthInv; + clRes.occ = occBin < 0 ? mTBinClOcc[0] : (occBin >= mTBinClOcc.size() ? mTBinClOcc.back() : mTBinClOcc[occBin]); + } + clCont.xyz = {xc, yc, zc}; + if (nmeas > 1) { + clCont.snp *= 0.5; + clCont.tgl *= 0.5; + clCont.q2pt *= 0.5; + } + } else { + clRes.contTracks.pop_back(); + } + } + } + if (clRes.getNCont()) { + clRes.sect = sector; + clRes.row = row; + clRes.qtot = clus.getQtot(); + clRes.qmax = clus.getQmax(); + clRes.flags = clus.getFlags(); + clRes.sigmaTimePacked = clus.sigmaTimePacked; + clRes.sigmaPadPacked = clus.sigmaPadPacked; + clRes.ncont = ncontLb; + clRes.sortCont(); + + if (tbinH < 0) { + tbinH = 0; + } else if (tbinH >= int(mTBinClOccHist.size())) { + tbinH = (int)mTBinClOccHist.size() - 1; + } + clRes.occBin = mTBinClOccHist[tbinH]; + + (*mDBGOut) << "clres" << "clr=" << clRes << "\n"; + } + } + } + } + // fill ITS cluster info + const auto* mcITSClusters = recoData.getITSClustersMCLabels(); + const auto& ITSClusters = recoData.getITSClusters(); + for (unsigned int icl = 0; icl < ITSClusters.size(); icl++) { + const auto labels = mcITSClusters->getLabels(icl); + for (const auto& lbl : labels) { + auto entry = mSelMCTracks.find(lbl); + if (entry == mSelMCTracks.end()) { // not selected + continue; + } + auto& mctr = entry->second.mcTrackInfo; + mctr.nITSCl++; + mctr.pattITSCl |= 0x1 << o2::itsmft::ChipMappingITS::getLayer(ITSClusters[icl].getChipID()); + } + } +} + +bool TrackMCStudy::propagateToRefX(o2::track::TrackParCov& trcTPC, o2::track::TrackParCov& trcITS) +{ + bool refReached = false; + constexpr float TgHalfSector = 0.17632698f; + const auto& par = o2::globaltracking::MatchTPCITSParams::Instance(); + int trialsLeft = 2; + while (o2::base::Propagator::Instance()->PropagateToXBxByBz(trcTPC, par.XMatchingRef, MaxSnp, 2., par.matCorr)) { + if (refReached) { + break; + } + // make sure the track is indeed within the sector defined by alpha + if (fabs(trcTPC.getY()) < par.XMatchingRef * TgHalfSector) { + refReached = true; + break; // ok, within + } + if (!trialsLeft--) { + break; + } + auto alphaNew = o2::math_utils::angle2Alpha(trcTPC.getPhiPos()); + if (!trcTPC.rotate(alphaNew) != 0) { + break; // failed (RS: check effect on matching tracks to neighbouring sector) + } + } + if (!refReached) { + return false; + } + refReached = false; + float alp = trcTPC.getAlpha(); + if (!trcITS.rotate(alp) != 0 || !o2::base::Propagator::Instance()->PropagateToXBxByBz(trcITS, par.XMatchingRef, MaxSnp, 2., par.matCorr)) { + return false; + } + return true; +} + +void TrackMCStudy::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { + LOG(info) << "ITS Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + mITSTimeBiasMUS = par.roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + mITSROFrameLengthMUS = par.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +//_____________________________________________________ +void TrackMCStudy::prepareITSData(const o2::globaltracking::RecoContainer& recoData) +{ + const auto ITSTracksArray = recoData.getITSTracks(); + const auto ITSTrackROFRec = recoData.getITSTracksROFRecords(); + int nROFs = ITSTrackROFRec.size(); + mITSROF.clear(); + mITSROFBracket.clear(); + mITSROF.reserve(ITSTracksArray.size()); + mITSROFBracket.reserve(ITSTracksArray.size()); + for (int irof = 0; irof < nROFs; irof++) { + const auto& rofRec = ITSTrackROFRec[irof]; + long nBC = rofRec.getBCData().differenceInBC(recoData.startIR); + float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS + mITSTimeBiasMUS; + float tMax = tMin + mITSROFrameLengthMUS; + mITSROFBracket.emplace_back(tMin, tMax); + for (int it = 0; it < rofRec.getNEntries(); it++) { + mITSROF.push_back(irof); + } + } +} +/* +float TrackMCStudy::getDCAYCut(float pt) const +{ + static TF1 fun("dcayvspt", mDCAYFormula.c_str(), 0, 20); + return fun.Eval(pt); +} +*/ + +bool TrackMCStudy::processMCParticle(int src, int ev, int trid) +{ + const auto& mcPart = (*mCurrMCTracks)[trid]; + int pdg = mcPart.GetPdgCode(); + bool res = false; + while (true) { + auto lbl = o2::MCCompLabel(trid, ev, src); + int decay = -1; // is this decay to watch? + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + if (mcPart.T() < params.decayMotherMaxT) { + for (int id = 0; id < mNCheckDecays; id++) { + if (params.decayPDG[id] == std::abs(pdg)) { + decay = id; + break; + } + } + if (decay >= 0) { // check if decay and kinematics is acceptable + auto& decayPool = mDecaysMaps[decay]; + int idd0 = mcPart.getFirstDaughterTrackId(), idd1 = mcPart.getLastDaughterTrackId(); // we want only charged and trackable daughters + int dtStart = mDecProdLblPool.size(), dtEnd = -1; + if (idd0 < 0) { + break; + } + for (int idd = idd0; idd <= idd1; idd++) { + const auto& product = (*mCurrMCTracks)[idd]; + auto lbld = o2::MCCompLabel(idd, ev, src); + if (!acceptMCCharged(product, lbld, decay)) { + decay = -1; // discard decay + mDecProdLblPool.resize(dtStart); + break; + } + mDecProdLblPool.push_back(lbld); // register prong entry and label + } + if (decay >= 0) { + // account decay + dtEnd = mDecProdLblPool.size(); + for (int dtid = dtStart; dtid < dtEnd; dtid++) { // flag selected decay parent entry in the prongs MCs + mSelMCTracks[mDecProdLblPool[dtid]].mcTrackInfo.parentEntry = decayPool.size(); + mSelMCTracks[mDecProdLblPool[dtid]].mcTrackInfo.parentDecID = int8_t(decay); + } + dtEnd--; + std::array xyz{(float)mcPart.GetStartVertexCoordinatesX(), (float)mcPart.GetStartVertexCoordinatesY(), (float)mcPart.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcPart.GetStartVertexMomentumX(), (float)mcPart.GetStartVertexMomentumY(), (float)mcPart.GetStartVertexMomentumZ()}; + decayPool.emplace_back(DecayRef{lbl, + o2::track::TrackPar(xyz, pxyz, TMath::Nint(O2DatabasePDG::Instance()->GetParticle(mcPart.GetPdgCode())->Charge() / 3), false), + mcPart.GetPdgCode(), dtStart, dtEnd}); + if (mVerbose > 1) { + LOGP(info, "Adding MC parent pdg={} {}, with prongs in {}:{} range", pdg, lbl.asString(), dtStart, dtEnd); + } + res = true; // Accept! + } + break; + } + } + // check if this is a charged which should be processed but was not accounted as a decay product + if (mSelMCTracks.find(lbl) == mSelMCTracks.end()) { + res = acceptMCCharged(mcPart, lbl); + } + break; + } + return res; +} + +bool TrackMCStudy::acceptMCCharged(const MCTrack& tr, const o2::MCCompLabel& lb, int followDecay) +{ + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + if (tr.GetPt() < params.minPtMC || + std::abs(tr.GetTgl()) > params.maxTglMC || + tr.R2() > params.maxRMC * params.maxRMC) { + if (mVerbose > 1 && followDecay > -1) { + LOGP(info, "rejecting decay {} prong : pdg={}, pT={}, tgL={}, r={}", followDecay, tr.GetPdgCode(), tr.GetPt(), tr.GetTgl(), std::sqrt(tr.R2())); + } + return false; + } + float dx = tr.GetStartVertexCoordinatesX() - mCurrMCVertex.X(), dy = tr.GetStartVertexCoordinatesY() - mCurrMCVertex.Y(), dz = tr.GetStartVertexCoordinatesZ() - mCurrMCVertex.Z(); + float r2 = dx * dx + dy * dy; + float posTgl2 = r2 > 1 && std::abs(dz) < 20 ? dz * dz / r2 : 0; + if (posTgl2 > params.maxPosTglMC * params.maxPosTglMC) { + if (mVerbose > 1 && followDecay > -1) { + LOGP(info, "rejecting decay {} prong : pdg={}, pT={}, tgL={}, dr={}, dz={} r={}, z={}, posTgl={}", followDecay, tr.GetPdgCode(), tr.GetPt(), tr.GetTgl(), std::sqrt(r2), dz, std::sqrt(tr.R2()), tr.GetStartVertexCoordinatesZ(), std::sqrt(posTgl2)); + } + return false; + } + if (params.requireITSorTPCTrackRefs) { + auto trspan = mcReader.getTrackRefs(lb.getSourceID(), lb.getEventID(), lb.getTrackID()); + bool ok = false; + for (const auto& trf : trspan) { + if (trf.getDetectorId() == DetID::ITS || trf.getDetectorId() == DetID::TPC) { + ok = true; + break; + } + } + if (!ok) { + return false; + } + } + TParticlePDG* pPDG = O2DatabasePDG::Instance()->GetParticle(tr.GetPdgCode()); + if (!pPDG) { + LOGP(debug, "Unknown particle {}", tr.GetPdgCode()); + return false; + } + if (pPDG->Charge() == 0.) { + return false; + } + return addMCParticle(tr, lb, pPDG); +} + +bool TrackMCStudy::addMCParticle(const MCTrack& mcPart, const o2::MCCompLabel& lb, TParticlePDG* pPDG) +{ + std::array xyz{(float)mcPart.GetStartVertexCoordinatesX(), (float)mcPart.GetStartVertexCoordinatesY(), (float)mcPart.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcPart.GetStartVertexMomentumX(), (float)mcPart.GetStartVertexMomentumY(), (float)mcPart.GetStartVertexMomentumZ()}; + if (!pPDG && !(pPDG = O2DatabasePDG::Instance()->GetParticle(mcPart.GetPdgCode()))) { + LOGP(debug, "Unknown particle {}", mcPart.GetPdgCode()); + return false; + } + auto& mcEntry = mSelMCTracks[lb]; + mcEntry.mcTrackInfo.pdg = mcPart.GetPdgCode(); + mcEntry.mcTrackInfo.track = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), true); + mcEntry.mcTrackInfo.label = lb; + mcEntry.mcTrackInfo.bcInTF = mIntBC[lb.getEventID()]; + mcEntry.mcTrackInfo.occTPC = mTPCOcc[lb.getEventID()]; + mcEntry.mcTrackInfo.occITS = mITSOcc[lb.getEventID()]; + mcEntry.mcTrackInfo.occTPCV = mMCVtVec[lb.getEventID()].occTPCV; + if (mRecProcStage) { + mcEntry.mcTrackInfo.setAddedAtRecStage(); + } + if (o2::mcutils::MCTrackNavigator::isPhysicalPrimary(mcPart, *mCurrMCTracks)) { + mcEntry.mcTrackInfo.setPrimary(); + } + int moth = -1; + o2::MCCompLabel mclbPar; + if ((moth = mcPart.getMotherTrackId()) >= 0) { + const auto& mcPartPar = (*mCurrMCTracks)[moth]; + mcEntry.mcTrackInfo.pdgParent = mcPartPar.GetPdgCode(); + } + if (mcPart.isPrimary() && mcReader.getNEvents(lb.getSourceID()) == mMCVtVec.size()) { + mMCVtVec[lb.getEventID()].nTrackSel++; + } + if (mVerbose > 1) { + LOGP(info, "Adding charged MC pdg={} {} ", mcPart.GetPdgCode(), lb.asString()); + } + return true; +} + +bool TrackMCStudy::refitV0(int i, o2::dataformats::V0& v0, const o2::globaltracking::RecoContainer& recoData) +{ + const auto& id = recoData.getV0sIdx()[i]; + auto seedP = recoData.getTrackParam(id.getProngID(0)); + auto seedN = recoData.getTrackParam(id.getProngID(1)); + bool isTPConly = (id.getProngID(0).getSource() == GTrackID::TPC) || (id.getProngID(1).getSource() == GTrackID::TPC); + const auto& svparam = o2::vertexing::SVertexerParams::Instance(); + if (svparam.mTPCTrackPhotonTune && isTPConly) { + mFitterV0.setMaxDZIni(svparam.mTPCTrackMaxDZIni); + mFitterV0.setMaxDXYIni(svparam.mTPCTrackMaxDXYIni); + mFitterV0.setMaxChi2(svparam.mTPCTrackMaxChi2); + mFitterV0.setCollinear(true); + } + int nCand = mFitterV0.process(seedP, seedN); + if (svparam.mTPCTrackPhotonTune && isTPConly) { // restore + // Reset immediately to the defaults + mFitterV0.setMaxDZIni(svparam.maxDZIni); + mFitterV0.setMaxDXYIni(svparam.maxDXYIni); + mFitterV0.setMaxChi2(svparam.maxChi2); + mFitterV0.setCollinear(false); + } + if (nCand == 0) { // discard this pair + return false; + } + const int cand = 0; + if (!mFitterV0.isPropagateTracksToVertexDone(cand) && !mFitterV0.propagateTracksToVertex(cand)) { + return false; + } + const auto& trPProp = mFitterV0.getTrack(0, cand); + const auto& trNProp = mFitterV0.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + auto p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + const auto& pv = recoData.getPrimaryVertex(id.getVertexID()); + const auto v0XYZ = mFitterV0.getPCACandidatePos(cand); + float dx = v0XYZ[0] - pv.getX(), dy = v0XYZ[1] - pv.getY(), dz = v0XYZ[2] - pv.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + new (&v0) o2::dataformats::V0(v0XYZ, pV0, mFitterV0.calcPCACovMatrixFlat(cand), trPProp, trNProp); + v0.setDCA(mFitterV0.getChi2AtPCACandidate(cand)); + v0.setCosPA(cosPA); + return true; +} + +void TrackMCStudy::loadTPCOccMap(const o2::globaltracking::RecoContainer& recoData) +{ + auto NHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + const auto& TPCOccMap = recoData.occupancyMapTPC; + auto prop = o2::base::Propagator::Instance(); + auto TPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, &mTPCCorrMapsLoader, prop->getNominalBz(), + recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), TPCOccMap.data(), TPCOccMap.size(), nullptr, prop); + mNTPCOccBinLength = TPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOcc.clear(); + if (mNTPCOccBinLength > 1 && TPCOccMap.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = NHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOcc.resize(nTPCOccBins); + mTBinClOccHist.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + for (int i = 0; i < nTPCOccBins; i++) { + mTBinClOccHist[i] = TPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mTBinClOccHist[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mTBinClOccHist[i + sumBins]; + } + mTBinClOcc[i] = sm; + } + } else { + mTBinClOcc.resize(1); + mTBinClOccHist.resize(1); + } +} + +void TrackMCStudy::processITSTracks(const o2::globaltracking::RecoContainer& recoData) +{ + if (!mITSDict) { + LOGP(warn, "ITS data is not loaded"); + return; + } + const auto itsTracks = recoData.getITSTracks(); + const auto itsLbls = recoData.getITSTracksMCLabels(); + const auto itsClRefs = recoData.getITSTracksClusterRefs(); + const auto clusITS = recoData.getITSClusters(); + const auto patterns = recoData.getITSClustersPatterns(); + const auto& params = o2::trackstudy::TrackMCStudyConfig::Instance(); + auto pattIt = patterns.begin(); + mITSClustersArray.clear(); + mITSClustersArray.reserve(clusITS.size()); + + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + auto geom = o2::its::GeometryTGeo::Instance(); + int ntr = itsLbls.size(); + LOGP(info, "We have {} ITS clusters and the number of patterns is {}, ITSdict:{} NMCLabels: {}", clusITS.size(), patterns.size(), mITSDict != nullptr, itsLbls.size()); + + std::vector evord(ntr); + std::iota(evord.begin(), evord.end(), 0); + std::sort(evord.begin(), evord.end(), [&](int i, int j) { return itsLbls[i] < itsLbls[j]; }); + std::vector outHitInfo; + std::array cl2arr{}; + + for (int itr0 = 0; itr0 < ntr; itr0++) { + auto itr = evord[itr0]; + const auto& itsTr = itsTracks[itr]; + const auto& itsLb = itsLbls[itr]; + // LOGP(info,"proc {} {} {}",itr0, itr, itsLb.asString()); + int nCl = itsTr.getNClusters(); + if (itsLb.isFake() || nCl < params.minITSClForITSoutput) { + continue; + } + auto entrySel = mSelMCTracks.find(itsLb); + if (entrySel == mSelMCTracks.end()) { + continue; + } + outHitInfo.clear(); + cl2arr.fill(-1); + auto clEntry = itsTr.getFirstClusterEntry(); + for (int iCl = nCl; iCl--;) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[itsClRefs[clEntry + iCl]]; + int hpos = outHitInfo.size(); + auto& hinf = outHitInfo.emplace_back(); + hinf.clus = cls; + hinf.clus.setCount(geom->getLayer(cls.getSensorID())); + geom->getSensorXAlphaRefPlane(cls.getSensorID(), hinf.chipX, hinf.chipAlpha); + cl2arr[hinf.clus.getCount()] = hpos; // to facilitate finding the cluster of the layer + } + auto trspan = mcReader.getTrackRefs(itsLb.getSourceID(), itsLb.getEventID(), itsLb.getTrackID()); + int ilrc = -1, nrefAcc = 0; + for (const auto& trf : trspan) { + if (trf.getDetectorId() != 0) { // process ITS only + continue; + } + int lrt = trf.getUserId(); // layer of the reference, but there might be multiple hits on the same layer + int clEnt = cl2arr[lrt]; + if (clEnt < 0) { + continue; + } + auto& hinf = outHitInfo[clEnt]; + float traX, traY; + o2::math_utils::rotateZInv(trf.X(), trf.Y(), traX, traY, std::sin(hinf.chipAlpha), std::cos(hinf.chipAlpha)); // tracking coordinates of the reference + if (hinf.trefXT < 1 || std::abs(traX - hinf.chipX) < std::abs(hinf.trefXT - hinf.chipX)) { + if (hinf.trefXT < 1) { + nrefAcc++; + } + hinf.tref = trf; + hinf.trefXT = traX; + hinf.trefYT = traY; + } + } + (*mDBGOut) << "itsTree" << "hits=" << outHitInfo << "trIn=" << ((o2::track::TrackParCov&)itsTr) << "trOut=" << itsTr.getParamOut() << "mcTr=" << entrySel->second.mcTrackInfo.track << "mcPDG=" << entrySel->second.mcTrackInfo.pdg << "nTrefs=" << nrefAcc << "\n"; + } +} + +DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) +{ + std::vector outputs; + Options opts{ + {"device-verbosity", VariantType::Int, 0, {"Verbosity level"}}, + {"dcay-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"min-tpc-clusters", VariantType::Int, 60, {"Cut on TPC clusters"}}, + {"max-tpc-dcay", VariantType::Float, 2.f, {"Cut on TPC dcaY"}}, + {"max-tpc-dcaz", VariantType::Float, 2.f, {"Cut on TPC dcaZ"}}, + {"min-x-prop", VariantType::Float, 6.f, {"track should be propagated to this X at least"}}}; + auto dataRequest = std::make_shared(); + bool useMC = true; + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + if (checkSV) { + dataRequest->requestSecondaryVertices(useMC); + } + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "track-mc-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, sclOpts, checkSV)}, + opts}; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx new file mode 100644 index 0000000000000..300e69c514ae3 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyConfig.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudyConfig.h" + +O2ParamImpl(o2::trackstudy::TrackMCStudyConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx new file mode 100644 index 0000000000000..b6236b7bf0e73 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudyTypes.cxx @@ -0,0 +1,142 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" + +namespace o2::trackstudy +{ + +RecTrack TrackFamily::dummyRecTrack; + +// get longest number of clusters on consecutive layers +int MCTrackInfo::getNITSClusCont() const +{ + if (nITSCl < 2) { + return nITSCl; + } + int longest = 0, current = 0; + for (int i = 0; i < 7; i++) { + if (pattITSCl & (0x1 << i)) { + if (++current > longest) { + longest = current; + } + } else { + current = 0; + } + } + return longest; +} + +// check how many clusters ITS-TPC afterburner could see (consecutively occupied layers starting from the last one) +int MCTrackInfo::getNITSClusForAB() const +{ + int ncl = 0; + if (nITSCl) { + for (int i = 6; i > 2; i--) { + if (pattITSCl & (0x1 << i)) { + ncl++; + } else { + break; + } + } + } + return ncl; +} + +// lowest ITS layer with cluster +int MCTrackInfo::getLowestITSLayer() const +{ + if (nITSCl) { + for (int i = 0; i < 7; i++) { + if (pattITSCl & (0x1 << i)) { + return i; + } + } + } + return -1; +} + +// highest ITS layer with cluster +int MCTrackInfo::getHighestITSLayer() const +{ + if (nITSCl) { + for (int i = 7; i--;) { + if (pattITSCl & (0x1 << i)) { + return i; + } + } + } + return -1; +} + +o2::track::TrackPar MCTrackInfo::getTrackParTPC(float b, float x) const +{ + o2::track::TrackPar t(track); + int ntri = 0; + while (ntri < 2) { + int sector0 = o2::math_utils::angle2Sector(t.getAlpha()); + if (!t.propagateParamTo(x, b)) { + t.invalidate(); + break; + } + int sector = o2::math_utils::angle2Sector(t.getPhiPos()); + float alpha = o2::math_utils::sector2Angle(sector); + if (!t.rotateParam(alpha)) { + t.invalidate(); + break; + } + if (sector != sector0) { + ntri++; + continue; + } + break; + } + // printf("%s ->\n%s <-\n",track.asString().c_str(), t.asString().c_str()); + return t; +} + +float MCTrackInfo::getTrackParTPCPar(int i, float b, float x) const +{ + auto t = getTrackParTPC(b, x); + return t.isValid() ? t.getParam(i) : -999.; +} + +float MCTrackInfo::getTrackParTPCPhiSec(float b, float x) const +{ + auto t = getTrackParTPC(b, x); + return t.isValid() ? std::atan2(t.getY(), t.getX()) : -999.; +} + +int TrackFamily::getLongestTPCTrackEntry() const +{ + int n = -1, ncl = 0; + int ntr = recTracks.size(); + for (int i = 0; i < ntr; i++) { + if (recTracks[i].nClTPC > ncl) { + ncl = recTracks[i].nClTPC; + n = i; + } + } + return n; +} + +int TrackFamily::getNTPCClones() const +{ + int n = 0; + for (auto& t : recTracks) { + if (t.nClTPC > 0) { + n++; + } + } + return n; +} + +} // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 3a0290a9c0b0d..b8a8f97737b4d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -12,9 +12,11 @@ #include #include #include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "ReconstructionDataFormats/TrackTPCITS.h" #include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GeometryManager.h" #include "SimulationDataFormat/MCEventLabel.h" @@ -24,17 +26,31 @@ #include "DataFormatsFT0/RecPoints.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" #include "ITSMFTBase/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" +#include "GlobalTrackingStudy/TrackInfoExt.h" +#include "GlobalTrackingStudy/TrackMCStudyTypes.h" #include "TPCBase/ParameterElectronics.h" #include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "DataFormatsFT0/RecPoints.h" #include "CommonUtils/TreeStreamRedirector.h" #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/DCA.h" +#include "TPCCalibration/VDriftHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "GPUO2InterfaceRefit.h" +#include "GPUO2ExternalUser.h" // Needed for propper settings in GPUParam.h +#include "GPUParam.h" +#include "GPUParam.inc" +#include "GPUTPCGeometry.h" #include "Steer/MCKinematicsReader.h" +#include "MathUtils/fit.h" +#include namespace o2::trackstudy { @@ -51,11 +67,16 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TrackingStudySpec : public Task +class TrackingStudySpec final : public Task { public: - TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} + TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } ~TrackingStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -65,19 +86,72 @@ class TrackingStudySpec : public Task private: void updateTimeDependentParams(ProcessingContext& pc); + float getDCAYCut(float pt) const; + float getDCAZCut(float pt) const; std::shared_ptr mDataRequest; std::shared_ptr mGGCCDBRequest; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; + o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; bool mUseMC{false}; ///< MC flag std::unique_ptr mDBGOut; - float mITSROFrameLengthMUS = 0.; + std::unique_ptr mDBGOutVtx; + std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction + std::vector mMltHistTB, mTBinClOccAft, mTBinClOccBef, mTBinClOccWgh; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting/preceding from the TB = i*mNTPCOccBinLength + std::unique_ptr mOccWghFun; + float mITSROFrameLengthMUS = 0.f; + float mTPCTBinMUS = 0.f; // TPC bin in microseconds + float mTPCTBinMUSInv = 0.f; + int mMaxNeighbours = 3; + float mMaxVTTimeDiff = 80.; // \mus + float mTPCDCAYCut = 2.; + float mTPCDCAZCut = 2.; + float mMinX = 46.; + float mMaxEta = 0.8; + float mMinPt = 0.1; + int mNOccBinsDrift = 10; + int mMinTPCClusters = 60; + int mNTPCOccBinLength = 0; ///< TPC occ. histo bin length in TBs + int mNHBPerTF = 0; + float mNTPCOccBinLengthInv; + bool mStoreWithITSOnly = false; + bool mDoPairsCorr = false; + std::string mDCAYFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; + std::string mDCAZFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; GTrackID::mask_t mTracksSrc{}; + o2::dataformats::MeanVertexObject mMeanVtx{}; o2::steer::MCKinematicsReader mcReader; // reader of MC information }; void TrackingStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mDBGOut = std::make_unique("trackStudy.root", "recreate"); + mTPCCorrMapsLoader.init(ic); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + std::string dbgnm = maxLanes == 1 ? "trackStudy.root" : fmt::format("trackStudy_{}.root", lane); + mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); + dbgnm = maxLanes == 1 ? "trackStudyVtx.root" : fmt::format("trackStudyVtx_{}.root", lane); + mDBGOutVtx = std::make_unique(dbgnm.c_str(), "recreate"); + mStoreWithITSOnly = ic.options().get("with-its-only"); + mMaxVTTimeDiff = ic.options().get("max-vtx-timediff"); + mMaxNeighbours = ic.options().get("max-vtx-neighbours"); + mTPCDCAYCut = ic.options().get("max-tpc-dcay"); + mTPCDCAZCut = ic.options().get("max-tpc-dcaz"); + mMinX = ic.options().get("min-x-prop"); + mMaxEta = ic.options().get("max-eta"); + mMinPt = ic.options().get("min-pt"); + mMinTPCClusters = ic.options().get("min-tpc-clusters"); + mDCAYFormula = ic.options().get("dcay-vs-pt"); + mDCAZFormula = ic.options().get("dcaz-vs-pt"); + mDoPairsCorr = ic.options().get("pair-correlations"); + mNOccBinsDrift = ic.options().get("noccbins"); + if (mNOccBinsDrift < 3) { + mNOccBinsDrift = 3; + } + auto str = ic.options().get("occ-weight-fun"); + if (!str.empty()) { + mOccWghFun = std::make_unique("occFun", str.c_str(), -100., 100.); + } } void TrackingStudySpec::run(ProcessingContext& pc) @@ -85,12 +159,58 @@ void TrackingStudySpec::run(ProcessingContext& pc) o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + if (recoData.inputsTPCclusters) { + mTPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, &mTPCCorrMapsLoader, o2::base::Propagator::Instance()->getNominalBz(), + recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), recoData.occupancyMapTPC.data(), + recoData.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); + mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 + mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; + mTBinClOccBef.clear(); + mTBinClOccAft.clear(); + mTBinClOccWgh.clear(); + } + + // prepare TPC occupancy data + if (mNTPCOccBinLength > 1 && recoData.occupancyMapTPC.size()) { + mNTPCOccBinLengthInv = 1. / mNTPCOccBinLength; + int nTPCBins = mNHBPerTF * o2::constants::lhc::LHCMaxBunches / 8, ninteg = 0; + int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); + mTBinClOccAft.resize(nTPCOccBins); + mTBinClOccBef.resize(nTPCOccBins); + float sm = 0., tb = 0.5 * mNTPCOccBinLength; + mMltHistTB.resize(nTPCOccBins); + for (int i = 0; i < nTPCOccBins; i++) { + mMltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); + tb += mNTPCOccBinLength; + } + for (int i = nTPCOccBins; i--;) { + sm += mMltHistTB[i]; + if (i + sumBins < nTPCOccBins) { + sm -= mMltHistTB[i + sumBins]; + } + mTBinClOccAft[i] = sm; + } + sm = 0; + for (int i = 0; i < nTPCOccBins; i++) { + sm += mMltHistTB[i]; + if (i - sumBins > 0) { + sm -= mMltHistTB[i - sumBins]; + } + mTBinClOccBef[i] = sm; + } + } else { + mTBinClOccBef.resize(1); + mTBinClOccAft.resize(1); + } + process(recoData); } void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + mTPCCorrMapsLoader.extractCCDBInputs(pc); static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -102,6 +222,27 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) } else { mITSROFrameLengthMUS = alpParams.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS ROFrame duration in \mus } + pc.inputs().get("meanvtx"); + mNHBPerTF = o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + auto& elParam = o2::tpc::ParameterElectronics::Instance(); + mTPCTBinMUS = elParam.ZbinWidth; // TPC bin in microseconds + mTPCTBinMUSInv = 1. / mTPCTBinMUS; + } + bool updateMaps = false; + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -111,36 +252,469 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs auto prop = o2::base::Propagator::Instance(); + auto FITInfo = recoData.getFT0RecPoints(); + static int TFCount = 0; + int nv = vtxRefs.size(); + o2::dataformats::PrimaryVertexExt pveDummy; + o2::dataformats::PrimaryVertexExt vtxDummy(mMeanVtx.getPos(), {}, {}, 0); + std::vector pveVec(nv); + std::vector tpcOccAftV, tpcOccBefV; + pveVec.back() = vtxDummy; + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + float tBiasITS = alpParams.roFrameBiasInBC * o2::constants::lhc::LHCBunchSpacingMUS; + const o2::ft0::InteractionTag& ft0Params = o2::ft0::InteractionTag::Instance(); + std::vector trcExtVec; + std::vector trcPairsVec; + auto vdrift = mTPCVDriftHelper.getVDriftObject().getVDrift(); + float maxDriftTB = 250.f / vdrift / (o2::constants::lhc::LHCBunchSpacingMUS * 8); + int groupOcc = std::ceil(maxDriftTB / mNOccBinsDrift / mNTPCOccBinLength); + + bool tpcTrackOK = recoData.isTrackSourceLoaded(GTrackID::TPC); + + auto fillTPCClInfo = [&recoData, this](const o2::tpc::TrackTPC& trc, o2::dataformats::TrackInfoExt& trExt, float timestampTB = -1e9) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto tpcClusAcc = recoData.getTPCClusters(); + const auto shMap = recoData.clusterShMapTPC; + + if (recoData.inputsTPCclusters) { + uint8_t clSect = 0, clRow = 0, lowestR = -1; + uint32_t clIdx = 0; + for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { // outside -> inside ordering, but on the sector boundaries backward jumps are possible + trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); + if (clRow < lowestR) { + trExt.rowCountTPC++; + lowestR = clRow; + } + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[clSect][clRow] + clIdx; + if (shMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + trExt.nClTPCShared++; + } + } + trExt.rowMinTPC = lowestR; + const auto& clus = tpcClusAcc.clusters[clSect][clRow][clIdx]; + trExt.padFromEdge = uint8_t(clus.getPad()); + int npads = o2::gpu::GPUTPCGeometry::NPads(lowestR); + if (trExt.padFromEdge > npads / 2) { + trExt.padFromEdge = npads - 1 - trExt.padFromEdge; + } + this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos0[0], trExt.innerTPCPos0[1], trExt.innerTPCPos0[2], trc.getTime0()); // nominal time of the track + if (timestampTB > -1e8) { + this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos[0], trExt.innerTPCPos[1], trExt.innerTPCPos[2], timestampTB); // time assigned from the global track track + } else { + trExt.innerTPCPos = trExt.innerTPCPos0; + } + trc.getClusterReference(clRefs, 0, clSect, clRow, clIdx); + trExt.rowMaxTPC = clRow; + } + }; + + auto getTPCPairSharing = [&recoData, this](const o2::tpc::TrackTPC& trc0, const o2::tpc::TrackTPC& trc1) { + const auto clRefs = recoData.getTPCTracksClusterRefs(); + uint8_t nsh = 0, nshRows = 0, lastSharedRow = -1; + if (recoData.inputsTPCclusters) { + uint8_t clSect0 = 0, clRow0 = 0, clSect1 = 0, clRow1 = 0; + uint32_t clIdx0 = 0, clIdx1 = 0; + int ic1Start = 0; + for (int ic0 = 0; ic0 < trc0.getNClusterReferences(); ic0++) { // outside -> inside, but on the sector boundaries backward jumps are possible + trc0.getClusterReference(clRefs, ic0, clSect0, clRow0, clIdx0); + for (int ic1 = ic1Start; ic1 < trc1.getNClusterReferences(); ic1++) { // outside -> inside, but on the sector boundaries backward jumps are possible + trc1.getClusterReference(clRefs, ic1, clSect1, clRow1, clIdx1); + if (clRow1 > clRow0) { + ic1Start = ic1 + 1; + continue; // catch up ic0 + } + if (clRow1 == clRow0) { + if (clSect0 == clSect1 && clIdx0 == clIdx1) { + nsh++; + if (lastSharedRow != clRow0) { + lastSharedRow = clRow0; + nshRows++; + } + ic1Start = ic1 + 1; + break; // check next ic0 + } + } + } + } + } + return std::make_pair(nsh, nshRows); + }; + + auto assignRecTrack = [&recoData, this](const o2::dataformats::TrackInfoExt& src, o2::trackstudy::RecTrack& dst) { + dst.track = src.track; + dst.gid = src.gid; + dst.ts.setTimeStamp(src.ttime); + dst.ts.setTimeStampError(src.ttimeE); + dst.nClITS = src.nClITS; + dst.nClTPC = src.nClTPC; + dst.pattITS = src.pattITS; + if (src.q2ptITS == 0. && dst.nClITS > 0) { + dst.pattITS |= 0x1 << 7; + } + dst.lowestPadRow = src.rowMinTPC; + if (this->mUseMC) { + auto gidSet = recoData.getSingleDetectorRefs(src.gid); + if (recoData.getTrackMCLabel(src.gid).isFake()) { + dst.flags |= RecTrack::FakeGLO; + } + auto msk = src.gid.getSourceDetectorsMask(); + if (msk[DetID::ITS]) { + if (gidSet[GTrackID::ITS].isSourceSet()) { // has ITS track rather than AB tracklet + auto lblITS = recoData.getTrackMCLabel(gidSet[GTrackID::ITS]); + if (lblITS.isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } else { // AB ITS tracklet + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSAB]).isFake()) { + dst.flags |= RecTrack::FakeITS; + } + } + if (msk[DetID::TPC]) { // has both ITS and TPC contribution + if (recoData.getTrackMCLabel(gidSet[GTrackID::ITSTPC]).isFake()) { + dst.flags |= RecTrack::FakeITSTPC; + } + } + } + if (msk[DetID::TPC]) { + if (recoData.getTrackMCLabel(gidSet[GTrackID::TPC]).isFake()) { + dst.flags |= RecTrack::FakeTPC; + } + } + } + }; + tpcOccAftV.resize(mNOccBinsDrift); + tpcOccBefV.resize(mNOccBinsDrift); - int nv = vtxRefs.size() - 1; for (int iv = 0; iv < nv; iv++) { + LOGP(debug, "processing PV {} of {}", iv, nv); const auto& vtref = vtxRefs[iv]; - auto pv = pvvec[iv]; - for (int is = 0; is < GTrackID::NSources; is++) { - if (!GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS]) { - continue; + if (iv != nv - 1) { + auto& pve = pveVec[iv]; + static_cast(pve) = pvvec[iv]; + // find best matching FT0 signal + float bestTimeDiff = 1000, bestTime = -999; + int bestFTID = -1; + if (mTracksSrc[GTrackID::FT0]) { + for (int ift0 = vtref.getFirstEntryOfSource(GTrackID::FT0); ift0 < vtref.getFirstEntryOfSource(GTrackID::FT0) + vtref.getEntriesOfSource(GTrackID::FT0); ift0++) { + const auto& ft0 = FITInfo[trackIndex[ift0]]; + if (ft0Params.isSelected(ft0)) { + auto fitTime = ft0.getInteractionRecord().differenceInBCMUS(recoData.startIR); + if (std::abs(fitTime - pve.getTimeStamp().getTimeStamp()) < bestTimeDiff) { + bestTimeDiff = fitTime - pve.getTimeStamp().getTimeStamp(); + bestFTID = trackIndex[ift0]; + } + } + } + } else { + LOGP(warn, "FT0 is not requested, cannot set complete vertex info"); + } + if (bestFTID >= 0) { + pve.FT0A = FITInfo[bestFTID].getTrigger().getAmplA(); + pve.FT0C = FITInfo[bestFTID].getTrigger().getAmplC(); + pve.FT0Time = double(FITInfo[bestFTID].getInteractionRecord().differenceInBCMUS(recoData.startIR)) + FITInfo[bestFTID].getCollisionTimeMean() * 1e-6; // time in \mus } - int idMin = vtxRefs[iv].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[iv].getEntriesOfSource(is); + pve.VtxID = iv; + } + trcExtVec.clear(); + trcPairsVec.clear(); + float q2ptITS, q2ptTPC, q2ptITSTPC, q2ptITSTPCTRD; + for (int is = 0; is < GTrackID::NSources; is++) { + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + bool skipTracks = !mTracksSrc[is] || !recoData.isTrackSourceLoaded(is) || !(dm[DetID::ITS] || dm[DetID::TPC]); + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); for (int i = idMin; i < idMax; i++) { auto vid = trackIndex[i]; bool pvCont = vid.isPVContributor(); + if (pvCont) { + pveVec[iv].nSrc[is]++; + } + if (skipTracks) { + continue; + } + GTrackID tpcTrID; + const o2::tpc::TrackTPC* tpcTr = nullptr; + int nclTPC = 0; + if (dm[DetID::TPC] && tpcTrackOK) { + tpcTrID = recoData.getTPCContributorGID(vid); + tpcTr = &recoData.getTPCTrack(tpcTrID); + nclTPC = tpcTr->getNClusters(); + if (nclTPC < mMinTPCClusters) { + continue; + } + } bool ambig = vid.isAmbiguous(); auto trc = recoData.getTrackParam(vid); + if (fabs(trc.getEta()) > mMaxEta) { + continue; + } + if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z + float corz = vdrift * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + if (tpcTr->hasASideClustersOnly()) { + corz = -corz; // A-side + } + trc.setZ(trc.getZ() + corz); + } float xmin = trc.getX(); o2::dataformats::DCA dca; - if (!prop->propagateToDCA(pv, trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) { + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) { + continue; + } + bool hasITS = GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS]; + if (std::abs(dca.getY()) > (hasITS ? getDCAYCut(trc.getPt()) : mTPCDCAYCut) || + std::abs(dca.getZ()) > (hasITS ? getDCAZCut(trc.getPt()) : mTPCDCAZCut)) { + continue; + } + if (trc.getPt() < mMinPt) { continue; } - (*mDBGOut) << "dca" - << "gid=" << vid << "pv=" << pv << "trc=" << trc << "pvCont=" << pvCont << "ambig=" << ambig << "dca=" << dca << "xmin=" << xmin << "\n"; + if (iv != nv - 1) { + pveVec[iv].nSrcA[is]++; + if (ambig) { + pveVec[iv].nSrcAU[is]++; + } + } + if (!hasITS && mStoreWithITSOnly) { + continue; + } + { + auto& trcExt = trcExtVec.emplace_back(); + recoData.getTrackTime(vid, trcExt.ttime, trcExt.ttimeE); + trcExt.track = trc; + trcExt.hashIU = trc.hash(); + trcExt.dca = dca; + trcExt.gid = vid; + trcExt.xmin = xmin; + trcExt.dcaTPC.set(-999.f, -999.f); + + if (tpcTr) { + float tsuse = trcExt.ttime / (8 * o2::constants::lhc::LHCBunchSpacingMUS); + if (tpcTr->hasASideClusters()) { + trcExt.setTPCA(); + } + if (tpcTr->hasCSideClusters()) { + trcExt.setTPCC(); + } + if (is == GTrackID::TPC) { + trcExt.dcaTPC = dca; + tsuse = -1e9; + } else { + o2::track::TrackParCov tmpTPC(*tpcTr); + if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z + float corz = vdrift * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + if (tpcTr->hasASideClustersOnly()) { + corz = -corz; // A-side + } + tmpTPC.setZ(tmpTPC.getZ() + corz); + } + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], tmpTPC, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &trcExt.dcaTPC)) { + trcExt.dcaTPC.set(-999.f, -999.f); + } + } + fillTPCClInfo(*tpcTr, trcExt, tsuse); + trcExt.chi2TPC = tpcTr->getChi2(); + } + auto gidRefs = recoData.getSingleDetectorRefs(vid); + if (gidRefs[GTrackID::ITS].isIndexSet()) { + const auto& itsTr = recoData.getITSTrack(gidRefs[GTrackID::ITS]); + trcExt.q2ptITS = itsTr.getQ2Pt(); + trcExt.nClITS = itsTr.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTr.hasHitOnLayer(il)) { + trcExt.pattITS |= 0x1 << il; + } + } + } else if (gidRefs[GTrackID::ITSAB].isIndexSet()) { + const auto& itsTrf = recoData.getITSABRefs()[gidRefs[GTrackID::ITSAB]]; + trcExt.nClITS = itsTrf.getNClusters(); + for (int il = 0; il < 7; il++) { + if (itsTrf.hasHitOnLayer(il)) { + trcExt.pattITS |= 0x1 << il; + } + } + } + if (gidRefs[GTrackID::TPC].isIndexSet()) { + trcExt.q2ptTPC = recoData.getTrackParam(gidRefs[GTrackID::TPC]).getQ2Pt(); + trcExt.nClTPC = nclTPC; + } + if (gidRefs[GTrackID::ITSTPC].isIndexSet()) { + const auto& trTPCITS = recoData.getTPCITSTrack(gidRefs[GTrackID::ITSTPC]); + trcExt.q2ptITSTPC = trTPCITS.getQ2Pt(); + trcExt.chi2ITSTPC = trTPCITS.getChi2Match(); + } + if (gidRefs[GTrackID::TRD].isIndexSet()) { + trcExt.q2ptITSTPCTRD = recoData.getTrackParam(gidRefs[GTrackID::TRD]).getQ2Pt(); + } + if (gidRefs[GTrackID::TOF].isIndexSet()) { + trcExt.infoTOF = recoData.getTOFMatch(vid); + } + } + } + } + float tpcOccBef = 0., tpcOccAft = 0.; + if (iv != nv - 1) { + int tb = pveVec[iv].getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv; + tpcOccBef = tb < 0 ? mTBinClOccBef[0] : (tb >= mTBinClOccBef.size() ? mTBinClOccBef.back() : mTBinClOccBef[tb]); + tpcOccAft = tb < 0 ? mTBinClOccAft[0] : (tb >= mTBinClOccAft.size() ? mTBinClOccAft.back() : mTBinClOccAft[tb]); + int tbc = pveVec[iv].getTimeStamp().getTimeStamp() * mTPCTBinMUSInv * mNTPCOccBinLengthInv - groupOcc / 2.; + for (int iob = 0; iob < mNOccBinsDrift; iob++) { + float sm = 0; + for (int ig = 0; ig < groupOcc; ig++) { + int ocb = tbc + ig + groupOcc * iob; + if (ocb < 0 || ocb >= (int)mMltHistTB.size()) { + sm = -1; + break; + } + sm += mMltHistTB[ocb]; + } + tpcOccAftV[iob] = sm; + // + sm = 0; + for (int ig = 0; ig < groupOcc; ig++) { + int ocb = tbc + ig - groupOcc * iob; + if (ocb < 0 || ocb >= (int)mMltHistTB.size()) { + sm = -1; + break; + } + sm += mMltHistTB[ocb]; + } + tpcOccBefV[iob] = sm; + } + } + (*mDBGOut) << "trpv" + << "orbit=" << recoData.startIR.orbit << "tfID=" << TFCount + << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft + << "tpcOccBefV=" << tpcOccBefV << "tpcOccAftV=" << tpcOccAftV + << "pve=" << pveVec[iv] << "trc=" << trcExtVec << "\n"; + + if (mDoPairsCorr) { + for (int it0 = 0; it0 < (int)trcExtVec.size(); it0++) { + const auto& tr0 = trcExtVec[it0]; + if (tr0.nClTPC < 1) { + continue; + } + for (int it1 = it0 + 1; it1 < (int)trcExtVec.size(); it1++) { + const auto& tr1 = trcExtVec[it1]; + if (tr1.nClTPC < 1) { + continue; + } + + if (std::abs(tr0.track.getTgl() - tr1.track.getTgl()) > 0.25) { + continue; + } + auto dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + if (std::abs(dphi) > 0.25) { + continue; + } + auto& pr = trcPairsVec.emplace_back(); + assignRecTrack(tr0, pr.tr0); + assignRecTrack(tr1, pr.tr1); + auto shinfo = getTPCPairSharing(recoData.getTPCTrack(recoData.getTPCContributorGID(tr0.gid)), recoData.getTPCTrack(recoData.getTPCContributorGID(tr1.gid))); + pr.nshTPC = shinfo.first; + pr.nshTPCRow = shinfo.second; + } + } + (*mDBGOut) << "pairs" << "pr=" << trcPairsVec << "\n"; + } + } + + int nvtot = mMaxNeighbours < 0 ? -1 : (int)pveVec.size(); + + auto insSlot = [maxSlots = mMaxNeighbours](std::vector& vc, float v, int slot, std::vector& vid, int id) { + for (int i = maxSlots - 1; i > slot; i--) { + std::swap(vc[i], vc[i - 1]); + std::swap(vid[i], vid[i - 1]); + } + vc[slot] = v; + vid[slot] = id; + }; + + for (int cnt = 0; cnt < nvtot; cnt++) { + const auto& pve = pveVec[cnt]; + float tv = pve.getTimeStamp().getTimeStamp(); + std::vector pveT(mMaxNeighbours); // neighbours in time + std::vector pveZ(mMaxNeighbours); // neighbours in Z + std::vector idT(mMaxNeighbours), idZ(mMaxNeighbours); + std::vector dT(mMaxNeighbours), dZ(mMaxNeighbours); + for (int i = 0; i < mMaxNeighbours; i++) { + idT[i] = idZ[i] = -1; + dT[i] = mMaxVTTimeDiff; + dZ[i] = 1e9; + } + int cntM = cnt - 1, cntP = cnt + 1; + for (; cntM >= 0; cntM--) { // backward + const auto& vt = pveVec[cntM]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + insSlot(dT, dtime, i, idT, cntM); + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + insSlot(dZ, dz, i, idZ, cntM); + break; + } + } + } + for (; cntP < nvtot; cntP++) { // forward + const auto& vt = pveVec[cntP]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + insSlot(dT, dtime, i, idT, cntP); + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + insSlot(dZ, dz, i, idZ, cntP); + break; + } + } + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idT[i] != -1) { + pveT[i] = pveVec[idT[i]]; + } else { + break; } } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idZ[i] != -1) { + pveZ[i] = pveVec[idZ[i]]; + } else { + break; + } + } + (*mDBGOutVtx) << "pvExt" + << "pve=" << pve + << "pveT=" << pveT + << "pveZ=" << pveZ + << "tfID=" << TFCount + << "\n"; } + + TFCount++; } void TrackingStudySpec::endOfStream(EndOfStreamContext& ec) { mDBGOut.reset(); + mDBGOutVtx.reset(); } void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -148,16 +722,40 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { return; } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } } -DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) +float TrackingStudySpec::getDCAYCut(float pt) const +{ + static TF1 fun("dcayvspt", mDCAYFormula.c_str(), 0, 20); + return fun.Eval(pt); +} + +float TrackingStudySpec::getDCAZCut(float pt) const +{ + static TF1 fun("dcazvspt", mDCAZFormula.c_str(), 0, 20); + return fun.Eval(pt); +} + +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector outputs; auto dataRequest = std::make_shared(); dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertertices(useMC); + dataRequest->requestPrimaryVertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF @@ -167,12 +765,31 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); + Options opts{ + {"max-vtx-neighbours", VariantType::Int, 3, {"Max PV neighbours fill, no PV study if < 0"}}, + {"max-vtx-timediff", VariantType::Float, 90.f, {"Max PV time difference to consider"}}, + {"dcay-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"dcaz-vs-pt", VariantType::String, "0.0105 + 0.0350 / pow(x, 1.1)", {"Formula for global tracks DCAy vs pT cut"}}, + {"min-tpc-clusters", VariantType::Int, 60, {"Cut on TPC clusters"}}, + {"max-tpc-dcay", VariantType::Float, 5.f, {"Cut on TPC dcaY"}}, + {"max-tpc-dcaz", VariantType::Float, 5.f, {"Cut on TPC dcaZ"}}, + {"max-eta", VariantType::Float, 1.0f, {"Cut on track eta"}}, + {"min-pt", VariantType::Float, 0.1f, {"Cut on track pT"}}, + {"with-its-only", VariantType::Bool, false, {"Store tracks with ITS only"}}, + {"pair-correlations", VariantType::Bool, false, {"Do pairs correlation"}}, + {"occ-weight-fun", VariantType::String, "(x>=-40&&x<-5) ? (1./1225*pow(x+40,2)) : ((x>-5&&x<15) ? 1. : ((x>=15&&x<40) ? (-0.4/25*x+1.24 ) : ( (x>40&&x<100) ? -0.4/60*x+0.6+0.8/3 : 0)))", {"Occupancy weighting f-n vs time in musec"}}, + {"noccbins", VariantType::Int, 10, {"Number of occupancy bins per full drift time"}}, + {"min-x-prop", VariantType::Float, 100.f, {"track should be propagated to this X at least"}}, + }; + o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + return DataProcessorSpec{ "track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, - Options{}}; + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC, sclOpts)}, + opts}; } } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx b/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx new file mode 100644 index 0000000000000..0ae0fd3bda96a --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/V0Ext.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// class for extended V0 info (for debugging) + +#include "GlobalTrackingStudy/V0Ext.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx new file mode 100644 index 0000000000000..b8230b59405d8 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResid.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"enable-mc", o2::framework::VariantType::Bool, false, {"enable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, "ITS", {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + // o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + // auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); + auto useMC = configcontext.options().get("enable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + + specs.emplace_back(o2::checkresid::getCheckResidSpec(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx new file mode 100644 index 0000000000000..7e104b82f4854 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx @@ -0,0 +1,78 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/SVStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"ignore-tpc-occ", VariantType::Bool, false, {"do not fill TPC occupancy (needs TPC clusters)"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + auto useMC = !configcontext.options().get("disable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls{}; + bool fillTPCOcc = !configcontext.options().get("ignore-tpc-occ"); + if (fillTPCOcc) { + srcCls = srcCls | GID::getSourcesMask("TPC"); + } + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // S-vertex is always needed + specs.emplace_back(o2::svstudy::getSVStudySpec(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx index f8ecc6883462b..3e92178c81b7d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx @@ -20,6 +20,8 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -41,6 +43,7 @@ void customize(std::vector& workflowOptions) {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -58,18 +61,20 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - auto useMC = !configcontext.options().get("disable-mc"); - if (!useMC) { - LOG(info) << "this task requires MC info, enforcing it"; - useMC = true; - } + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); - + if (sclOpt.requestCTPLumi) { + srcTrc = srcTrc | GID::getSourcesMask("CTP"); + srcCls = srcCls | GID::getSourcesMask("CTP"); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC)); + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC, sclOpt)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx new file mode 100644 index 0000000000000..7aa53e2190a9e --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/TrackMCStudy.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"ignore-sv-check", VariantType::Bool, false, {"disable check for SV combinatorics"}}, + {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation, never use it"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + auto useMC = !configcontext.options().get("disable-mc"); + auto checkSV = !configcontext.options().get("ignore-sv-check"); + if (!useMC) { + throw std::runtime_error("MC cannot be disabled for this workflow"); + } + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS,TPC"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + srcCls |= GID::getSourcesMask("ITS,TPC"); + if (sclOpt.requestCTPLumi) { + srcTrc = srcTrc | GID::getSourcesMask("CTP"); + srcCls = srcCls | GID::getSourcesMask("CTP"); + } + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, true); // P-vertex is always needed + if (checkSV) { + o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); + } + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } + + specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, sclOpt, checkSV)); + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +} diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index 2e9e67ea7251b..413d2e63653fc 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -20,6 +20,8 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -38,9 +40,10 @@ void customize(std::vector& workflowOptions) std::vector options{ {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, - {"cluster-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of cluster sources to use"}}, + {"cluster-sources", VariantType::String, "TPC,TOF", {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -58,15 +61,21 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); auto useMC = !configcontext.options().get("disable-mc"); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); - + if (sclOpt.requestCTPLumi) { + srcCls = srcCls | GID::getSourcesMask("CTP"); + } + if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC)); + + specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC, sclOpt)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx deleted file mode 100644 index 18b26355ff5ec..0000000000000 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "TOFWorkflow/RecoWorkflowSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/DataRefUtils.h" -#include "Framework/Lifetime.h" -#include "Framework/Task.h" -#include "Framework/SerializationMethods.h" -#include "Headers/DataHeader.h" -#include "DataFormatsTOF/Cluster.h" -#include "GlobalTracking/MatchTOF.h" -#include "ReconstructionDataFormats/TrackTPCITS.h" -#include "DetectorsBase/GeometryManager.h" -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GRPGeomHelper.h" -#include "CommonUtils/NameConf.h" -#include -#include "TStopwatch.h" -#include "TPCCalibration/VDriftHelper.h" - -// from FIT -#include "DataFormatsFT0/RecPoints.h" - -#include // for make_shared, make_unique, unique_ptr -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace tof -{ - -// use the tasking system of DPL -// just need to implement 2 special methods init + run (there is no need to inherit from anything) -class TOFDPLRecoWorkflowTask -{ - using evIdx = o2::dataformats::EvIndex; - using MatchOutputType = std::vector; - - bool mUseMC = true; - bool mUseFIT = false; - - public: - explicit TOFDPLRecoWorkflowTask(std::shared_ptr gr, bool useMC, bool useFIT) : mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT) {} - - void init(framework::InitContext& ic) - { - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTimer.Stop(); - mTimer.Reset(); - } - - void run(framework::ProcessingContext& pc) - { - mTimer.Start(false); - updateTimeDependentParams(pc); - //>>>---------- attach input data --------------->>> - const auto clustersRO = pc.inputs().get>("tofcluster"); - const auto tracksRO = pc.inputs().get>("globaltrack"); - - if (mUseFIT) { - // Note: the particular variable will go out of scope, but the span is passed by copy to the - // worker and the underlying memory is valid throughout the whole computation - auto recPoints = std::move(pc.inputs().get>("fitrecpoints")); - mMatcher.setFITRecPoints(recPoints); - LOG(info) << "TOF Reco Workflow pulled " << recPoints.size() << " FIT RecPoints"; - } - - // we do a copy of the input but we are looking for a way to avoid it (current problem in conversion form unique_ptr to *) - - gsl::span itstpclab; - o2::dataformats::MCTruthContainer toflab; - if (mUseMC) { - const auto toflabel = pc.inputs().get*>("tofclusterlabel"); - itstpclab = pc.inputs().get>("itstpclabel"); - toflab = std::move(*toflabel); - } - - mMatcher.run(tracksRO, clustersRO, toflab, itstpclab); - - // in run_match_tof aggiugnere esplicitamente la chiamata a fill del tree (nella classe MatchTOF) e il metodo per leggere i vettori di output - - //... - // LOG(info) << "TOF CLUSTERER : TRANSFORMED " << digits->size() - // << " DIGITS TO " << mClustersArray.size() << " CLUSTERS"; - - // send matching-info - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector()); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMATCHTOF", 0, Lifetime::Timeframe}, mMatcher.getMatchedTOFLabelsVector()); - } - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe}, mMatcher.getCalibVector()); - mTimer.Stop(); - } - - void endOfStream(EndOfStreamContext& ec) - { - LOGF(info, "TOF Matching total timing: Cpu: %.3e Real: %.3e s in %d slots", - mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); - } - - void updateTimeDependentParams(ProcessingContext& pc) - { - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - mTPCVDriftHelper.extractCCDBInputs(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - // put here init-once stuff - } - // we may have other params which need to be queried regularly - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getSourceName()); - mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - } - } - - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) - { - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - return; - } - } - - private: - o2::globaltracking::MatchTOF mMatcher; ///< Cluster finder - std::shared_ptr mGGCCDBRequest; - o2::tpc::VDriftHelper mTPCVDriftHelper{}; - TStopwatch mTimer; -}; - -o2::framework::DataProcessorSpec getTOFRecoWorkflowSpec(bool useMC, bool useFIT) -{ - std::vector inputs; - std::vector outputs; - inputs.emplace_back("tofcluster", o2::header::gDataOriginTOF, "CLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("globaltrack", "GLO", "TPCITS", 0, Lifetime::Timeframe); - if (useMC) { - inputs.emplace_back("tofclusterlabel", o2::header::gDataOriginTOF, "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("itstpclabel", "GLO", "TPCITS_MC", 0, Lifetime::Timeframe); - } - - if (useFIT) { - inputs.emplace_back("fitrecpoints", o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); - } - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - true, // GRPMagField - true, // askMatLUT - o2::base::GRPGeomRequest::Aligned, // geometry - inputs, - true); - o2::tpc::VDriftHelper::requestCCDBInputs(inputs); - - outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_ITSTPC", 0, Lifetime::Timeframe); - if (useMC) { - outputs.emplace_back(o2::header::gDataOriginTOF, "MCMATCHTOF", 0, Lifetime::Timeframe); - } - outputs.emplace_back(o2::header::gDataOriginTOF, "CALIBDATA", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "TOFRecoWorkflow", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC, useFIT)}, - Options{ - {"material-lut-path", VariantType::String, "", {"Path of the material LUT file"}}}}; -} - -} // end namespace tof -} // end namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx index 14186497fc77a..27a48d93dac8b 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx @@ -74,8 +74,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto listname = cfgc.options().get("collection-infile"); auto toftpc = cfgc.options().get("tpc-matches"); - char* stringTBP = new char[listname.size()]; - sprintf(stringTBP, "%s", listname.c_str()); + char* stringTBP = new char[listname.size() + 1]; + snprintf(stringTBP, listname.size() + 1, "%s", listname.c_str()); // the lane configuration defines the subspecification ids to be distributed among the lanes. // auto tofSectors = o2::RangeTokenizer::tokenize(cfgc.options().get("tof-sectors")); diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx index e16145b33113e..7c8e41a92f931 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-reco-workflow.cxx @@ -32,6 +32,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "Framework/CompletionPolicyHelpers.h" +#include "TOFWorkflowIO/TOFCalibWriterSpec.h" #include #include @@ -71,6 +72,7 @@ void customize(std::vector& workflowOptions) {"orbits-per-tf", VariantType::Int, -1, {"default(-1) from GRP/CCDB, a valid value is required to run compressor for epn"}}, {"calib-cluster", VariantType::Bool, false, {"to enable calib info production from clusters"}}, {"for-calib", VariantType::Bool, false, {"to disable check on problematic, otherwise masked for new calibrations"}}, + {"calib-dia", VariantType::Bool, false, {"enabling writing calib for diagnostics"}}, {"cosmics", VariantType::Bool, false, {"to enable cosmics utils"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -147,6 +149,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto orbitPerTF = cfgc.options().get("orbits-per-tf"); auto ccdb_url = o2::base::NameConf::getCCDBServer(); auto isForCalib = cfgc.options().get("for-calib"); + auto writingDia = cfgc.options().get("calib-dia"); LOG(debug) << "TOF RECO WORKFLOW configuration"; LOG(debug) << "TOF input = " << cfgc.options().get("input-type"); @@ -205,6 +208,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } } + if (writingDia) { + specs.emplace_back(o2::tof::getTOFCalibWriterSpec("o2calib_tof.root", false, true, true)); + } + LOG(debug) << "Number of active devices = " << specs.size(); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt index c8db0209d4471..09ec6081b06b8 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/CMakeLists.txt @@ -9,6 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# add_compile_options(-O0 -g -fPIC -fno-omit-frame-pointer) + o2_add_library(TPCInterpolationWorkflow SOURCES src/TPCInterpolationSpec.cxx src/TPCResidualWriterSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h index ac19f41ac3354..83dbb1bd0f5fe 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCInterpolationSpec.h @@ -38,7 +38,8 @@ namespace tpc class TPCInterpolationDPL : public Task { public: - TPCInterpolationDPL(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput) {} + TPCInterpolationDPL(std::shared_ptr dr, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr gr, bool useMC, + bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), mGGCCDBRequest(gr), mUseMC(useMC), mProcessITSTPConly(processITSTPConly), mSendTrackData(sendTrackData), mDebugOutput(debugOutput), mExtDetResid(extDetResid) {} ~TPCInterpolationDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -52,17 +53,22 @@ class TPCInterpolationDPL : public Task std::shared_ptr mGGCCDBRequest; o2::tpc::VDriftHelper mTPCVDriftHelper{}; const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + o2::dataformats::GlobalTrackID::mask_t mSources{}; ///< which input sources are configured + o2::dataformats::GlobalTrackID::mask_t mSourcesMap{}; ///< possible subset of mSources specifically for map creation bool mUseMC{false}; ///< MC flag bool mProcessITSTPConly{false}; ///< should also tracks without outer point (ITS-TPC only) be processed? bool mProcessSeeds{false}; ///< process not only most complete track, but also its shorter parts bool mDebugOutput{false}; ///< add more information to the output (track points of ITS, TRD and TOF) + bool mExtDetResid{true}; ///< produce unbinned residuals for external detectors bool mSendTrackData{false}; ///< if true, not only the clusters but also corresponding track data will be sent uint32_t mSlotLength{600u}; ///< the length of one calibration slot required to calculate max number of tracks per TF + int mMatCorr{2}; ///< the material correction to be used for track interpolation TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput); +framework::DataProcessorSpec getTPCInterpolationSpec(o2::dataformats::GlobalTrackID::mask_t srcCls, o2::dataformats::GlobalTrackID::mask_t srcVtx, o2::dataformats::GlobalTrackID::mask_t srcTrk, + o2::dataformats::GlobalTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid); } // namespace tpc } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h index a20f437984a5c..99f20e390a09a 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h @@ -19,6 +19,7 @@ #include "DetectorsCalibration/Utils.h" #include "SpacePoints/TrackResiduals.h" #include "SpacePoints/ResidualAggregator.h" +#include "TPCCalibration/VDriftHelper.h" #include "CommonUtils/MemFileHelper.h" #include "Framework/Task.h" #include "Framework/ConfigParamRegistry.h" @@ -101,22 +102,35 @@ class ResidualAggregatorDevice : public o2::framework::Task void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { - o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } } void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { + // new run is starting + LOG(info) << "New run start detected"; + mRunStopRequested = false; + mInitDone = false; + } + if (mRunStopRequested) { + return; + } auto runStartTime = std::chrono::high_resolution_clock::now(); o2::globaltracking::RecoContainer recoCont; recoCont.collectData(pc, *mDataRequest); updateTimeDependentParams(pc); std::chrono::duration ccdbUpdateTime = std::chrono::high_resolution_clock::now() - runStartTime; - // assume that the orbit reset time (given here in ms) can change within a run - auto orbitResetTime = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(); - - // we always require the unbinned residuals and the associated track references + // we always require the unbinned residuals and the associated detector info and track references auto residualsData = pc.inputs().get>("unbinnedRes"); + auto residualsDataDet = pc.inputs().get>("detinfoRes"); auto trackRefs = pc.inputs().get>("trackRefs"); // track data input is optional @@ -132,37 +146,57 @@ class ResidualAggregatorDevice : public o2::framework::Task using lumiDataType = std::decay_t(""))>; std::optional lumiInput; if (mCTPInput) { - recoCont.getCTPLumi(); lumiInput = recoCont.getCTPLumi(); lumi = &lumiInput.value(); } o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mAggregator->getCurrentTFInfo()); - LOG(info) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; - mAggregator->process(residualsData, trackRefs, orbitResetTime, trkDataPtr, lumi); + LOG(detail) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; + mAggregator->process(residualsData, residualsDataDet, trackRefs, trkDataPtr, lumi); std::chrono::duration runDuration = std::chrono::high_resolution_clock::now() - runStartTime; LOGP(debug, "Duration for run method: {} ms. From this taken for time dependent param update: {} ms", std::chrono::duration_cast(runDuration).count(), std::chrono::duration_cast(ccdbUpdateTime).count()); + if (pc.transitionState() == TransitionHandlingState::Requested) { + LOG(info) << "Run stop requested, finalizing"; + mRunStopRequested = true; + mAggregator->checkSlotsToFinalize(); + mAggregator.reset(); + LOG(info) << "Finalizing done"; + } } void endOfStream(o2::framework::EndOfStreamContext& ec) final { + if (mRunStopRequested) { + return; + } LOG(info) << "Finalizing calibration for end of stream"; mAggregator->checkSlotsToFinalize(); mAggregator.reset(); // must invoke destructor manually here, otherwise we get a segfault + LOG(info) << "Finalizing done for end of stream"; } private: void updateTimeDependentParams(ProcessingContext& pc) { o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { - initOnceDone = true; + mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mAggregator->setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); + mTPCVDriftHelper.acknowledgeUpdate(); + } + if (!mInitDone) { mAggregator->setDataTakingContext(pc.services().get()); + mAggregator->setOrbitResetTime(o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS()); + mInitDone = true; } } + o2::tpc::VDriftHelper mTPCVDriftHelper{}; std::unique_ptr mAggregator; ///< the TimeSlotCalibration device std::shared_ptr mCCDBRequest; std::shared_ptr mDataRequest; ///< optional CTP input @@ -171,6 +205,8 @@ class ResidualAggregatorDevice : public o2::framework::Task bool mWriteBinnedResiduals{false}; ///< flag, whether to write binned residuals to output file bool mWriteUnbinnedResiduals{false}; ///< flag, whether to write unbinned residuals to output file bool mWriteTrackData{false}; ///< flag, whether to write track data to output file + bool mRunStopRequested{false}; ///< flag in case the run was stopped + bool mInitDone{false}; ///< flag whether initialization was done for current run }; } // namespace calibration @@ -185,7 +221,9 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b dataRequest->requestClusters(GID::getSourcesMask("CTP"), false); } auto& inputs = dataRequest->inputs; + o2::tpc::VDriftHelper::requestCCDBInputs(inputs); inputs.emplace_back("unbinnedRes", "GLO", "UNBINNEDRES"); + inputs.emplace_back("detinfoRes", "GLO", "DETINFORES"); inputs.emplace_back("trackRefs", "GLO", "TRKREFS"); if (trackInput) { inputs.emplace_back("trkData", "GLO", "TRKDATA"); @@ -196,7 +234,8 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b false, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry - inputs); + inputs, + true); return DataProcessorSpec{ "residual-aggregator", inputs, diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h index 6c40bb355eb21..724151c90576f 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h @@ -43,6 +43,7 @@ class TPCUnbinnedResidualReader : public o2::framework::Task std::string mInFileName; std::string mInTreeName; std::vector mUnbinnedResid, *mUnbinnedResidPtr = &mUnbinnedResid; + std::vector mDetInfoUnbRes, *mDetInfoUnbResPtr = &mDetInfoUnbRes; std::vector mTrackData, *mTrackDataPtr = &mTrackData; std::vector mTrackDataCompact, *mTrackDataCompactPtr = &mTrackDataCompact; }; diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index 976fd48492ac4..4912a1df36a33 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -51,6 +51,10 @@ void TPCInterpolationDPL::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mSlotLength = ic.options().get("sec-per-slot"); mProcessSeeds = ic.options().get("process-seeds"); + mMatCorr = ic.options().get("matCorrType"); + if (mProcessSeeds && mSources != mSourcesMap) { + LOG(fatal) << "process-seeds option is not compatible with using different track sources for vDrift and map extraction"; + } } void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) @@ -62,17 +66,22 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) initOnceDone = true; // other init-once stuff const auto& param = SpacePointsCalibConfParam::Instance(); - mInterpolation.init(); + mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); + mInterpolation.setNHBPerTF(o2::base::GRPGeomHelper::getNHBFPerTF()); + mInterpolation.init(mSources, mSourcesMap); + if (mProcessITSTPConly) { + mInterpolation.setProcessITSTPConly(); + } int nTfs = mSlotLength / (o2::base::GRPGeomHelper::getNHBFPerTF() * o2::constants::lhc::LHCOrbitMUS * 1e-6); bool limitTracks = (param.maxTracksPerCalibSlot < 0) ? false : true; int nTracksPerTfMax = (nTfs > 0 && limitTracks) ? param.maxTracksPerCalibSlot / nTfs : -1; if (nTracksPerTfMax > 0) { LOGP(info, "We will stop processing tracks after validating {} tracks per TF, since we want to accumulate {} tracks for a slot with {} TFs", nTracksPerTfMax, param.maxTracksPerCalibSlot, nTfs); - if (param.additionalTracksITSTPC > 0) { - int nITSTPCadd = param.additionalTracksITSTPC / nTfs; - LOGP(info, "In addition up to {} ITS-TPC tracks are processed per TF", nITSTPCadd); - mInterpolation.setAddITSTPCTracksPerTF(nITSTPCadd); + if (param.additionalTracksMap > 0) { + int nTracksAdditional = param.additionalTracksMap / nTfs; + LOGP(info, "In addition up to {} additional tracks are processed per TF", nTracksAdditional); + mInterpolation.setAddTracksForMapPerTF(nTracksAdditional); } } else if (nTracksPerTfMax < 0) { LOG(info) << "The number of processed tracks per TF is not limited"; @@ -80,7 +89,16 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) LOG(error) << "No tracks will be processed. maxTracksPerCalibSlot must be greater than slot length in TFs"; } mInterpolation.setMaxTracksPerTF(nTracksPerTfMax); + mInterpolation.setMatCorr(static_cast(mMatCorr)); + if (mProcessSeeds) { + mInterpolation.setProcessSeeds(); + } o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)); + mInterpolation.setExtDetResid(mExtDetResid); + mInterpolation.setITSClusterDictionary(mITSDict); + if (mDebugOutput) { + mInterpolation.setDumpTrackPoints(); + } } // we may have other params which need to be queried regularly if (mTPCVDriftHelper.isUpdated()) { @@ -91,10 +109,6 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) mInterpolation.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); } - if (mDebugOutput) { - mInterpolation.setDumpTrackPoints(); - mInterpolation.setITSClusterDictionary(mITSDict); - } } void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -114,122 +128,30 @@ void TPCInterpolationDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) void TPCInterpolationDPL::run(ProcessingContext& pc) { - LOG(info) << "TPC Interpolation Workflow initialized. Start processing..."; mTimer.Start(false); RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); - const auto& param = SpacePointsCalibConfParam::Instance(); - - // load the input tracks - std::vector gidTables; - std::vector seeds; - std::vector trkTimes; - std::vector gids; - std::unordered_map trkCounters; - // make sure the map has entries for every possible track input type - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTRDTOF, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTRD, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPCTOF, 0)); - trkCounters.insert(std::make_pair(GTrackID::Source::ITSTPC, 0)); - - // so that the flags can be used inside the lambda - bool processITSTPConly = mProcessITSTPConly; - bool processSeeds = mProcessSeeds; - // the creator goes from most complete track (ITS-TPC-TRD-TOF) to least complete one (ITS-TPC) - auto creator = [&gidTables, &seeds, &trkTimes, &recoData, &processITSTPConly, &processSeeds, &gids, ¶m, &trkCounters](auto& _tr, GTrackID _origID, float t0, float tErr) { - if constexpr (std::is_base_of_v>) { - bool trackGood = true; - bool hasOuterPoint = false; - auto gidTable = recoData.getSingleDetectorRefs(_origID); - if (!gidTable[GTrackID::ITS].isIndexSet() || !gidTable[GTrackID::TPC].isIndexSet()) { - // ITS and TPC track is always needed. At this stage ITS afterburner tracks are also rejected - return true; - } - if (gidTable[GTrackID::TRD].isIndexSet()) { - // TRD specific cuts - const auto& trdTrk = recoData.getITSTPCTRDTrack(gidTable[GTrackID::ITSTPCTRD]); - if (trdTrk.getNtracklets() < param.minTRDNTrklts) { - trackGood = false; - } - hasOuterPoint = true; - } - if (gidTable[GTrackID::TOF].isIndexSet()) { - // TOF specific cuts (if any) - hasOuterPoint = true; - } - const auto itstpcTrk = &recoData.getTPCITSTrack(gidTable[GTrackID::ITSTPC]); - const auto itsTrk = &recoData.getITSTrack(gidTable[GTrackID::ITS]); - const auto tpcTrk = &recoData.getTPCTrack(gidTable[GTrackID::TPC]); - // apply track quality cuts - if (itsTrk->getChi2() / itsTrk->getNumberOfClusters() > param.maxITSChi2 || tpcTrk->getChi2() / tpcTrk->getNClusterReferences() > param.maxTPCChi2) { - // reduced chi2 cut is the same for all track types - trackGood = false; - } - if (!hasOuterPoint) { - // ITS-TPC track (does not have outer points in TRD or TOF) - if (!processITSTPConly) { - return true; - } - if (itsTrk->getNumberOfClusters() < param.minITSNClsNoOuterPoint || tpcTrk->getNClusterReferences() < param.minTPCNClsNoOuterPoint) { - trackGood = false; - } - if (itsTrk->getPt() < param.minPtNoOuterPoint) { - trackGood = false; - } - } else { - if (itsTrk->getNumberOfClusters() < param.minITSNCls || tpcTrk->getNClusterReferences() < param.minTPCNCls) { - trackGood = false; - } - } - if (trackGood) { - trkTimes.push_back(t0); - seeds.emplace_back(itsTrk->getParamOut()); // FIXME: should this not be a refit of the ITS track? - gidTables.emplace_back(gidTable); - gids.push_back(_origID); - trkCounters[_origID.getSource()] += 1; - } - if (processSeeds || (gidTable[GTrackID::TRD].isIndexSet() && gidTable[GTrackID::TOF].isIndexSet())) { - // for ITS-TPC-TRD-TOF tracks we are interested also in the ITS-TPC-TRD part - // we want to have both available, so return false for the full barrell tracks - return false; - } else { - return true; - } - } else { - return false; - } - }; - recoData.createTracksVariadic(creator); // create track sample considered for interpolation - LOGP(info, "Created {} seeds. {} ITS-TPC-TRD-TOF, {} ITS-TPC-TRD, {} ITS-TPC-TOF, {} ITS-TPC", - seeds.size(), trkCounters.at(GTrackID::Source::ITSTPCTRDTOF), trkCounters.at(GTrackID::Source::ITSTPCTRD), - trkCounters.at(GTrackID::Source::ITSTPCTOF), trkCounters.at(GTrackID::Source::ITSTPC)); - - if (mUseMC) { - // possibly MC labels will be used to check filtering procedure performance before interpolation - // not yet implemented - } - - mInterpolation.process(recoData, gids, gidTables, seeds, trkTimes, trkCounters); + mInterpolation.prepareInputTrackSample(recoData); + mInterpolation.process(); mTimer.Stop(); LOGF(info, "TPC interpolation timing: Cpu: %.3e Real: %.3e s", mTimer.CpuTime(), mTimer.RealTime()); - - if (param.writeUnfiltered) { + if (SpacePointsCalibConfParam::Instance().writeUnfiltered) { // these are the residuals and tracks before outlier rejection; they are not used in production - pc.outputs().snapshot(Output{"GLO", "TPCINT_RES", 0, Lifetime::Timeframe}, mInterpolation.getClusterResidualsUnfiltered()); + pc.outputs().snapshot(Output{"GLO", "TPCINT_RES", 0}, mInterpolation.getClusterResidualsUnfiltered()); if (mSendTrackData) { - pc.outputs().snapshot(Output{"GLO", "TPCINT_TRK", 0, Lifetime::Timeframe}, mInterpolation.getReferenceTracksUnfiltered()); + pc.outputs().snapshot(Output{"GLO", "TPCINT_TRK", 0}, mInterpolation.getReferenceTracksUnfiltered()); } } - pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0, Lifetime::Timeframe}, mInterpolation.getClusterResiduals()); - pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0, Lifetime::Timeframe}, mInterpolation.getTrackDataCompact()); + pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mInterpolation.getClusterResiduals()); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mInterpolation.getClusterResidualsDetInfo()); + pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mInterpolation.getTrackDataCompact()); if (mSendTrackData) { - pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0, Lifetime::Timeframe}, mInterpolation.getReferenceTracks()); + pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mInterpolation.getReferenceTracks()); } if (mDebugOutput) { - pc.outputs().snapshot(Output{"GLO", "TRKDATAEXT", 0, Lifetime::Timeframe}, mInterpolation.getTrackDataExtended()); + pc.outputs().snapshot(Output{"GLO", "TRKDATAEXT", 0}, mInterpolation.getTrackDataExtended()); } - mInterpolation.reset(); } @@ -239,7 +161,7 @@ void TPCInterpolationDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput) +DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mask_t srcVtx, GTrackID::mask_t srcTrk, GTrackID::mask_t srcTrkMap, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput, bool extDetResid) { auto dataRequest = std::make_shared(); std::vector outputs; @@ -248,12 +170,13 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool LOG(fatal) << "MC usage must be disabled for this workflow, since it is not yet implemented"; } - dataRequest->requestTracks(src, useMC); - dataRequest->requestClusters(src, useMC); + dataRequest->requestTracks(srcVtx, useMC); + dataRequest->requestClusters(srcCls, useMC); + dataRequest->requestPrimaryVertices(useMC); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true - false, // GRPLHCIF + true, // GRPLHCIF true, // GRPMagField true, // askMatLUT o2::base::GRPGeomRequest::Aligned, // geometry @@ -267,6 +190,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool } } outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (sendTrackData) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); @@ -279,8 +203,9 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t src, bool useMC, bool "tpc-track-interpolation", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput)}, + AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)}, Options{ + {"matCorrType", VariantType::Int, 2, {"material correction type (definition in Propagator.h)"}}, {"sec-per-slot", VariantType::UInt32, 600u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, {"process-seeds", VariantType::Bool, false, {"do not remove duplicates, e.g. for ITS-TPC-TRD track also process its seeding ITS-TPC part"}}}}; } diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx index 23a6c8fd06694..b3040d99bc4f2 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualReaderSpec.cxx @@ -251,7 +251,7 @@ void TPCResidualReader::run(ProcessingContext& pc) mTrackResiduals.closeOutputFile(); // FIXME remove when map output is handled properly // const auto& voxResArray = mTrackResiduals.getVoxelResults(); // array with one vector of results per sector - // pc.outputs().snapshot(Output{"GLO", "VOXELRESULTS", 0, Lifetime::Timeframe}, voxResArray); // send results as one large vector? + // pc.outputs().snapshot(Output{"GLO", "VOXELRESULTS", 0}, voxResArray); // send results as one large vector? pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx index 5f6d7ad7b361c..8b06444bdb9b3 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCResidualWriterSpec.cxx @@ -38,6 +38,7 @@ DataProcessorSpec getTPCResidualWriterSpec(bool writeTrackData, bool debugOutput BranchDefinition>{InputSpec{"tracksUnfiltered", "GLO", "TPCINT_TRK", 0}, "tracksUnfiltered", ((writeUnfiltered && writeTrackData) ? 1 : 0)}, BranchDefinition>{InputSpec{"residualsUnfiltered", "GLO", "TPCINT_RES", 0}, "residualsUnfiltered", (writeUnfiltered ? 1 : 0)}, BranchDefinition>{InputSpec{"residuals", "GLO", "UNBINNEDRES"}, "residuals"}, + BranchDefinition>{InputSpec{"detInfo", "GLO", "DETINFORES"}, "detInfo"}, BranchDefinition>{InputSpec{"trackRefs", "GLO", "TRKREFS"}, "trackRefs"}, BranchDefinition>{InputSpec{"tracks", "GLO", "TRKDATA"}, "tracks", (writeTrackData ? 1 : 0)}, BranchDefinition>{InputSpec{"trackExt", "GLO", "TRKDATAEXT"}, "trackExt", (debugOutput ? 1 : 0)})(); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx index af50b32bc8bba..c2dae375731a4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCUnbinnedResidualReaderSpec.cxx @@ -44,6 +44,11 @@ void TPCUnbinnedResidualReader::connectTree() assert(mTreeIn); mTreeIn->SetBranchAddress("residuals", &mUnbinnedResidPtr); mTreeIn->SetBranchAddress("trackRefs", &mTrackDataCompactPtr); + if (mTreeIn->GetBranch("detInfo")) { + mTreeIn->SetBranchAddress("detInfo", &mDetInfoUnbResPtr); + } else { + LOGP(warn, "No detInfo branch found in the unbinned residuals tree, empty vector will be sent"); + } if (mTrackInput) { mTreeIn->SetBranchAddress("tracks", &mTrackDataPtr); } @@ -56,11 +61,12 @@ void TPCUnbinnedResidualReader::run(ProcessingContext& pc) assert(currEntry < mTreeIn->GetEntries()); // this should not happen mTreeIn->GetEntry(currEntry); LOG(info) << "Pushing " << mUnbinnedResid.size() << " unbinned residuals at entry " << currEntry; - pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0, Lifetime::Timeframe}, mUnbinnedResid); - pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0, Lifetime::Timeframe}, mTrackDataCompact); + pc.outputs().snapshot(Output{"GLO", "UNBINNEDRES", 0}, mUnbinnedResid); + pc.outputs().snapshot(Output{"GLO", "TRKREFS", 0}, mTrackDataCompact); + pc.outputs().snapshot(Output{"GLO", "DETINFORES", 0}, mDetInfoUnbRes); if (mTrackInput) { LOG(info) << "Pushing " << mTrackData.size() << " reference tracks for these residuals"; - pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0, Lifetime::Timeframe}, mTrackData); + pc.outputs().snapshot(Output{"GLO", "TRKDATA", 0}, mTrackData); } if (mTreeIn->GetReadEntry() + 1 >= mTreeIn->GetEntries()) { @@ -73,6 +79,7 @@ DataProcessorSpec getUnbinnedTPCResidualsReaderSpec(bool trkInput) { std::vector outputs; outputs.emplace_back("GLO", "UNBINNEDRES", 0, Lifetime::Timeframe); + outputs.emplace_back("GLO", "DETINFORES", 0, Lifetime::Timeframe); outputs.emplace_back("GLO", "TRKREFS", 0, Lifetime::Timeframe); if (trkInput) { outputs.emplace_back("GLO", "TRKDATA", 0, Lifetime::Timeframe); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx index 2af94a7614e70..2f28fc5bb2d34 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -37,10 +37,12 @@ void customize(std::vector& workflowOptions) {"disable-root-input", VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", VariantType::Bool, false, {"disable root-files output writers"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"enable-itsonly", VariantType::Bool, false, {"process tracks without outer point (ITS-TPC only)"}}, - {"tracking-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use for tracking"}}, + {"vtx-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources used for the vertex finding"}}, + {"tracking-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use for track inter-/extrapolation"}}, + {"tracking-sources-map-extraction", VariantType::String, std::string{GID::ALL}, {"can be subset of \"tracking-sources\""}}, {"send-track-data", VariantType::Bool, false, {"Send also the track information to the aggregator"}}, {"debug-output", VariantType::Bool, false, {"Dump extended tracking information for debugging"}}, + {"skip-ext-det-residuals", VariantType::Bool, false, {"Do not produce residuals for external detectors"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -66,25 +68,52 @@ void customize(std::vector& policies) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; - GID::mask_t allowedSources = GID::getSourcesMask("ITS,TPC,TRD,TOF,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSources = GID::getSourcesMask("ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t srcVtx = allowedSources & GID::getSourcesMask(configcontext.options().get("vtx-sources")); + GID::mask_t srcTracks = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources")); + GID::mask_t srcTracksMap = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources-map-extraction")); + if (srcTracks.count() > srcVtx.count()) { + LOGP(error, "More sources configured for inter-/extrapolation: {} than for vertexing: {}. Additional sources will be ignored", GID::getSourcesNames(srcTracks), GID::getSourcesNames(srcVtx)); + srcTracks &= srcVtx; + } + srcTracksMap &= srcVtx; + if (((srcTracksMap | srcTracks) ^ srcTracks).any()) { + LOGP(fatal, "tracking-sources-map-extraction ({}) must be a subset of tracking-sources ({}).", GID::getSourcesNames(srcTracksMap), GID::getSourcesNames(srcTracks)); + } else if (srcTracksMap != srcTracks) { + LOGP(info, "Will extract residual from different track types. For vDrift from {} and for distortion map from {}", GID::getSourcesNames(srcTracks), GID::getSourcesNames(srcTracksMap)); + } else { + LOGP(info, "Only a single track source is defined for residuals extraction: {}", GID::getSourcesNames(srcTracks)); + } + LOG(debug) << "Data sources for inter-/extrapolation: " << GID::getSourcesNames(srcTracks); + // check first if ITS-TPC tracks were specifically requested from command line + bool processITSTPConly = srcTracks[GID::ITSTPC]; + srcTracks |= GID::getSourcesMask("ITS,TPC,ITS-TPC"); // now add them in any case + srcTracksMap |= GID::getSourcesMask("ITS,TPC,ITS-TPC"); + srcVtx |= srcTracks; + GID::mask_t srcClusters = srcTracks; + if (srcTracks[GID::ITSTPCTRD] || srcTracks[GID::ITSTPCTRDTOF]) { + srcClusters |= GID::getSourcesMask("TRD"); + } + if (srcTracks[GID::ITSTPCTOF] || srcTracks[GID::ITSTPCTRDTOF]) { + srcClusters |= GID::getSourcesMask("TOF"); + } // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); // write the configuration used for the workflow o2::conf::ConfigurableParam::writeINI("o2tpcinterpolation-workflow_configuration.ini"); auto useMC = !configcontext.options().get("disable-mc"); useMC = false; // force disabling MC as long as it is not implemented - auto processITSTPConly = configcontext.options().get("enable-itsonly"); auto sendTrackData = configcontext.options().get("send-track-data"); auto debugOutput = configcontext.options().get("debug-output"); - GID::mask_t src = allowedSources & GID::getSourcesMask(configcontext.options().get("tracking-sources")); - LOG(info) << "Data sources: " << GID::getSourcesNames(src); + auto extDetResid = !configcontext.options().get("skip-ext-det-residuals"); - specs.emplace_back(o2::tpc::getTPCInterpolationSpec(src, useMC, processITSTPConly, sendTrackData, debugOutput)); + specs.emplace_back(o2::tpc::getTPCInterpolationSpec(srcClusters, srcVtx, srcTracks, srcTracksMap, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::tpc::getTPCResidualWriterSpec(sendTrackData, debugOutput)); } - o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcClusters, srcVtx, srcVtx, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx index 402dd5608f61e..20e37c3bcc3b4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx @@ -14,19 +14,27 @@ #include "TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h" #include "TPCInterpolationWorkflow/TPCUnbinnedResidualReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + // we need to add workflow options before including Framework/runDataProcessing void customize(std::vector& workflowOptions) { std::vector options{ - {"output-type", VariantType::String, "binnedResid", {"Comma separated list of outputs (without spaces). Valid strings: unbinnedResid, binnedResid, trackParams"}}, + {"output-type", VariantType::String, "unbinnedResid,trackParams", {"Comma separated list of outputs (without spaces). Valid strings: unbinnedResid, binnedResid, trackParams"}}, {"enable-track-input", VariantType::Bool, false, {"Whether to expect track data from interpolation workflow"}}, {"enable-ctp", VariantType::Bool, false, {"Subscribe to lumi info from CTP"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input readers"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -43,7 +51,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) bool writeUnbinnedResiduals = false; bool writeBinnedResiduals = false; bool writeTrackData = false; - auto outputType = configcontext.options().get("output-type"); + auto outputType = configcontext.options().get("output-type"); std::vector outputTypes; size_t pos = 0; while ((pos = outputType.find(",")) != std::string::npos) { @@ -79,5 +87,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, maskClusters, maskNone, maskNone, false); } + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + return specs; } diff --git a/Detectors/HMPID/README.md b/Detectors/HMPID/README.md index 775f2ad24d77c..3a744b72812ea 100644 --- a/Detectors/HMPID/README.md +++ b/Detectors/HMPID/README.md @@ -8,4 +8,5 @@ This is a top page for the HMPID detector documentation. diff --git a/Detectors/HMPID/base/include/HMPIDBase/Param.h b/Detectors/HMPID/base/include/HMPIDBase/Param.h index 9d5b044e155cf..ce895dd678c8b 100644 --- a/Detectors/HMPID/base/include/HMPIDBase/Param.h +++ b/Detectors/HMPID/base/include/HMPIDBase/Param.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -67,8 +67,10 @@ class Param kPadSigmaMasked = 20 }; // One can go up to 5 sigma cut, overflow is protected in AliHMPIDCalib static float r2d() { return 57.2957795; } - static float sizePadX() { return fgCellX; } // pad size x, [cm] - static float sizePadY() { return fgCellY; } // pad size y, [cm] + static float sizePadX() { return fgCellX; } // pad size x, [cm] + static float sizePadY() { return fgCellY; } // pad size y, [cm] + static float sizeHalfPadX() { return fgHalfCellX; } // half pad size x, [cm] + static float sizeHalfPadY() { return fgHalfCellY; } // half pad size y, [cm] static float sizePcX() { return fgPcX; } // PC size x static float sizePcY() { return fgPcY; } // PC size y @@ -100,8 +102,8 @@ class Param bool getInstType() const { return fgInstanceType; } // return if the instance is from geom or ideal - inline static bool isInDead(float x, float y); // is the point in dead area? - inline static bool isDeadPad(Int_t padx, Int_t pady, Int_t ch); // is a dead pad? + static bool isInDead(float x, float y); // is the point in dead area? + static bool isDeadPad(Int_t padx, Int_t pady, Int_t ch); // is a dead pad? inline void setChStatus(Int_t ch, bool status = kTRUE); inline void setSectStatus(Int_t ch, Int_t sect, bool status); @@ -281,8 +283,8 @@ class Param static Int_t fgThreshold; // sigma Cut static bool fgInstanceType; // kTRUE if from geomatry kFALSE if from ideal geometry - static float fgCellX, fgCellY, fgPcX, fgPcY, fgAllX, fgAllY; // definition of HMPID geometric parameters - Param(bool noGeo); // default ctor is protected to enforce it to be singleton + static float fgCellX, fgCellY, fgHalfCellX, fgHalfCellY, fgPcX, fgPcY, fgAllX, fgAllY; // definition of HMPID geometric parameters + Param(bool noGeo); // default ctor is protected to enforce it to be singleton static Param* fgInstance; // static pointer to instance of Param singleton diff --git a/Detectors/HMPID/base/src/Param.cxx b/Detectors/HMPID/base/src/Param.cxx index 9696067801fd6..3aa8ac0a220e7 100644 --- a/Detectors/HMPID/base/src/Param.cxx +++ b/Detectors/HMPID/base/src/Param.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -48,6 +48,8 @@ bool Param::fgMapPad[160][144][7]; float Param::fgCellX = 0.; float Param::fgCellY = 0.; +float Param::fgHalfCellX = 0.; +float Param::fgHalfCellY = 0.; float Param::fgPcX = 0; float Param::fgPcY = 0; @@ -57,14 +59,14 @@ float Param::fgAllY = 0; bool Param::fgInstanceType = kTRUE; -Param* Param::fgInstance = nullptr; //singleton pointer +Param* Param::fgInstance = nullptr; // singleton pointer Int_t Param::fgNSigmas = 4; Int_t Param::fgThreshold = 4; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mTemp(25) -//just set a refractive index for C6F14 at ephot=6.675 eV @ T=25 C +// just set a refractive index for C6F14 at ephot=6.675 eV @ T=25 C { // Here all the intitializition is taken place when Param::Instance() is invoked for the first time. // In particular, matrices to be used for LORS<->MARS trasnformations are initialized from TGeo structure. @@ -90,12 +92,12 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT } } */ - mRefIdx = meanIdxRad(); //initialization of the running ref. index of freon + mRefIdx = meanIdxRad(); // initialization of the running ref. index of freon float dead = 2.6; // cm of the dead zones between PCs-> See 2CRC2099P1 if (noGeo == kTRUE) { - fgInstanceType = kFALSE; //instance from ideal geometry, no actual geom is present + fgInstanceType = kFALSE; // instance from ideal geometry, no actual geom is present } if (noGeo == kFALSE && !gGeoManager) { @@ -107,6 +109,8 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT fgCellX = 0.8; fgCellY = 0.84; + fgHalfCellX = fgCellX * 0.5; + fgHalfCellY = fgCellY * 0.5; if (!noGeo == kTRUE) { TGeoVolume* pCellVol = gGeoManager->GetVolume("Hcel"); @@ -148,7 +152,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT for (Int_t ich = kMinCh; ich <= kMaxCh; ich++) { for (Int_t padx = 0; padx < 160; padx++) { for (Int_t pady = 0; pady < 144; pady++) { - fgMapPad[padx][pady][ich] = kTRUE; //init all the pads are active at the beginning.... + fgMapPad[padx][pady][ich] = kTRUE; // init all the pads are active at the beginning.... } } } @@ -157,7 +161,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT if (gGeoManager && gGeoManager->IsClosed()) { TGeoPNEntry* pne = gGeoManager->GetAlignableEntry(Form("/HMPID/Chamber%i", i)); if (!pne) { - //AliErrorClass(Form("The symbolic volume %s does not correspond to any physical entry!",Form("HMPID_%i",i))); + // AliErrorClass(Form("The symbolic volume %s does not correspond to any physical entry!",Form("HMPID_%i",i))); mM[i] = new TGeoHMatrix; idealPosition(i, mM[i]); } else { @@ -175,7 +179,7 @@ Param::Param(bool noGeo) : mX(0), mY(0), mRefIdx(1.28947), mPhotEMean(6.675), mT } } fgInstance = this; -} //ctor +} // ctor //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::print(Option_t* opt) const { @@ -184,7 +188,7 @@ void Param::print(Option_t* opt) const for (Int_t i = 0; i < 7; i++) { mM[i]->Print(opt); } -} //Print() +} // Print() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) { @@ -201,46 +205,46 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) case 0: pMatrix->RotateY(kAngHor); pMatrix->RotateZ(-kAngVer); - break; //right and down + break; // right and down case 1: pMatrix->RotateZ(-kAngVer); - break; //down + break; // down case 2: pMatrix->RotateY(kAngHor); - break; //right + break; // right case 3: - break; //no rotation + break; // no rotation case 4: pMatrix->RotateY(-kAngHor); - break; //left + break; // left case 5: pMatrix->RotateZ(kAngVer); - break; //up + break; // up case 6: pMatrix->RotateY(-kAngHor); pMatrix->RotateZ(kAngVer); - break; //left and up + break; // left and up } - pMatrix->RotateZ(kAngCom); //apply common rotation in XY plane + pMatrix->RotateZ(kAngCom); // apply common rotation in XY plane } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ /*Int_t Param::Stack(Int_t evt,Int_t tid) { // Prints some useful info from stack // Arguments: evt - event number. if not -1 print info only for that event -// tid - track id. if not -1 then print it and all it's mothers if any +// tid - track id. if not -1 then print it and all it's mothers if any // Returns: mother tid of the given tid if any - AliRunLoader *pAL=AliRunLoader::Open(); + AliRunLoader *pAL=AliRunLoader::Open(); if(pAL->LoadHeader()) return -1; if(pAL->LoadKinematics()) return -1; - + Int_t mtid=-1; Int_t iNevt=pAL->GetNumberOfEvents(); - + for(Int_t iEvt=0;iEvtGetEvent(iEvt); - AliStack *pStack=pAL->Stack(); + pAL->GetEvent(iEvt); + AliStack *pStack=pAL->Stack(); if(tid==-1){ //print all tids for this event for(Int_t i=0;iGetNtrack();i++) pStack->Particle(i)->Print(); Printf("totally %i tracks including %i primaries for event %i out of %i event(s)", @@ -253,8 +257,8 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) while((tid=pTrack->GetFirstMother()) >= 0){ pTrack=pStack->Particle(tid); str+=" from ";str+=pTrack->GetName(); - } - }//if(tid==-1) + } + }//if(tid==-1) }//events loop pAL->UnloadHeader(); pAL->UnloadKinematics(); return mtid; @@ -263,15 +267,15 @@ void Param::idealPosition(Int_t iCh, TGeoHMatrix* pMatrix) /*Int_t Param::StackCount(Int_t pid,Int_t evt) { // Counts total number of particles of given sort (including secondary) for a given event - AliRunLoader *pAL=AliRunLoader::Open(); - pAL->GetEvent(evt); + AliRunLoader *pAL=AliRunLoader::Open(); + pAL->GetEvent(evt); if(pAL->LoadHeader()) return 0; if(pAL->LoadKinematics()) return 0; AliStack *pStack=pAL->Stack(); - + Int_t iCnt=0; for(Int_t i=0;iGetNtrack();i++) if(pStack->Particle(i)->GetPdgCode()==pid) iCnt++; - + pAL->UnloadHeader(); pAL->UnloadKinematics(); return iCnt; }*/ @@ -290,7 +294,7 @@ double Param::sigma2(double trkTheta, double trkPhi, double ckovTh, double ckovP double trkBeta = 1. / (TMath::Cos(ckovTh) * getRefIdx()); if (trkBeta > 1) { - trkBeta = 1; //protection against bad measured thetaCer + trkBeta = 1; // protection against bad measured thetaCer } if (trkBeta < 0) { trkBeta = 0.0001; // @@ -337,7 +341,7 @@ double Param::sigLoc(double trkTheta, double trkPhi, double thetaC, double phiC, // formula (7) pag.4 double dtdyc = kk * (k * (cosfd * sinf + cost * sinfd * cosf) + (alpha * e / (betaM * betaM)) * sint * sinfd); - double errX = 0.2, errY = 0.25; //end of page 7 + double errX = 0.2, errY = 0.25; // end of page 7 return TMath::Sqrt(errX * errX * dtdxc * dtdxc + errY * errY * dtdyc * dtdyc); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -365,7 +369,7 @@ double Param::sigCrom(double trkTheta, double trkPhi, double thetaC, double phiC double f = 0.0172 * (7.75 - 5.635) / TMath::Sqrt(24.); return f * dtdn; -} //SigCrom() +} // SigCrom() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ double Param::sigGeom(double trkTheta, double trkPhi, double thetaC, double phiC, double betaM) { @@ -404,7 +408,7 @@ double Param::sigGeom(double trkTheta, double trkPhi, double thetaC, double phiC double trErr = radThick() / (TMath::Sqrt(12.) * cost); return trErr * dtdT; -} //SigGeom() +} // SigGeom() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ double Param::sigmaCorrFact(Int_t iPart, double occupancy) { @@ -437,10 +441,10 @@ Param* Param::instance() // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry if (!fgInstance) { - new Param(kFALSE); //default setting for reconstruction, if no geometry.root -> AliFatal + new Param(kFALSE); // default setting for reconstruction, if no geometry.root -> AliFatal } return fgInstance; -} //Instance() +} // Instance() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Param* Param::instanceNoGeo() @@ -449,10 +453,10 @@ Param* Param::instanceNoGeo() // Arguments: none // Returns: pointer to the instance of AliHMPIDParam or 0 if no geometry if (!fgInstance) { - new Param(kTRUE); //to avoid AliFatal, for MOOD and displays, use ideal geometry parameters + new Param(kTRUE); // to avoid AliFatal, for MOOD and displays, use ideal geometry parameters } return fgInstance; -} //Instance() +} // Instance() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bool Param::isInDead(float x, float y) { @@ -461,7 +465,7 @@ bool Param::isInDead(float x, float y) // Returns: 1 if not in sensitive zone for (Int_t iPc = 0; iPc < 6; iPc++) { if (x >= fgkMinPcX[iPc] && x <= fgkMaxPcX[iPc] && y >= fgkMinPcY[iPc] && y <= fgkMaxPcY[iPc]) { - return kFALSE; //in current pc + return kFALSE; // in current pc } } @@ -475,7 +479,7 @@ bool Param::isDeadPad(Int_t padx, Int_t pady, Int_t ch) // Returns: kTRUE if dead, kFALSE if active if (fgMapPad[padx - 1][pady - 1][ch]) { - return kFALSE; //current pad active + return kFALSE; // current pad active } return kTRUE; @@ -490,25 +494,25 @@ void Param::lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) if (x > fgkMinPcX[0] && x < fgkMaxPcX[0]) { pc = 0; px = Int_t(x / sizePadX()); - } //PC 0 or 2 or 4 + } // PC 0 or 2 or 4 else if (x > fgkMinPcX[1] && x < fgkMaxPcX[1]) { pc = 1; px = Int_t((x - fgkMinPcX[1]) / sizePadX()); - } //PC 1 or 3 or 5 + } // PC 1 or 3 or 5 else { return; } if (y > fgkMinPcY[0] && y < fgkMaxPcY[0]) { py = Int_t(y / sizePadY()); - } //PC 0 or 1 + } // PC 0 or 1 else if (y > fgkMinPcY[2] && y < fgkMaxPcY[2]) { pc += 2; py = Int_t((y - fgkMinPcY[2]) / sizePadY()); - } //PC 2 or 3 + } // PC 2 or 3 else if (y > fgkMinPcY[4] && y < fgkMaxPcY[4]) { pc += 4; py = Int_t((y - fgkMinPcY[4]) / sizePadY()); - } //PC 4 or 5 + } // PC 4 or 5 else { return; } @@ -516,9 +520,9 @@ void Param::lors2Pad(float x, float y, Int_t& pc, Int_t& px, Int_t& py) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Int_t Param::inHVSector(float y) { - //Calculate the HV sector corresponding to the cluster position - //Arguments: y - //Returns the HV sector in the single module + // Calculate the HV sector corresponding to the cluster position + // Arguments: y + // Returns the HV sector in the single module Int_t hvsec = -1; Int_t pc, px, py; @@ -535,15 +539,15 @@ Int_t Param::inHVSector(float y) double Param::findTemp(double tLow, double tHigh, double y) { // Model for gradient in temperature - double yRad = hinRad(y); //height in a given radiator + double yRad = hinRad(y); // height in a given radiator if (tHigh < tLow) { - tHigh = tLow; //if Tout < Tin consider just Tin as reference... + tHigh = tLow; // if Tout < Tin consider just Tin as reference... } if (yRad < 0) { - yRad = 0; //protection against fake y values + yRad = 0; // protection against fake y values } if (yRad > sizePcY()) { - yRad = sizePcY(); //protection against fake y values + yRad = sizePcY(); // protection against fake y values } double gradT = (tHigh - tLow) / sizePcY(); // linear gradient @@ -552,9 +556,9 @@ double Param::findTemp(double tLow, double tHigh, double y) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setChStatus(Int_t ch, bool status) { - //Set a chamber on or off depending on the status - //Arguments: ch=chamber,status=kTRUE = active, kFALSE=off - //Returns: none + // Set a chamber on or off depending on the status + // Arguments: ch=chamber,status=kTRUE = active, kFALSE=off + // Returns: none for (Int_t padx = 0; padx < kMaxPcx + 1; padx++) { for (Int_t pady = 0; pady < kMaxPcy + 1; pady++) { fgMapPad[padx][pady][ch] = status; @@ -564,10 +568,10 @@ void Param::setChStatus(Int_t ch, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setSectStatus(Int_t ch, Int_t sect, bool status) { - //Set a given sector sect for a chamber ch on or off depending on the status - //Sector=0,5 (6 sectors) - //Arguments: ch=chamber,sect=sector,status: kTRUE = active, kFALSE=off - //Returns: none + // Set a given sector sect for a chamber ch on or off depending on the status + // Sector=0,5 (6 sectors) + // Arguments: ch=chamber,sect=sector,status: kTRUE = active, kFALSE=off + // Returns: none Int_t npadsect = (kMaxPcy + 1) / 6; Int_t padSectMin = npadsect * sect; @@ -582,9 +586,9 @@ void Param::setSectStatus(Int_t ch, Int_t sect, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setPcStatus(Int_t ch, Int_t pc, bool status) { - //Set a given PC pc for a chamber ch on or off depending on the status - //Arguments: ch=chamber,pc=PC,status: kTRUE = active, kFALSE=off - //Returns: none + // Set a given PC pc for a chamber ch on or off depending on the status + // Arguments: ch=chamber,pc=PC,status: kTRUE = active, kFALSE=off + // Returns: none Int_t deltaX = pc % 2; Int_t deltaY = pc / 2; @@ -602,9 +606,9 @@ void Param::setPcStatus(Int_t ch, Int_t pc, bool status) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::printChStatus(Int_t ch) { - //Print the map status of a chamber on or off depending on the status - //Arguments: ch=chamber - //Returns: none + // Print the map status of a chamber on or off depending on the status + // Arguments: ch=chamber + // Returns: none Printf(" "); Printf(" --------- C H A M B E R %d ---------------", ch); for (Int_t pady = kMaxPcy; pady >= 0; pady--) { @@ -624,9 +628,9 @@ void Param::printChStatus(Int_t ch) //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Param::setGeomAccept() { - //Set the real acceptance of the modules, due to ineficciency or hardware problems (up tp 1/6/2010) - //Arguments: none - //Returns: none + // Set the real acceptance of the modules, due to ineficciency or hardware problems (up tp 1/6/2010) + // Arguments: none + // Returns: none setSectStatus(0, 3, kFALSE); setSectStatus(4, 0, kFALSE); setSectStatus(5, 1, kFALSE); diff --git a/Detectors/HMPID/calibration/README.md b/Detectors/HMPID/calibration/README.md index 674ff2eca1265..a9a08309bb061 100644 --- a/Detectors/HMPID/calibration/README.md +++ b/Detectors/HMPID/calibration/README.md @@ -1,4 +1,8 @@ -#HMPID calibration + + +# HMPID calibration For a local test, use diff --git a/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx b/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx index 55bfe8fc101e7..02852a3213b30 100644 --- a/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx +++ b/Detectors/HMPID/calibration/src/HMPIDDCSProcessor.cxx @@ -395,7 +395,7 @@ double HMPIDDCSProcessor::calculatePhotonEnergy(int i) if (dp.id.get_type() == DeliveryType::DPVAL_DOUBLE) { lambda = o2::dcs::getValue(dp); } else { - LOGP(warn, "DP type is {}", dp.id.get_type()); + LOGP(warn, "DP type is {}", (int)dp.id.get_type()); LOGP(warn, "Not correct datatype for HMP_TRANPLANT_MEASURE_{}_WAVELENGTH --> Default wavelength used for iteration procTrans{}", i, i); lambda = arrWaveLenDefault[i]; } @@ -708,6 +708,7 @@ std::unique_ptr HMPIDDCSProcessor::finalizeHv(int iCh, int iSec) } else { (pHvTF).reset(new TF1(Form("HV%i%i", iCh, iSec), "[0]+x*[1]", hvFirstTime, hvLastTime)); + pGrHV->Fit(Form("HV%i%i", iCh, iSec), "Q"); // ef added } } else { LOGP(warn, "No entries in High Voltage for HV{}{}", iCh, iSec); diff --git a/Detectors/HMPID/reconstruction/CMakeLists.txt b/Detectors/HMPID/reconstruction/CMakeLists.txt index c56e087ba555d..9a4623fd347d2 100644 --- a/Detectors/HMPID/reconstruction/CMakeLists.txt +++ b/Detectors/HMPID/reconstruction/CMakeLists.txt @@ -10,16 +10,20 @@ # or submit itself to any jurisdiction. o2_add_library(HMPIDReconstruction - SOURCES src/Clusterer.cxx - src/HmpidDecoder2.cxx + TARGETVARNAME targetName + SOURCES src/Clusterer.cxx + src/HmpidDecoder2.cxx src/HmpidEquipment.cxx src/CTFHelper.cxx + src/Recon.cxx src/CTFCoder.cxx PUBLIC_LINK_LIBRARIES O2::HMPIDBase O2::DataFormatsHMP + ROOT::Physics O2::HMPIDSimulation) - + o2_target_root_dictionary(HMPIDReconstruction HEADERS include/HMPIDReconstruction/Clusterer.h include/HMPIDReconstruction/HmpidDecoder2.h + include/HMPIDReconstruction/Recon.h include/HMPIDReconstruction/HmpidEquipment.h) diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index d1c825f603e83..894c11864f061 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -23,7 +23,6 @@ #include "DataFormatsHMP/CTF.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" -#include "rANS/rans.h" #include "HMPIDReconstruction/CTFHelper.h" class TTree; @@ -33,10 +32,10 @@ namespace o2 namespace hmpid { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF @@ -85,14 +84,14 @@ o2::ctf::CTFIOSize CTFCoder::encode_impl(VEC& buff, const gsl::spansetHeader(helper.createHeader()); assignDictVersion(static_cast(ec->getHeader())); - ec->getANSHeader().majorVersion = 0; - ec->getANSHeader().minorVersion = 1; + ec->setANSHeader(mANSVersion); // at every encoding the buffer might be autoexpanded, so we don't work with fixed pointer ec o2::ctf::CTFIOSize iosize; -#define ENCODEHMP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)].get(), getMemMarginFactor()); +#define ENCODEHMP(beg, end, slot, bits) CTF::get(buff.data())->encode(beg, end, int(slot), bits, optField[int(slot)], &buff, mCoders[int(slot)], getMemMarginFactor()); // clang-format off iosize += ENCODEHMP(helper.begin_bcIncTrig(), helper.end_bcIncTrig(), CTF::BLC_bcIncTrig, 0); iosize += ENCODEHMP(helper.begin_orbitIncTrig(), helper.end_orbitIncTrig(), CTF::BLC_orbitIncTrig, 0); @@ -136,12 +134,14 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VDIG& di auto header = ec.getHeader(); checkDictVersion(static_cast(header)); ec.print(getPrefix(), mVerbosity); - std::vector bcInc, q; - std::vector orbitInc, entriesDig; + std::vector bcInc; + std::vector orbitInc; + std::vector q; + std::vector entriesDig; std::vector chID, ph, x, y; o2::ctf::CTFIOSize iosize; -#define DECODEHMP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)].get()) +#define DECODEHMP(part, slot) ec.decode(part, int(slot), mCoders[int(slot)]) // clang-format off iosize += DECODEHMP(bcInc, CTF::BLC_bcIncTrig); iosize += DECODEHMP(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h index 094aa7f367b3f..8e7c1823a0340 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFHelper.h @@ -60,7 +60,7 @@ class CTFHelper class _Iter { public: - using difference_type = int64_t; + using difference_type = std::ptrdiff_t; using value_type = T; using pointer = const T*; using reference = const T&; @@ -69,92 +69,86 @@ class CTFHelper _Iter(const gsl::span& data, bool end = false) : mData(data), mIndex(end ? M * data.size() : 0){}; _Iter() = default; - const I& operator++() + inline I& operator++() noexcept { ++mIndex; - return (I&)(*this); + return static_cast(*this); } - const I operator++(int) + inline I operator++(int) { - auto res = *this; + I res = *(static_cast(this)); ++mIndex; return res; } - const I& operator--() + inline I& operator--() noexcept { mIndex--; - return (I&)(*this); + return static_cast(*this); } - const I operator--(int) + inline I operator--(int) { - auto res = *this; + I res = *(static_cast(this)); --mIndex; return res; } - const I& operator+=(difference_type i) + I& operator+=(difference_type i) noexcept { mIndex += i; - return (I&)(*this); + return static_cast(*this); } - const I operator+=(difference_type i) const + I operator+(difference_type i) const { - auto tmp = *const_cast(this); - return tmp += i; + I res = *(const_cast(static_cast(this))); + return res += i; } - const I& operator-=(difference_type i) + I& operator-=(difference_type i) noexcept { mIndex -= i; - return (I&)(*this); + return static_cast(*this); } - const I operator-=(difference_type i) const + I operator-(difference_type i) const { - auto tmp = *const_cast(this); - return tmp -= i; + I res = *(const_cast(static_cast(this))); + return res -= i; } - difference_type operator-(const I& other) const { return mIndex - other.mIndex; } + difference_type operator-(const I& other) const noexcept { return mIndex - other.mIndex; } - difference_type operator-(size_t idx) const { return mIndex - idx; } + inline friend I operator+(difference_type i, const I& iter) { return iter + i; }; - const I& operator-(size_t idx) - { - mIndex -= idx; - return (I&)(*this); - } - - bool operator!=(const I& other) const { return mIndex != other.mIndex; } - bool operator==(const I& other) const { return mIndex == other.mIndex; } - bool operator>(const I& other) const { return mIndex > other.mIndex; } - bool operator<(const I& other) const { return mIndex < other.mIndex; } - bool operator>=(const I& other) const { return mIndex >= other.mIndex; } - bool operator<=(const I& other) const { return mIndex <= other.mIndex; } + bool operator!=(const I& other) const noexcept { return mIndex != other.mIndex; } + bool operator==(const I& other) const noexcept { return mIndex == other.mIndex; } + bool operator>(const I& other) const noexcept { return mIndex > other.mIndex; } + bool operator<(const I& other) const noexcept { return mIndex < other.mIndex; } + bool operator>=(const I& other) const noexcept { return mIndex >= other.mIndex; } + bool operator<=(const I& other) const noexcept { return mIndex <= other.mIndex; } protected: gsl::span mData{}; - size_t mIndex = 0; + difference_type mIndex = 0; }; //_______________________________________________ // BC difference wrt previous if in the same orbit, otherwise the abs.value. // For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_bcIncTrig : public _Iter + class Iter_bcIncTrig : public _Iter { public: - using _Iter::_Iter; + using _Iter::_Iter; value_type operator*() const { if (mIndex) { if (mData[mIndex].getOrbit() == mData[mIndex - 1].getOrbit()) { - return mData[mIndex].getBc() - mData[mIndex - 1].getBc(); + return value_type(mData[mIndex].getBc() - mData[mIndex - 1].getBc()); } else { - return mData[mIndex].getBc(); + return value_type(mData[mIndex].getBc()); } } return 0; @@ -164,9 +158,9 @@ class CTFHelper size_t id = mIndex + i; if (id) { if (mData[id].getOrbit() == mData[id - 1].getOrbit()) { - return mData[id].getBc() - mData[id - 1].getBc(); + return value_type(mData[id].getBc() - mData[id - 1].getBc()); } else { - return mData[id].getBc(); + return value_type(mData[id].getBc()); } } return 0; @@ -175,15 +169,15 @@ class CTFHelper //_______________________________________________ // Orbit difference wrt previous. For the very 1st entry return 0 (diff wrt 1st BC in the CTF header) - class Iter_orbitIncTrig : public _Iter + class Iter_orbitIncTrig : public _Iter { public: - using _Iter::_Iter; - value_type operator*() const { return mIndex ? mData[mIndex].getOrbit() - mData[mIndex - 1].getOrbit() : 0; } + using _Iter::_Iter; + value_type operator*() const { return value_type(mIndex ? mData[mIndex].getOrbit() - mData[mIndex - 1].getOrbit() : 0); } value_type operator[](difference_type i) const { size_t id = mIndex + i; - return id ? mData[id].getOrbit() - mData[id - 1].getOrbit() : 0; + return value_type(id ? mData[id].getOrbit() - mData[id - 1].getOrbit() : 0); } }; diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h index 35ee3e311f363..fb2fabfd9ce8c 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Clusterer.h @@ -22,6 +22,8 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "SimulationDataFormat/MCCompLabel.h" +#include "TMatrixF.h" // ef: added + namespace o2 { @@ -47,7 +49,7 @@ class Clusterer static void Dig2Clu(gsl::span digs, std::vector& clus, float* pUserCut, bool isUnfold = kTRUE); // digits->clusters static void FormClu(Cluster& pClu, int pDig, gsl::span digs, TMatrixF& pDigMap); // cluster formation recursive algorithm static int UseDig(int padX, int padY, TMatrixF& pDigMap); // use this pad's digit to form a cluster - inline bool IsDigSurvive(Digit* pDig) const; // check for sigma cut + inline bool IsDigSurvive(Digit* pDig) const; // check for sigma cut private: // void processChamber(std::vector& clusters, MCLabelContainer const* digitMCTruth); diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h deleted file mode 100644 index e92e8375ad0d0..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWFILE_H_ -#define COMMON_HMPIDDECODERAWFILE_H_ - -#include -#include -#include -#include -#include -#include - -#include "HMPIDReconstruction/HmpidDecoder.h" - -#define MAXFILENAMEBUFFER 512 -#define MAXRAWFILEBUFFER RAWBLOCKDIMENSION_W * 4 + 8 - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawFile : public HmpidDecoder -{ - public: - HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawFile(int numOfEquipments); - ~HmpidDecodeRawFile(); - - bool setUpStream(void* InpuFileName, long Size); - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); - bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); - int fileExists(char* filewithpath); - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge); - - private: - FILE* fh; - char mInputFile[MAXFILENAMEBUFFER]; - uint32_t mFileBuffer[MAXRAWFILEBUFFER]; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h deleted file mode 100644 index d5d82d0f238e9..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWMEM_H_ -#define COMMON_HMPIDDECODERAWMEM_H_ - -#include -#include -#include -#include -#include -#include - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecoder.h" - -using namespace o2; - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawMem : public HmpidDecoder -{ - public: - HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawMem(int numOfEquipments); - ~HmpidDecodeRawMem(); - - bool setUpStream(void* Buffer, long BufferLen) override; - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size) override; - bool getHeaderFromStream(uint32_t** streamPtr) override; - bool getWordFromStream(uint32_t* word) override; - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; - - private: -}; - -class HmpidDecodeRawDigit : public HmpidDecodeRawMem -{ - public: - HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawDigit(int numOfEquipments); - ~HmpidDecodeRawDigit(); - - std::vector mDigits; - - private: - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h index d8f59ee48ce1b..fb8409e685f83 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecoder2.h @@ -163,6 +163,7 @@ class HmpidDecoder2 public: bool decodeHmpidError(int ErrorField, char* outbuf); void dumpHmpidError(HmpidEquipment* eq, int ErrorField, int mHeBCDI, int mHeORBIT); + void dumpMemory(const void* MemoryStartPtr, std::size_t Dimension); bool isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge); int decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex); HmpidEquipment* evaluateHeaderContents(int EquipmentIndex); @@ -171,7 +172,6 @@ class HmpidDecoder2 protected: bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); uint32_t readWordFromStream(); uint32_t* getActualStreamPtr() { diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h index 185dc653b22a2..1482e0c9a921a 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidEquipment.h @@ -47,7 +47,7 @@ const int ERR_WRONGSIZESEGMENTMARK = 10; const int ERR_LOSTEOSMARK = 11; const int ERR_HMPID = 12; -// ---- HMPID error def ------- +// ---- HMPID TRY errors def ------- const int TH_FILENOTEXISTS = 9; const int TH_OPENFILE = 8; const int TH_CREATEFILE = 7; @@ -59,6 +59,7 @@ const int TH_WRONGFILELEN = 14; const int TH_NULLBUFFERPOINTER = 13; const int TH_BUFFEREMPTY = 12; const int TH_WRONGBUFFERDIM = 11; +const int TH_BUFFERPOINTERTOEND = 16; const uint64_t OUTRANGEEVENTNUMBER = 0x1FFFFFFFFFFF; diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h new file mode 100644 index 0000000000000..cd976d3c14a7a --- /dev/null +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/Recon.h @@ -0,0 +1,194 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_HMPID_RECON_H +#define ALICEO2_HMPID_RECON_H + +#include //base class + +#include +#include "Math/GenVector/RotationX.h" +#include "Math/GenVector/RotationY.h" +#include "Math/GenVector/RotationZ.h" + +#include +#include "Math/Vector3D.h" + +#include +#include + +#include + +#include "HMPIDBase/Param.h" +#include "DataFormatsHMP/Cluster.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" + +class Param; + +// using Polar3DVector = ROOT::Math::Polar3DVector; +using MatchInfo = o2::dataformats::MatchInfoHMP; +using TrackParCov = o2::track::TrackParCov; + +namespace o2 +{ +namespace hmpid +{ +class Recon : public TNamed +{ + public: + // ef : moved Recon(): ctor from .cxx file + // Recon() = default; + + Recon() : TNamed("RichRec", "RichPat"), // ef : moved from cxx + fPhotCnt(-1), + fPhotFlag(0x0), + fPhotClusIndex(0x0), + fPhotCkov(0x0), + fPhotPhi(0x0), + fPhotWei(0x0), + fCkovSigma2(0), + fIsWEIGHT(kFALSE), + fDTheta(0.001), + fWindowWidth(0.045), + fRingArea(0), + fRingAcc(0), + fTrkDir(0, 0, 1), // Just for test + fTrkPos(30, 40), // Just for test + fMipPos(0, 0), + fPc(0, 0), + fParam(o2::hmpid::Param::instance()) + // fParam(Param::instance()) + { + //.. + // init of data members + //.. + + fParam->setRefIdx(fParam->meanIdxRad()); // initialization of ref index to a default one // ef:ok + } + + //~Recon() = default; + virtual ~Recon() { ; } // dtor + + // ef : methods in these classeS? + void initVars(int n); // init space for variables + + // ef : commented out: no need to delete variables when smart-pointer + // void deleteVars() const; // delete variables + + // void CkovAngle (AliESDtrack *pTrk,TClonesArray *pCluLst,int index,double nmean,float xRa,float yRa ); + void ckovAngle(o2::dataformats::MatchInfoHMP* match, const std::vector clusters, int index, double nmean, float xRa, float yRa); // reconstructed Theta Cerenkov + + bool findPhotCkov(double cluX, double cluY, double& thetaCer, double& phiCer); // find ckov angle for single photon candidate + bool findPhotCkov2(double cluX, double cluY, double& thetaCer, double& phiCer); + double findRingCkov(int iNclus); // best ckov for ring formed by found photon candidates + void findRingGeom(double ckovAng, int level = 1); // estimated area of ring in cm^2 and portion accepted by geometry + + // template + const TVector2 intWithEdge(TVector2 p1, TVector2 p2); // find intercection between plane and lines of 2 thetaC + + int flagPhot(double ckov, const std::vector clusters, float* photChargeVec); // is photon ckov near most probable track ckov + // int flagPhot(double ckov, const std::vector clusters); // is photon ckov near most probable track ckov + + double houghResponse(); // most probable track ckov angle + // template + void propagate(const TVector3 dir, TVector3& pos, double z) const; // propagate photon alogn the line + // void refract(math_utils::Vector3D& dir, double n1, double n2) const; // refract photon on the boundary + + // template + void refract(TVector3& dir, double n1, double n2) const; // + + TVector2 tracePhot(double ckovTh, double ckovPh) const; // trace photon created by track to PC + + // ef : commented out addObjectToFriends + // void addObjectToFriends(TClonesArray *pCluLst, int photonIndex, AliESDtrack *pTrk ); // Add AliHMPIDCluster object to ESD friends + // template + TVector2 traceForward(TVector3 dirCkov) const; // tracing forward a photon from (x,y) to PC + void lors2Trs(TVector3 dirCkov, double& thetaCer, double& phiCer) const; // LORS to TRS + void trs2Lors(TVector3 dirCkov, double& thetaCer, double& phiCer) const; // TRS to LORS + + TVector2 getMip() const + { + return fMipPos; + } // mip coordinates + + double getRingArea() const + { + return fRingArea; + } // area of the current ring in cm^2 + + double getRingAcc() const + { + return fRingAcc; + } // portion of the ring ([0,1]) accepted by geometry.To scale n. of photons + double findRingExt(double ckov, int ch, double xPc, double yPc, double thRa, double phRa); // find ring acceptance by external parameters + + void setTrack(double xRad, double yRad, double theta, double phi) + { + fTrkDir.SetMagThetaPhi(1., theta, phi); + fTrkPos.Set(xRad, yRad); + } // set track parameter at RAD + + void setImpPC(double xPc, double yPc) + { + fPc.Set(xPc, yPc); + } // set track impact to PC + + void setMip(double xmip, double ymip) + { + fMipPos.Set(xmip, ymip); + } // set track impact to PC + + enum eTrackingFlags { kNotPerformed = -20, + kMipDistCut = -9, + kMipQdcCut = -5, + kNoPhotAccept = -11, + kNoRad = -22 }; + // + protected: + int fPhotCnt; // counter of photons candidate + + // ef : changed to smart-pointer arrays + std::unique_ptr fPhotFlag; // flags of photon candidates + std::unique_ptr fPhotClusIndex; // cluster index of photon candidates + std::unique_ptr fPhotCkov; // Ckov angles of photon candidates, [rad] + std::unique_ptr fPhotPhi; // phis of photons candidates, [rad] + std::unique_ptr fPhotWei; // weigths of photon candidates + + // int *fPhotClusIndex; // cluster index of photon candidates + + double fCkovSigma2; // sigma2 of the reconstructed ring + + bool fIsWEIGHT; // flag to consider weight procedure + float fDTheta; // Step for sliding window + float fWindowWidth; // Hough width of sliding window + + double fRingArea; // area of a given ring + double fRingAcc; // fraction of the ring accepted by geometry + + TVector3 fTrkDir; // track direction in LORS at RAD + + TVector2 fTrkPos; // track positon in LORS at RAD // XY mag + TVector2 fMipPos; // mip positon for a given trackf // XY + TVector2 fPc; // track position at PC // XY + + o2::hmpid::Param* fParam = o2::hmpid::Param::instance(); // Pointer to HMPIDParam + + private: + Recon(const Recon& r); // dummy copy constructor + Recon& operator=(const Recon& r); // dummy assignment operator + // + ClassDef(Recon, 3) +}; + +} // namespace hmpid +} // namespace o2 +#endif diff --git a/Detectors/HMPID/reconstruction/src/CTFCoder.cxx b/Detectors/HMPID/reconstruction/src/CTFCoder.cxx index 72fbfd66180ee..c09a4c924cfe9 100644 --- a/Detectors/HMPID/reconstruction/src/CTFCoder.cxx +++ b/Detectors/HMPID/reconstruction/src/CTFCoder.cxx @@ -41,11 +41,13 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa { const auto ctf = CTF::getImage(bufVec.data()); // just to get types - uint16_t bcInc, HCIDTrk, q; - uint32_t orbitInc, entriesDig; + int16_t bcInc; + int32_t orbitInc; + uint16_t HCIDTrk, q; + uint32_t entriesDig; uint8_t chID, ph, x, y; -#define MAKECODER(part, slot) createCoder(op, ctf.getFrequencyTable(slot), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(bcInc, CTF::BLC_bcIncTrig); MAKECODER(orbitInc, CTF::BLC_orbitIncTrig); diff --git a/Detectors/HMPID/reconstruction/src/Clusterer.cxx b/Detectors/HMPID/reconstruction/src/Clusterer.cxx index 9ddacbb3de005..d2cbcf770f749 100644 --- a/Detectors/HMPID/reconstruction/src/Clusterer.cxx +++ b/Detectors/HMPID/reconstruction/src/Clusterer.cxx @@ -12,7 +12,7 @@ /// \file Clusterer.cxx /// \brief Implementation of the HMPID cluster finder #include -#include // for LOG +#include "FairLogger.h" // for LOG #include "Framework/Logger.h" #include "HMPIDBase/Param.h" #include "DataFormatsHMP/Cluster.h" @@ -39,6 +39,52 @@ void Clusterer::Dig2Clu(gsl::span digs, std::vector vPad; + std::vector digVec; + for (int iCh = Param::kMinCh; iCh <= Param::kMaxCh; iCh++) { // chambers loop + padMap = (Float_t)-1; // reset map to -1 (means no digit for this pad) + for (size_t iDig = 0; iDig < digs.size(); iDig++) { + o2::hmpid::Digit::pad2Absolute(digs[iDig].getPadID(), &module, &padChX, &padChY); + vPad.push_back({padChX, padChY, module}); + if (module == iCh) { + padMap(padChX, padChY) = iDig; // fill the map for the given chamber, (padx,pady) cell takes digit index + } + } // digits loop for current chamber + + for (size_t iDig = 0; iDig < digs.size(); iDig++) { // digits loop for current chamber + // o2::hmpid::Digit::pad2Absolute(digs[iDig].getPadID(), &module, &padChX, &padChY); + if (vPad.at(iDig).m != iCh || (pUsedDig = UseDig(vPad.at(iDig).x, vPad.at(iDig).y, padMap)) == -1) { // this digit is from other module or already taken in FormClu(), go after next digit + continue; + } + digVec.clear(); + Cluster clu; + clu.setDigits(&digVec); + clu.setCh(iCh); + FormClu(clu, pUsedDig, digs, padMap); // form cluster starting from this digit by recursion + clu.solve(&clus, pUserCut, isUnfold); // solve this cluster and add all unfolded clusters to provided list + } // digits loop for current chamber + vPad.clear(); + } // chambers loop + return; +} // Dig2Clu() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/*void Clusterer::Dig2Clu(gsl::span digs, std::vector& clus, float* pUserCut, bool isUnfold) +{ + // Finds all clusters for a given digits list provided not empty. Currently digits list is a list of all digits for a single chamber. + // Puts all found clusters in separate lists, one per clusters. + // Arguments: pDigAll - list of digits for all chambers + // pCluAll - list of clusters for all chambers + // isTryUnfold - flag to choose between CoG and Mathieson fitting + // Returns: none + TMatrixF padMap(Param::kMinPx, Param::kMaxPcx, Param::kMinPy, Param::kMaxPcy); // pads map for single chamber 0..159 x 0..143 int pUsedDig = -1; @@ -63,16 +109,17 @@ void Clusterer::Dig2Clu(gsl::span digs, std::vector digs, TMatrixF& pDigMap) { // Forms the initial cluster as a combination of all adjascent digits. Starts from the given digit then calls itself recursevly for all neighbours. // Arguments: pClu - pointer to cluster being formed // Returns: none + pClu.digAdd(&digs[pDig]); // take this digit in cluster int cnt = 0; int cx[4]; diff --git a/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h b/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h index 1fa77ff6676f2..af7466d30c2e5 100644 --- a/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h +++ b/Detectors/HMPID/reconstruction/src/HMPIDReconstructionLinkDef.h @@ -18,6 +18,7 @@ //#pragma link C++ class o2::hmpid::DataReader + ; #pragma link C++ class o2::hmpid::Clusterer + ; //#pragma link C++ class o2::hmpid::HmpidDecodeRawMem + ; +#pragma link C++ class o2::hmpid::Recon + ; //#pragma link C++ class o2::hmpid::HmpidDecoder + ; #pragma link C++ class o2::hmpid::HmpidDecoder2 + ; #pragma link C++ class o2::hmpid::HmpidEquipment + ; diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx deleted file mode 100644 index df97a4d2101e0..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "HMPIDReconstruction/HmpidDecodeRawFile.h" - -using namespace o2::hmpid; - -/// Constructor with the default HMPID equipments map at P2 -/// @param[in] numOfEquipments : number of defined equipments [0..13] -HmpidDecodeRawFile::HmpidDecodeRawFile(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ - fh = 0; -} - -/// Constructor with the HMPID address map -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawFile::HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ - fh = 0; -} - -/// Destructor -HmpidDecodeRawFile::~HmpidDecodeRawFile() -{ -} - -/// Setup the Input Stream with a File Handle -/// verify the existence and try to open it -/// @param[in] *FileName : the string that contains the File Name -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_FILENOTEXISTS Thrown if the file doesn't exists -/// @throws TH_OPENFILE Thrown if Fails to open the file -bool HmpidDecodeRawFile::setUpStream(void* FileName, long Size) -{ - strcpy(mInputFile, (const char*)FileName); - // files section ---- - if (!fileExists(mInputFile)) { - LOG(error) << "The input file " << mInputFile << " does not exist at this time."; - throw TH_FILENOTEXISTS; - } - // open the file - fh = fopen(mInputFile, "rb"); - if (fh == 0) { - LOG(error) << "ERROR to open Input file ! [" << mInputFile << "]"; - throw TH_OPENFILE; - } - - mActualStreamPtr = 0; // sets the pointer to the Buffer - mEndStreamPtr = 0; //sets the End of buffer - mStartStreamPtr = 0; - - return (true); -} - -/// Gets a sized chunk from the stream. Read from the file and update the pointers -/// ATTENTION : in order to optimize the disk accesses the block read pre-load a -/// complete Header+Payload block, the Size parameter is recalculated with the -/// dimension of the pack extract from the header field 'Offeset' -/// -/// verify the existence and try to open it -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_WRONGFILELEN Thrown if the file doesn't contains enough words -bool HmpidDecodeRawFile::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - if (Size > MAXRAWFILEBUFFER) - return (false); - int nr = fread(mFileBuffer, sizeof(int32_t), HEADERDIMENSION_W, fh); - if (nr != HEADERDIMENSION_W) { - throw TH_WRONGFILELEN; - } - Size = ((mFileBuffer[2] & 0x0000FFFF) / sizeof(int32_t)) - HEADERDIMENSION_W; - nr = fread(mFileBuffer + HEADERDIMENSION_W, sizeof(int32_t), Size, fh); - LOG(debug) << " getBlockFromStream read " << nr << " of " << Size + HEADERDIMENSION_W << " words !"; - if (nr != Size) { - throw TH_WRONGFILELEN; - } - *streamPtr = mFileBuffer; - mStartStreamPtr = mFileBuffer; - mActualStreamPtr = mFileBuffer; - mEndStreamPtr = mFileBuffer + Size; - return (true); -} - -/// Reads the Header from the file -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawFile::getHeaderFromStream(uint32_t** streamPtr) -{ - bool flag = getBlockFromStream(streamPtr, RAWBLOCKDIMENSION_W); // reads the 8k block - mActualStreamPtr += HEADERDIMENSION_W; // Move forward for the first word - return (flag); -} - -/// Read one word from the pre-load buffer -/// @param[in] *word : the buffer for the read word -/// @returns True every time -bool HmpidDecodeRawFile::getWordFromStream(uint32_t* word) -{ - *word = *mActualStreamPtr; - mActualStreamPtr++; - return (true); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawFile::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -/// Checks if the file exists ! -/// @param[in] *filewithpath : the File Name to check -/// @returns True if the file exists -int HmpidDecodeRawFile::fileExists(char* filewithpath) -{ - if (access(filewithpath, F_OK) != -1) { - return (true); - } else { - return (false); - } -} -o2::hmpid::Digit diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx deleted file mode 100644 index 5a4f2acbfd97b..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecodeRawMem.h" - -using namespace o2::hmpid; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawMem::HmpidDecodeRawMem(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawMem::HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawMem::~HmpidDecodeRawMem() = default; - -/// Setup the Input Stream with a Memory Pointer -/// the buffer length is in byte, some controls are done -/// -/// @param[in] *Buffer : the pointer to Memory buffer -/// @param[in] BufferLen : the length of the buffer (bytes) -/// @returns True if the stream is set -/// @throws TH_NULLBUFFERPOINTER Thrown if the pointer to the buffer is NULL -/// @throws TH_BUFFEREMPTY Thrown if the buffer is empty -/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then one header -bool HmpidDecodeRawMem::setUpStream(void* Buffer, long BufferLen) -{ - long wordsBufferLen = BufferLen / (sizeof(int32_t) / sizeof(char)); // Converts the len in words - if (Buffer == nullptr) { - LOG(error) << "Raw data buffer null Pointer ! "; - throw TH_NULLBUFFERPOINTER; - } - if (wordsBufferLen == 0) { - LOG(error) << "Raw data buffer Empty ! "; - throw TH_BUFFEREMPTY; - } - if (wordsBufferLen < 16) { - LOG(error) << "Raw data buffer less then the Header Dimension = " << wordsBufferLen; - throw TH_WRONGBUFFERDIM; - } - - mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; //sets the End of buffer - mStartStreamPtr = ((uint32_t*)Buffer); - // std::cout << " setUpStrem : StPtr=" << mStartStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << wordsBufferLen << std::endl; - return (true); -} - -/// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : the dimension of the chunk (words) -/// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested -bool HmpidDecodeRawMem::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - *streamPtr = mActualStreamPtr; - mActualStreamPtr += Size; - if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; - // throw TH_WRONGBUFFERDIM; - return (false); - } - return (true); -} - -/// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawMem::getHeaderFromStream(uint32_t** streamPtr) -{ - return (getBlockFromStream(streamPtr, mRDHSize)); -} - -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecodeRawMem::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - *word = *mActualStreamPtr; - return (getBlockFromStream(&appo, 1)); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawMem::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -// ======================================================================================== - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int numOfEquipments) - : HmpidDecodeRawMem(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecodeRawMem(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawDigit::~HmpidDecodeRawDigit() = default; - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawDigit::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - mDigits.push_back(o2::hmpid::Digit(charge, eq->getEquipmentId(), col, dil, ch)); - //std::cout << "DI " << mDigits.back() << " "< // for LOG -#include "Framework/Logger.h" -#include "Headers/RAWDataHeader.h" -#include "HMPIDReconstruction/HmpidDecoder.h" -#include "DataFormatsHMP/Digit.h" - -using namespace o2::hmpid; - -// ============= HmpidDecoder Class implementation ======= - -/// Decoding Error Messages Definitions -char HmpidDecoder::sErrorDescription[MAXERRORS][MAXDESCRIPTIONLENGHT] = {"Word that I don't known !", - "Row Marker Word with 0 words", "Duplicated Pad Word !", "Row Marker Wrong/Lost -> to EoE", - "Row Marker Wrong/Lost -> to EoE", "Row Marker reports an ERROR !", "Lost EoE Marker !", "Double EoE marker", - "Wrong size definition in EoE Marker", "Double Mark Word", "Wrong Size in Segment Marker", "Lost EoS Marker !", - "HMPID Header Errors"}; - -/// HMPID Firmware Error Messages Definitions -char HmpidDecoder::sHmpidErrorDescription[MAXHMPIDERRORS][MAXDESCRIPTIONLENGHT] = { - "L0 Missing," - "L1 is received without L0", - "L1A signal arrived before the L1 Latency", "L1A signal arrived after the L1 Latency", - "L1A is missing or L1 timeout", "L1A Message is missing or L1 Message"}; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecoder::HmpidDecoder(int numOfEquipments) -{ - // The standard definition of HMPID equipments at P2 - int EqIds[] = {0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 10, 11, 12, 13}; - int CruIds[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - int LinkIds[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; - - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecoder::HmpidDecoder(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) -{ - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Destructor : remove the Equipments instances -HmpidDecoder::~HmpidDecoder() -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - delete mTheEquipments[i]; - } -} - -/// Init all the members variables. -void HmpidDecoder::init() -{ - mRDHSize = sizeof(o2::header::RAWDataHeader) / sizeof(uint32_t); - - mVerbose = 0; - mHeEvent = 0; - mHeBusy = 0; - mNumberWordToRead = 0; - mPayloadTail = 0; - - mHeFEEID = 0; - mHeSize = 0; - mHeVer = 0; - mHePrior = 0; - mHeStop = 0; - mHePages = 0; - mEquipment = 0; - - mHeOffsetNewPack = 0; - mHeMemorySize = 0; - - mHeDetectorID = 0; - mHeDW = 0; - mHeCruID = 0; - mHePackNum = 0; - mHePAR = 0; - mHePageNum = 0; - mHeLinkNum = 0; - mHeFirmwareVersion = 0; - mHeHmpidError = 0; - mHeBCDI = 0; - mHeORBIT = 0; - mHeTType = 0; - - mActualStreamPtr = nullptr; - mEndStreamPtr = nullptr; - mStartStreamPtr = nullptr; - - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - } -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the FLP hardware coords (CRU_Id and Link_Id) -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the Equipment_ID (Firmaware defined Id AKA FFEID) -/// @param[in] EquipmentId : the Equipment ID [0..13] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int EquipmentId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId() == EquipmentId) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment_ID converting the FLP hardware coords -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentID : the ID of the Equipment [0..13] (-1 := error) -int HmpidDecoder::getEquipmentID(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (mTheEquipments[i]->getEquipmentId()); - } - } - return (-1); -} - -/// Scans the BitMap of Raw Data File word and detect the type -/// and the parameters -/// @param[in] wp : the word to analyze -/// @param[out] *p1 : first parameter extract (if it exists) -/// @param[out] *p2 : second parameter extract (if it exists) -/// @param[out] *p3 : third parameter extract (if it exists) -/// @param[out] *p4 : fourth parameter extract (if it exists) -/// @returns Type of Word : the type of word [0..4] (0 := undetect) -int HmpidDecoder::checkType(uint32_t wp, int* p1, int* p2, int* p3, int* p4) -{ - if ((wp & 0x0000ffff) == 0x000036A8 || (wp & 0x0000ffff) == 0x000032A8 || (wp & 0x0000ffff) == 0x000030A0 || (wp & 0x0800ffff) == 0x080010A0) { - *p2 = (wp & 0x03ff0000) >> 16; // Number of words of row - *p1 = wp & 0x0000ffff; - return (WTYPE_ROW); - } - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *p2 = (wp & 0x000fff00) >> 8; // Number of words of Segment - *p1 = (wp & 0xfff00000) >> 20; - *p3 = wp & 0x0000000F; - if (*p3 < 4 && *p3 > 0) { - return (WTYPE_EOS); - } - } - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0000007F); - if (*p1 < 25 && *p2 < 11) { - return (WTYPE_EOE); - } - } - if ((wp & 0x08000000) == 0) { // # this is a pad - // PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0003F000) >> 12; - *p4 = (wp & 0x00000FFF); - if (*p1 > 0 && *p1 < 25 && *p2 > 0 && *p2 < 11 && *p3 < 48) { - return (WTYPE_PAD); - } - } else { - return (WTYPE_NONE); - } - return (WTYPE_NONE); -} - -/// Checks if is a Raw Marker and extract the Row Size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *rowSize : the number of words of the row -/// @param[out] *mark : the row marker -/// @returns True if Row Marker is detected -bool HmpidDecoder::isRowMarker(uint32_t wp, int* Err, int* rowSize, int* mark) -{ - if ((wp & 0x0000ffff) == 0x36A8 || (wp & 0x0000ffff) == 0x32A8 || (wp & 0x0000ffff) == 0x30A0 || (wp & 0x0800ffff) == 0x080010A0) { - *rowSize = (wp & 0x03ff0000) >> 16; // # Number of words of row - *mark = wp & 0x0000ffff; - *Err = false; - return (true); - } else { - *Err = true; - return (false); - } -} - -/// Checks if is a Segment Marker and extracts the Segment number and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *segSize : the number of words of the segment -/// @param[out] *Seg : the Segment number [1..3] -/// @param[out] *mark : the Segment Marker -/// @returns True if Segment Marker is detected -bool HmpidDecoder::isSegmentMarker(uint32_t wp, int* Err, int* segSize, int* Seg, int* mark) -{ - *Err = false; - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *segSize = (wp & 0x000fff00) >> 8; // # Number of words of Segment - *mark = (wp & 0xfff00000) >> 20; - *Seg = wp & 0x0000000F; - if (*Seg > 3 || *Seg < 1) { - LOG(info) << " Wrong segment Marker Word, bad Number of segment" << *Seg << "!"; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Checks if is a PAD Word and extracts all the parameters -/// PAD map : 0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Channel : the channel number [0..47] -/// @param[out] *Charge : the value of Charge [0..4095] -/// @returns True if PAD Word is detected -bool HmpidDecoder::isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge) -{ - *Err = false; - // if ((wp & 0x08000000) != 0) { - if ((wp & 0x08000000) != 0) { - return (false); - } - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Channel = (wp & 0x0003F000) >> 12; - *Charge = (wp & 0x00000FFF); - - if ((wp & 0x0ffff) == 0x036A8 || (wp & 0x0ffff) == 0x032A8 || (wp & 0x0ffff) == 0x030A0 || (wp & 0x0ffff) == 0x010A0) { // # ! this is a pad - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - return (false); - } - } else { - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - // LOG(warning) << " Wrong Pad values Col=" << *Col << " Dilogic=" << *Dilogic << " Channel=" << *Channel << " Charge=" << *Charge << " wp:0x" << std::hex << wp << std::dec; - *Err = true; - return (false); - } - } - return (true); -} - -/// Checks if is a EoE Marker and extracts the Column, Dilogic and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Eoesize : the number of words for dilogic -/// @returns True if EoE marker is detected -bool HmpidDecoder::isEoEmarker(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Eoesize) -{ - *Err = false; - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Eoesize = (wp & 0x0000007F); - if (*Col > 24 || *Dilogic > 10) { - LOG(info) << " EoE size wrong definition. Col=" << *Col << " Dilogic=" << *Dilogic; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Decode the HMPID error BitMap field (5 bits) and returns true if there are -/// errors and in addition the concat string that contains the error messages -/// ATTENTION : the char * outbuf MUST point to a 250 bytes buffer -/// @param[in] ErrorField : the HMPID Error field -/// @param[out] *outbuf : the output buffer that contains the error description -/// @returns True if EoE marker is detected -bool HmpidDecoder::decodeHmpidError(int ErrorField, char* outbuf) -{ - int res = false; - outbuf[0] = '\0'; - for (int i = 0; i < MAXHMPIDERRORS; i++) { - if ((ErrorField & (0x01 << i)) != 0) { - res = true; - strcat(outbuf, sHmpidErrorDescription[i]); - } - } - return (res); -} - -/// This Decode the Raw Data Header, returns the EquipmentIndex -/// that is obtained with the FLP hardware coords -/// -/// ATTENTION : the 'EquipIndex' parameter and the mEquipment member -/// are different data: the first is the pointer in the Equipments instances -/// array, the second is the FEE_ID number -/// -/// The EVENT_NUMBER : actually is calculated from the ORBIT number -/// -/// @param[in] *streamPtrAdr : the pointer to the Header buffer -/// @param[out] *EquipIndex : the Index to the Equipment Object Array [0..13] -/// @returns True every time -/// @throws TH_WRONGEQUIPINDEX Thrown if the Equipment Index is out of boundary (Equipment not recognized) -int HmpidDecoder::decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex) -{ - uint32_t* buffer = streamPtrAdr; // Sets the pointer to buffer - o2::header::RAWDataHeader* hpt = (o2::header::RAWDataHeader*)buffer; - - /* - mHeFEEID = (buffer[0] & 0x000f0000) >> 16; - mHeSize = (buffer[0] & 0x0000ff00) >> 8; - mHeVer = (buffer[0] & 0x000000ff); - mHePrior = (buffer[1] & 0x000000FF); - mHeDetectorID = (buffer[1] & 0x0000FF00) >> 8; - mHeOffsetNewPack = (buffer[2] & 0x0000FFFF); - mHeMemorySize = (buffer[2] & 0xffff0000) >> 16; - mHeDW = (buffer[3] & 0xF0000000) >> 24; - mHeCruID = (buffer[3] & 0x0FF0000) >> 16; - mHePackNum = (buffer[3] & 0x0000FF00) >> 8; - mHeLinkNum = (buffer[3] & 0x000000FF); - mHeBCDI = (buffer[4] & 0x00000FFF); - mHeORBIT = buffer[5]; - mHeTType = buffer[8]; - mHePageNum = (buffer[9] & 0x0000FFFF); - mHeStop = (buffer[9] & 0x00ff0000) >> 16; - mHeBusy = (buffer[12] & 0xfffffe00) >> 9; - mHeFirmwareVersion = buffer[12] & 0x0000000f; - mHeHmpidError = (buffer[12] & 0x000001F0) >> 4; - mHePAR = buffer[13] & 0x0000FFFF; - */ - mHeFEEID = hpt->feeId; - mHeSize = hpt->headerSize; - mHeVer = hpt->version; - mHePrior = hpt->priority; - mHeDetectorID = hpt->sourceID; - mHeOffsetNewPack = hpt->offsetToNext; - mHeMemorySize = hpt->memorySize; - mHeDW = hpt->endPointID; - mHeCruID = hpt->cruID; - mHePackNum = hpt->packetCounter; - mHeLinkNum = hpt->linkID; - mHeBCDI = hpt->bunchCrossing; - mHeORBIT = hpt->orbit; - mHeTType = hpt->triggerType; - mHePageNum = hpt->pageCnt; - mHeStop = hpt->stop; - mHeBusy = (hpt->detectorField & 0xfffffe00) >> 9; - mHeFirmwareVersion = hpt->detectorField & 0x0000000f; - mHeHmpidError = (hpt->detectorField & 0x000001F0) >> 4; - mHePAR = hpt->detectorPAR; - - *EquipIndex = getEquipmentIndex(mHeCruID, mHeLinkNum); - // mEquipment = (*EquipIndex != -1) ? mTheEquipments[*EquipIndex]->getEquipmentId() : -1; - mEquipment = mHeFEEID & 0x000F; - mNumberWordToRead = ((mHeMemorySize - mHeSize) / sizeof(uint32_t)); - mPayloadTail = ((mHeOffsetNewPack - mHeMemorySize) / sizeof(uint32_t)); - - // ---- Event ID : Actualy based on ORBIT NUMBER and BC - mHeEvent = (mHeORBIT << 12) | mHeBCDI; - - LOG(debug) << "FEE-ID=" << mHeFEEID << " HeSize=" << mHeSize << " HePrior=" << mHePrior << " Det.Id=" << mHeDetectorID << " HeMemorySize=" << mHeMemorySize << " HeOffsetNewPack=" << mHeOffsetNewPack; - LOG(debug) << " Equipment=" << mEquipment << " PakCounter=" << mHePackNum << " Link=" << mHeLinkNum << " CruID=" << mHeCruID << " DW=" << mHeDW << " BC=" << mHeBCDI << " ORBIT=" << mHeORBIT; - LOG(debug) << " TType=" << mHeTType << " HeStop=" << mHeStop << " PagesCounter=" << mHePageNum << " FirmVersion=" << mHeFirmwareVersion << " BusyTime=" << mHeBusy << " Error=" << mHeHmpidError << " PAR=" << mHePAR; - LOG(debug) << " EquIdx = " << *EquipIndex << " Event = " << mHeEvent << " Payload : Words to read=" << mNumberWordToRead << " PailoadTail=" << mPayloadTail; - - if (*EquipIndex == -1) { - LOG(error) << "ERROR ! Bad equipment Number: " << mEquipment; - throw TH_WRONGEQUIPINDEX; - } - // std::cout << "HMPID ! Exit decode header" << std::endl; - return (true); -} - -/// Updates some information related to the Event -/// this function is called at the end of the event -/// @param[in] *eq : the pointer to the Equipment Object -void HmpidDecoder::updateStatistics(HmpidEquipment* eq) -{ - eq->mPadsPerEventAverage = ((eq->mPadsPerEventAverage * (eq->mNumberOfEvents - 1)) + eq->mSampleNumber) / (eq->mNumberOfEvents); - eq->mEventSizeAverage = ((eq->mEventSizeAverage * (eq->mNumberOfEvents - 1)) + eq->mEventSize) / (eq->mNumberOfEvents); - eq->mBusyTimeAverage = ((eq->mBusyTimeAverage * eq->mBusyTimeSamples) + eq->mBusyTimeValue) / (++(eq->mBusyTimeSamples)); - if (eq->mSampleNumber == 0) { - eq->mNumberOfEmptyEvents += 1; - } - if (eq->mErrorsCounter > 0) { - eq->mNumberOfWrongEvents += 1; - } - eq->mTotalPads += eq->mSampleNumber; - eq->mTotalErrors += eq->mErrorsCounter; - - //std::cout << ">>>updateStatistics() >>> "<< eq->getEquipmentId() << "="<< eq->mNumberOfEvents<<" :" << eq->mEventSize <<","<< eq->mTotalPads << ", " << eq->mSampleNumber << std::endl; - - return; -} - -/// Evaluates the content of the header and detect the change of the event -/// with the relevant updates... -/// @param[in] EquipmentIndex : the pointer to the Array of Equipments Array -/// @returns the Pointer to the modified Equipment object -HmpidEquipment* HmpidDecoder::evaluateHeaderContents(int EquipmentIndex) -{ - //std::cout << "Enter evaluateHeaderContents.."; - HmpidEquipment* eq = mTheEquipments[EquipmentIndex]; - if (mHeEvent != eq->mEventNumber) { // Is a new event - if (eq->mEventNumber != OUTRANGEEVENTNUMBER) { // skip the first - updateStatistics(eq); // update previous statistics - } - eq->mNumberOfEvents++; - eq->mEventNumber = mHeEvent; - eq->mBusyTimeValue = mHeBusy * 0.00000005; - eq->mEventSize = 0; // reset the event - eq->mSampleNumber = 0; - eq->mErrorsCounter = 0; - mIntReco = {(uint16_t)mHeBCDI, (uint32_t)mHeORBIT}; - } - eq->mEventSize += mNumberWordToRead * sizeof(uint32_t); // Calculate the size in bytes - if (mHeHmpidError != 0) { - LOG(info) << "HMPID Header reports an error : " << mHeHmpidError; - dumpHmpidError(mHeHmpidError); - eq->setError(ERR_HMPID); - } - // std::cout << ".. end evaluateHeaderContents = " << eq->mEventNumber << std::endl; - return (eq); -} - -/// --------------- Decode One Page from Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePage(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(debug) << "End main decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(error) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int p1, p2, p3, p4; - int error; - int type; - bool isIt; - - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - if (newOne == true) { - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - type = checkType(wp, &p1, &p2, &p3, &p4); - if (type == WTYPE_NONE) { - if (eq->mWillBePad == true) { // try to recover the first pad ! - type = checkType((wp & 0xF7FFFFFF), &p1, &p2, &p3, &p4); - if (type == WTYPE_PAD && p3 == 0 && eq->mWordsPerDilogicCounter == 0) { - newOne = false; // # reprocess as pad - continue; - } - } - eq->setError(ERR_NOTKNOWN); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_NOTKNOWN] << " [" << wp << "]"; - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - payIndex++; - continue; - } - } - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << std::hex << wp << std::dec << "<" << type; - } - if (eq->mWillBeRowMarker == true) { // #shoud be a Row Marker - if (type == WTYPE_ROW) { - eq->mColumnCounter++; - eq->mWordsPerSegCounter++; - eq->mRowSize = p2; - switch (p2) { - case 0: // Empty column - eq->setError(ERR_ROWMARKEMPTY); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKEMPTY] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FF: // Error in column - eq->setError(ERR_ROWMARKERROR); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKERROR] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FE: // Masked column - LOG(info) << "Equip=" << mEquipment << "The column=" << (eq->mSegment) * 8 + eq->mColumnCounter << " is Masked !"; - eq->mWillBeRowMarker = true; - break; - default: - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - break; - } - newOne = true; - } else { - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == WTYPE_EOE) { // # Could be a EoE - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKWRONG); - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_PAD) { //# Could be a PAD - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_EOS) { // # Could be a EoS - eq->mWillBeRowMarker = false; - eq->mWillBeSegmentMarker = true; - newOne = false; - } else { - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } - } - } else if (eq->mWillBePad == true) { // # We expect a pad - //# PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - // c = 1..24 d = 1..10 n = 0..47 - if (type == WTYPE_PAD) { - newOne = true; - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - } else if (p1 != (eq->mSegment * 8 + eq->mColumnCounter)) { // # Manage - // We try to recover the RowMarker misunderstanding - isIt = isRowMarker(wp, &error, &p2, &p1); - if (isIt == true && error == false) { - type = WTYPE_ROW; - newOne = false; - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } else { - LOG(debug) << "Equip=" << mEquipment << " Mismatch in column" - << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mColumnCounter = p1 % 8; - } - } else { - setPad(eq, p1 - 1, p2 - 1, p3, p4); - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << p1 - 1 << "," << p2 - 1 << "," << p3 << "," << p4; - } - eq->mWordsPerDilogicCounter++; - eq->mSampleNumber++; - if (p3 == 47) { - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } - } - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - } else if (type == WTYPE_EOE) { //# the pads are end ok - eq->mWillBeEoE = true; - eq->mWillBePad = false; - newOne = false; - } else if (type == WTYPE_ROW) { // # We Lost the EoE ! - // We try to recover the PAD misunderstanding - isIt = isPadWord(wp, &error, &p1, &p2, &p3, &p4); - if (isIt == true && error == false) { - type = WTYPE_PAD; - newOne = false; // # reprocess as pad - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (type == WTYPE_EOS) { // # We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (eq->mWillBeEoE == true) { // # We expect a EoE - if (type == WTYPE_EOE) { - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - if (wpprev == wp) { - eq->setError(ERR_DOUBLEEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEEOEMARK] << " col=" << p1; - } else if (p3 != eq->mWordsPerDilogicCounter) { - eq->setError(ERR_WRONGSIZEINEOE); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZEINEOE] << " col=" << p1; - } - eq->mWordsPerDilogicCounter = 0; - if (p2 == 10) { - if (p1 % 8 != 0) { // # we expect the Row Marker - eq->mWillBeRowMarker = true; - } else { - eq->mWillBeSegmentMarker = true; - } - } else { - eq->mWillBePad = true; - } - eq->mWillBeEoE = false; - newOne = true; - } else if (type == WTYPE_EOS) { // We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_ROW) { //# We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_PAD) { // # We Lost the EoE ! - int typb, p1b, p2b, p3b, p4b; - typb = checkType((wp | 0x08000000), &p1b, &p2b, &p3b, &p4b); - if (typb == WTYPE_EOE && p3b == 48) { - type = typb; - p1 = p1b; - p2 = p2b; - p3 = p3b; - p4 = p4b; - newOne = false; // # reprocess as EoE - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBePad = true; - eq->mWillBeEoE = false; - newOne = false; - } - } - } else if (eq->mWillBeSegmentMarker == true) { // # We expect a EoSegment - if (wpprev == wp) { - eq->setError(ERR_DOUBLEMARKWORD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEMARKWORD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == 2) { - if (abs(eq->mWordsPerSegCounter - p2) > 5) { - eq->setError(ERR_WRONGSIZESEGMENTMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZESEGMENTMARK] << " Seg=" << p2; - } - eq->mWordsPerSegCounter = 0; - eq->mWordsPerRowCounter = 0; - eq->mColumnCounter = 0; - eq->mSegment = p3 % 3; - eq->mWillBeRowMarker = true; - eq->mWillBeSegmentMarker = false; - newOne = true; - } else { - eq->setError(ERR_LOSTEOSMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOSMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = false; - eq->mWillBeRowMarker = true; - newOne = false; - } - } - if (newOne) { - payIndex += 1; - } - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } -} - -/// --------------- Read Raw Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBuffer() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - mTheEquipments[i]->resetErrors(); - } - - int type; - int equipmentIndex = -1; - int isIt; - HmpidEquipment* eq; - uint32_t* streamBuf; - LOG(debug) << "Enter decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePage(&streamBuf); - } catch (int e) { - LOG(debug) << "End main buffer decoding loop !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -/// --------- Decode One Page from Data Buffer with Fast Decoding -------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePageFast(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(info) << "End Fast Page decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(info) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int Column, Dilogic, Channel, Charge; - int pwer; - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - if (wp == wpprev) { - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << Column << "]"; - } else { - if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { - if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; - } - } - } - payIndex += 1; - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } - return; -} -/// ---------- Read Raw Data Buffer with Fast Decoding ---------- -/// Read the stream, decode the contents and store resuls. -/// Fast alghoritm : no parsing of control words ! -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBufferFast() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - } - - uint32_t* streamBuf; - LOG(info) << "Enter FAST decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePageFast(&streamBuf); - } catch (int e) { - LOG(info) << " End Buffer Fast Decoding !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -// ========================================================= - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Number of entries for specified pad -uint16_t HmpidDecoder::getPadSamples(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getPadSum(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getPadSquares(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[c][d][h]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Number of Entries for specified pad -uint16_t HmpidDecoder::getChannelSamples(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getChannelSum(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getChannelSquare(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[Column][Dilogic][Channel]); -} - -/// Gets the Average Event Size value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Event Size value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageEventSize(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mEventSizeAverage); -} - -/// Gets the Average Busy Time value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Busy Time value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageBusyTime(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mBusyTimeAverage); -} - -// =================================================== -// Methods to dump info - -/// Prints on the standard output the table of decoding -/// errors for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -void HmpidDecoder::dumpErrors(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - std::cout << "Dump Errors for the Equipment = " << EquipmId << std::endl; - for (int i = 0; i < MAXERRORS; i++) { - std::cout << sErrorDescription[i] << " = " << mTheEquipments[EqInd]->mErrors[i] << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output a Table of statistical -/// decoding information for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @type[in] The type of info. 0 = Entries, 1 = Sum, 2 = Sum of squares -void HmpidDecoder::dumpPads(int EquipmId, int type) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - int Module = EquipmId / 2; - int StartRow = (EquipmId % 2 == 1) ? 80 : 0; - int EndRow = (EquipmId % 2 == 1) ? 160 : 80; - std::cout << "Dump Pads for the Equipment = " << EquipmId << std::endl; - for (int c = 0; c < 144; c++) { - for (int r = StartRow; r < EndRow; r++) { - switch (type) { - case 0: - std::cout << getPadSamples(Module, r, c) << ","; - break; - case 1: - std::cout << getPadSum(Module, r, c) << ","; - break; - case 2: - std::cout << getPadSquares(Module, r, c) << ","; - break; - } - } - std::cout << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output the decoded HMPID error field -/// @param[in] ErrorField : the HMPID readout error field -void HmpidDecoder::dumpHmpidError(int ErrorField) -{ - char printbuf[MAXHMPIDERRORS * MAXDESCRIPTIONLENGHT]; - if (decodeHmpidError(ErrorField, printbuf) == true) { - LOG(error) << "HMPID Error field = " << ErrorField << " : " << printbuf; - } - return; -} - -/// Writes in a ASCCI File the complete report of the decoding -/// procedure -/// @param[in] *summaryFileName : the name of the output file -/// @throws TH_CREATEFILE Thrown if was not able to create the file -void HmpidDecoder::writeSummaryFile(char* summaryFileName) -{ - FILE* fs = fopen(summaryFileName, "w"); - if (fs == nullptr) { - printf("Error opening the file %s !\n", summaryFileName); - throw TH_CREATEFILE; - } - - fprintf(fs, "HMPID Readout Raw Data Decoding Summary File\n"); - fprintf(fs, "Equipment Id\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->getEquipmentId()); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average Event Size\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mEventSizeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Total pads\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalPads); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average pads per event\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mPadsPerEventAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Busy Time average\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Event rate\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", 1 / mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of Empty Events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEmptyEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "-------------Errors--------------------\n"); - fprintf(fs, "Wrong events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfWrongEvents); - } - fprintf(fs, "\n"); - - for (int j = 0; j < MAXERRORS; j++) { - fprintf(fs, "%s\t", sErrorDescription[j]); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mErrors[j]); - } - fprintf(fs, "\n"); - } - - fprintf(fs, "Total errors\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalErrors); - } - fprintf(fs, "\n"); - - fclose(fs); - return; -} diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx index d96d27e07a0d0..f88c3a6b65253 100644 --- a/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx +++ b/Detectors/HMPID/reconstruction/src/HmpidDecoder2.cxx @@ -17,6 +17,7 @@ /// \date 17/11/2020 /* ------ HISTORY --------- +24/01/2024 - review of pointer management from the stream AF */ #include // for LOG @@ -133,6 +134,7 @@ void HmpidDecoder2::init() for (int i = 0; i < mNumberOfEquipments; i++) { mTheEquipments[i]->init(); mTheEquipments[i]->resetPadMap(); + mTheEquipments[i]->resetErrors(); } mDigits.clear(); @@ -531,9 +533,7 @@ void HmpidDecoder2::decodePage(uint32_t** streamBuf) while (payIndex < mNumberWordToRead) { // start the payload loop word by word if (newOne == true) { wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } + wp = readWordFromStream(); type = checkType(wp, &p1, &p2, &p3, &p4); if (type == WTYPE_NONE) { if (eq->mWillBePad == true) { // try to recover the first pad ! @@ -744,7 +744,7 @@ void HmpidDecoder2::decodePage(uint32_t** streamBuf) } } for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); + wp = readWordFromStream(); } *streamBuf = mActualStreamPtr; } @@ -822,7 +822,7 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) // The stream end ! if (mVerbose > 6) { std::cout << "HMPID Decoder2 : [INFO] " - << "End Fast Page decoding loop !" << std::endl; + << "End Fast Page decoding loop ! (" << e << ")" << std::endl; } throw TH_BUFFEREMPTY; } @@ -831,15 +831,19 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) } catch (int e) { if (mVerbose > 6) { std::cout << "HMPID Decoder2 : [INFO] " - << "Failed to decode the Header !" << std::endl; + << "Failed to decode the Header ! (" << e << ")" << std::endl; } - throw TH_WRONGHEADER; + throw e; } HmpidEquipment* eq; try { eq = evaluateHeaderContents(equipmentIndex); } catch (int e) { - throw TH_WRONGHEADER; + if (mVerbose > 6) { + std::cout << "HMPID Decoder2 : [INFO] " + << "Failed to evaluate the Header ! (" << e << ")" << std::endl; + } + throw e; } uint32_t wpprev = 0; uint32_t wp = 0; @@ -858,8 +862,10 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) } else { if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; + if (!((equipmentIndex == 6 && Column == 16 && Dilogic == 10) || (equipmentIndex == 13 && Column == 18 && Dilogic == 2))) { + setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); + eq->mSampleNumber++; + } } } } @@ -879,7 +885,7 @@ void HmpidDecoder2::decodePageFast(uint32_t** streamBuf) /// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header bool HmpidDecoder2::decodeBufferFast() { - bool isNotEmpty = false; + bool isNotEmpty = true; // suppress the Error Decoding Superpage message in normal verbosity // ---------resets the PAdMap----------- for (int i = 0; i < mNumberOfEquipments; i++) { mTheEquipments[i]->init(); @@ -895,15 +901,14 @@ bool HmpidDecoder2::decodeBufferFast() try { decodePageFast(&streamBuf); } catch (int e) { - if (mVerbose > 6) { - std::cout << "HMPID Decoder2 : [INFO] " - << " End Buffer Fast Decoding !" << std::endl; + if (mVerbose > 6) { // print all the decode errors eccept the end buffer + std::cout << "HMPID Decoder2 : [ERROR] " + << "End Buffer Fast Decoding! Exit code error = " << e << std::endl; + isNotEmpty = false; } break; } - isNotEmpty = true; - } // this is the end of stream - + } // cycle in order to update info for the last event for (int i = 0; i < mNumberOfEquipments; i++) { if (mTheEquipments[i]->mNumberOfEvents > 0) { @@ -1103,6 +1108,33 @@ void HmpidDecoder2::dumpHmpidError(HmpidEquipment* eq, int ErrorField, int mHeBC return; } +/// Prints a block of memory +/// @param[in] MemoryStartPtr : the pointer to the begin +/// @param[in] Dimension : the block dimension in bytes +void HmpidDecoder2::dumpMemory(const void* MemoryStartPtr, std::size_t Dimension) +{ + const unsigned char* data = static_cast(MemoryStartPtr); + for (std::size_t i = 0; i < Dimension; i += 16) { + std::cout << std::hex << std::setw(4) << std::setfill('0') << i << " : "; + // Print hexadecimal values + for (std::size_t j = 0; j < 16; ++j) { + if (i + j < Dimension) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(data[i + j]) << " "; + } else { + std::cout << " "; + } + } + // Print ASCII values + std::cout << " - "; + for (std::size_t j = 0; j < 16 && i + j < Dimension; ++j) { + char ch = (data[i + j] >= 32 && data[i + j] <= 126) ? static_cast(data[i + j]) : '.'; + std::cout << ch; + } + std::cout << std::endl; + } + return; +} + /// Writes in a ASCCI File the complete report of the decoding /// procedure /// @param[in] *summaryFileName : the name of the output file @@ -1190,17 +1222,20 @@ void HmpidDecoder2::writeSummaryFile(char* summaryFileName) } /// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer +/// @param[out] **streamPtr : the pointer to the memory buffer /// @param[in] Size : the dimension of the chunk (words) /// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested +/// @throws TH_BUFFERPOINTERTOEND Thrown if the pointer to the buffer is past end +/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then the size requested bool HmpidDecoder2::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) { + if (mActualStreamPtr > mEndStreamPtr) { + throw TH_BUFFERPOINTERTOEND; + return (false); + } *streamPtr = mActualStreamPtr; mActualStreamPtr += Size; if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; throw TH_WRONGBUFFERDIM; return (false); } @@ -1208,35 +1243,26 @@ bool HmpidDecoder2::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) } /// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer +/// @param[out] **streamPtr : the pointer to the memory buffer /// @returns True if the header is read bool HmpidDecoder2::getHeaderFromStream(uint32_t** streamPtr) { return (getBlockFromStream(streamPtr, mRDHSize)); } -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecoder2::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - if (getBlockFromStream(&appo, 1)) { - *word = *mActualStreamPtr; - return (true); - } - return (false); -} - /// Gets a Word from the stream. /// @returns The word read +/// @throws TH_BUFFERPOINTERTOEND Thrown if the pointer to the buffer is past end uint32_t HmpidDecoder2::readWordFromStream() { - mActualStreamPtr++; + uint32_t word = 0; if (mActualStreamPtr > mEndStreamPtr) { - throw TH_WRONGBUFFERDIM; + throw TH_BUFFERPOINTERTOEND; + return (word); } - return (*mActualStreamPtr); + word = *mActualStreamPtr; + mActualStreamPtr++; + return (word); } /// Setup the Input Stream with a Memory Pointer @@ -1274,7 +1300,16 @@ bool HmpidDecoder2::setUpStream(void* Buffer, long BufferLen) } mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; // sets the End of buffer + mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen - 1; // sets the End of buffer mStartStreamPtr = ((uint32_t*)Buffer); + + if (mVerbose > 6) { + std::cout << "HMPID Decoder2 : setUpStream() " + << "Buffer addr = " << std::hex << mActualStreamPtr << " End addr = " << mEndStreamPtr + << " BufferLen = " << std::dec << BufferLen << std::endl; + if (BufferLen < 256) { + dumpMemory((const void*)mActualStreamPtr, (std::size_t)BufferLen); + } + } return (true); } diff --git a/Detectors/HMPID/reconstruction/src/Recon.cxx b/Detectors/HMPID/reconstruction/src/Recon.cxx new file mode 100644 index 0000000000000..867e72cc319c8 --- /dev/null +++ b/Detectors/HMPID/reconstruction/src/Recon.cxx @@ -0,0 +1,628 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "HMPIDBase/Param.h" +#include "HMPIDReconstruction/Recon.h" //class header +// #include "ReconstructionDataFormats/MatchInfoHMP.h" + +#include //TracePhot() +#include //HoughResponse() +#include //HoughResponse() + +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/Track.h" + +using MatchInfo = o2::dataformats::MatchInfoHMP; + +using namespace o2::hmpid; +// ClassImp(Recon); +ClassImp(o2::hmpid::Recon); +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::initVars(int n) +{ + //.. + // Init some variables + //.. + if (n <= 0) { + return; + } + + // ef : changed to smart-pointer Array + // fPhotFlag = new int[n]; + fPhotFlag = std::unique_ptr(new int[n]); + fPhotClusIndex = std::unique_ptr(new int[n]); + + fPhotCkov = std::unique_ptr(new double[n]); + fPhotPhi = std::unique_ptr(new double[n]); + fPhotWei = std::unique_ptr(new double[n]); + // +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::ckovAngle(o2::dataformats::MatchInfoHMP* match, const std::vector clusters, int index, double nmean, float xRa, float yRa) +{ + // Pattern recognition method based on Hough transform + // Arguments: pTrk - track for which Ckov angle is to be found + // pCluLst - list of clusters for this chamber + // Returns: - track ckov angle, [rad], + + const int nMinPhotAcc = 3; // Minimum number of photons required to perform the pattern recognition + + int nClusTot = clusters.size(); + + initVars(nClusTot); + + float xPc, yPc, th, ph; + + match->getHMPIDtrk(xPc, yPc, th, ph); // initialize this track: th and ph angles at middle of RAD + + setTrack(xRa, yRa, th, ph); + + fParam->setRefIdx(nmean); + + float mipQ = -1, mipX = -1, mipY = -1; + int chId = -1, sizeClu = -1; + + fPhotCnt = 0; + + int nPads = 0; + + for (int iClu = 0; iClu < clusters.size(); iClu++) { // clusters loop + + o2::hmpid::Cluster cluster = clusters.at(iClu); + nPads += cluster.size(); + if (iClu == index) { // this is the MIP! not a photon candidate: just store mip info + mipX = cluster.x(); + mipY = cluster.y(); + mipQ = cluster.q(); + sizeClu = cluster.size(); + continue; + } + + chId = cluster.ch(); + if (cluster.q() > 2 * fParam->qCut() || cluster.size() > 4) { + continue; + } + double thetaCer, phiCer; + if (findPhotCkov(cluster.x(), cluster.y(), thetaCer, phiCer)) { // find ckov angle for this photon candidate + fPhotCkov[fPhotCnt] = thetaCer; // actual theta Cerenkov (in TRS) + fPhotPhi[fPhotCnt] = phiCer; + fPhotClusIndex[fPhotCnt] = iClu; // actual phi Cerenkov (in TRS): -pi to come back to "unusual" ref system (X,Y,-Z) + fPhotCnt++; // increment counter of photon candidates + } + } // clusters loop + + match->setHMPIDmip(mipX, mipY, mipQ, fPhotCnt); // store mip info in any case + match->setIdxHMPClus(chId, index + 1000 * sizeClu); // set index of cluster + match->setMipClusSize(sizeClu); + + if (fPhotCnt < nMinPhotAcc) { // no reconstruction with <=3 photon candidates + match->setHMPsignal(kNoPhotAccept); // set the appropriate flag + return; + } + + fMipPos.Set(mipX, mipY); + + // PATTERN RECOGNITION STARTED: + if (fPhotCnt > fParam->multCut()) { + fIsWEIGHT = kTRUE; + } // offset to take into account bkg in reconstruction + else { + fIsWEIGHT = kFALSE; + } + + float photCharge[10] = {0x0}; + + int iNrec = flagPhot(houghResponse(), clusters, photCharge); // flag photons according to individual theta ckov with respect to most probable + // int iNrec = flagPhot(houghResponse(), clusters); // flag photons according to individual theta ckov with respect to most probable + + match->setPhotCharge(photCharge); + match->setHMPIDmip(mipX, mipY, mipQ, iNrec); // store mip info + + if (iNrec < nMinPhotAcc) { + match->setHMPsignal(kNoPhotAccept); // no photon candidates are accepted + return; + } + + int occupancy = (int)(1000 * (nPads / (6. * 80. * 48.))); + + double thetaC = findRingCkov(clusters.size()); // find the best reconstructed theta Cherenkov + findRingGeom(thetaC, 2); + + match->setHMPsignal(thetaC + occupancy); // store theta Cherenkov and chmaber occupancy + // match->SetHMPIDchi2(fCkovSigma2); //store experimental ring angular resolution squared + + // deleteVars(); ef : in case of smart-pointers, should not be necessary? +} // CkovAngle() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +bool Recon::findPhotCkov(double cluX, double cluY, double& thetaCer, double& phiCer) +{ + // Finds Cerenkov angle for this photon candidate + // Arguments: cluX,cluY - position of cadidate's cluster + // Returns: Cerenkov angle + + TVector3 dirCkov; + + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 rad(fTrkPos.X(), fTrkPos.Y(), zRad); // impact point at middle of RAD + TVector3 pc(cluX, cluY, 0.5 * fParam->winThick() + fParam->gapThick()); // mip at PC + double cluR = TMath::Sqrt((cluX - fPc.X()) * (cluX - fPc.X()) + + (cluY - fPc.Y()) * (cluY - fPc.Y())); // ref. distance impact RAD-CLUSTER + double phi = (pc - rad).Phi(); // phi of photon + + double ckov1 = 0; + double ckov2 = 0.75 + fTrkDir.Theta(); // start to find theta cerenkov in DRS + const double kTol = 0.01; + Int_t iIterCnt = 0; + while (1) { + if (iIterCnt >= 50) { + return kFALSE; + } + double ckov = 0.5 * (ckov1 + ckov2); + dirCkov.SetMagThetaPhi(1, ckov, phi); + TVector2 posC = traceForward(dirCkov); // trace photon with actual angles + double dist = cluR - (posC - fPc).Mod(); // get distance between trial point and cluster position + if (posC.X() == -999) { + dist = -999; + } // total reflection problem + iIterCnt++; // counter step + if (dist > kTol) { + ckov1 = ckov; + } // cluster @ larger ckov + else if (dist < -kTol) { + ckov2 = ckov; + } // cluster @ smaller ckov + else { // precision achived: ckov in DRS found + dirCkov.SetMagThetaPhi(1, ckov, phi); // + lors2Trs(dirCkov, thetaCer, phiCer); // find ckov (in TRS:the effective Cherenkov angle!) + return kTRUE; + } + } +} // FindPhotTheta() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +bool Recon::findPhotCkov2(double cluX, double cluY, double& thetaCer, double& phiCer) +{ + + // TVector3 emissionV; + /** set emission point **/ + // emissionV.SetXYZ(xEm, yEm, zEm); + + // TVector3 directionV; + /** set track direction vector **/ + // directionV.SetXYZ(trkPx, trkPy, trkPz); + + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 emissionV(fTrkPos.X(), fTrkPos.Y(), zRad); // impact point at middle of RAD + + TVector3 photonHitV, apparentV, surfaceV; + photonHitV.SetXYZ(cluX, cluY, 0.5 * fParam->winThick() + fParam->gapThick()); + apparentV = photonHitV - emissionV; + surfaceV = emissionV; + // surfaceV.SetZ(0); + + Double_t n1 = fParam->getRefIdx(); + Double_t n2 = 1.; + Double_t apparentTheta = apparentV.Theta(); + Double_t correctedTheta = asin(n2 / n1 * sin(apparentTheta)); + Double_t deltaTheta = apparentTheta - correctedTheta; + + TVector3 perpV = apparentV.Cross(surfaceV); + TVector3 cherenkovV = apparentV; + // cherenkovV.Rotate(deltaTheta, perpV); + + lors2Trs(cherenkovV, thetaCer, phiCer); + + // thetaCer = cherenkovV.Angle(fTrkDir); + // phiCer = cherenkovV.Phi(); + + return kTRUE; +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +TVector2 Recon::traceForward(TVector3 dirCkov) const +{ + // Trace forward a photon from (x,y) up to PC + // Arguments: dirCkov photon vector in LORS + // Returns: pos of traced photon at PC + + TVector2 pos(-999, -999); + double thetaCer = dirCkov.Theta(); + if (thetaCer > TMath::ASin(1. / fParam->getRefIdx())) { + return pos; + } // total refraction on WIN-GAP boundary + double zRad = -0.5 * fParam->radThick() - 0.5 * fParam->winThick(); // z position of middle of RAD + TVector3 posCkov(fTrkPos.X(), fTrkPos.Y(), zRad); // RAD: photon position is track position @ middle of RAD + propagate(dirCkov, posCkov, -0.5 * fParam->winThick()); // go to RAD-WIN boundary + refract(dirCkov, fParam->getRefIdx(), fParam->winIdx()); // RAD-WIN refraction + propagate(dirCkov, posCkov, 0.5 * fParam->winThick()); // go to WIN-GAP boundary + refract(dirCkov, fParam->winIdx(), fParam->gapIdx()); // WIN-GAP refraction + propagate(dirCkov, posCkov, 0.5 * fParam->winThick() + fParam->gapThick()); // go to PC + pos.Set(posCkov.X(), posCkov.Y()); + return pos; + +} // TraceForward() + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::lors2Trs(TVector3 dirCkov, double& thetaCer, double& phiCer) const +{ + // Theta Cerenkov reconstruction + // Arguments: dirCkov photon vector in LORS + // Returns: thetaCer of photon in TRS + // phiCer of photon in TRS + // TVector3 dirTrk; + // dirTrk.SetMagThetaPhi(1,fTrkDir.Theta(),fTrkDir.Phi()); -> dirTrk.SetCoordinates(1,fTrkDir.Theta(),fTrkDir.Phi()) + // double thetaCer = TMath::ACos(dirCkov*dirTrk); + + TRotation mtheta; + mtheta.RotateY(-fTrkDir.Theta()); + + TRotation mphi; + mphi.RotateZ(-fTrkDir.Phi()); + + TRotation mrot = mtheta * mphi; + + TVector3 dirCkovTRS; + dirCkovTRS = mrot * dirCkov; + phiCer = dirCkovTRS.Phi(); // actual value of the phi of the photon + thetaCer = dirCkovTRS.Theta(); // actual value of thetaCerenkov of the photon +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::trs2Lors(TVector3 dirCkov, double& thetaCer, double& phiCer) const +{ + // Theta Cerenkov reconstruction + // Arguments: dirCkov photon vector in TRS + // Returns: thetaCer of photon in LORS + // phiCer of photon in LORS + + // TRotation mtheta; + // mtheta.RotateY(fTrkDir.Theta()); ef : changed to : + + TRotation mtheta; + mtheta.RotateY(fTrkDir.Theta()); + + TRotation mphi; + mphi.RotateZ(fTrkDir.Phi()); + + TRotation mrot = mphi * mtheta; + + TVector3 dirCkovLORS; + dirCkovLORS = mrot * dirCkov; + + phiCer = dirCkovLORS.Phi(); // actual value of the phi of the photon + thetaCer = dirCkovLORS.Theta(); // actual value of thetaCerenkov of the photon +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::findRingGeom(double ckovAng, int level) +{ + // Find area covered in the PC acceptance + // Arguments: ckovAng - cerenkov angle + // level - precision in finding area and portion of ring accepted (multiple of 50) + // Returns: area of the ring in cm^2 for given theta ckov + + Int_t kN = 50 * level; + Int_t nPoints = 0; + Double_t area = 0; + + Bool_t first = kFALSE; + TVector2 pos1; + + for (Int_t i = 0; i < kN; i++) { + if (!first) { + pos1 = tracePhot(ckovAng, Double_t(TMath::TwoPi() * (i + 1) / kN)); // find a good trace for the first photon + if (pos1.X() == -999) { + continue; + } // no area: open ring + if (!fParam->isInside(pos1.X(), pos1.Y(), 0)) { + pos1 = intWithEdge(fMipPos, pos1); // find the very first intersection... + } else { + if (!fParam->isInDead(pos1.X(), pos1.Y())) { + nPoints++; + } // photon is accepted if not in dead zone + } + first = kTRUE; + continue; + } + TVector2 pos2 = tracePhot(ckovAng, Double_t(TMath::TwoPi() * (i + 1) / kN)); // trace the next photon + if (pos2.X() == -999) { + { + continue; + } + } // no area: open ring + if (!fParam->isInside(pos2.X(), pos2.Y(), 0)) { + pos2 = intWithEdge(fMipPos, pos2); + } else { + if (!fParam->isInDead(pos2.X(), pos2.Y())) { + nPoints++; + } // photon is accepted if not in dead zone + } + area += TMath::Abs((pos1 - fMipPos).X() * (pos2 - fMipPos).Y() - (pos1 - fMipPos).Y() * (pos2 - fMipPos).X()); // add area of the triangle... + pos1 = pos2; + } + //--- find area and length of the ring; + fRingAcc = (Double_t)nPoints / (Double_t)kN; + area *= 0.5; + fRingArea = area; + +} // FindRingGeom() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +const TVector2 Recon::intWithEdge(TVector2 p1, TVector2 p2) +{ + // It finds the intersection of the line for 2 points traced as photons + // and the edge of a given PC + // Arguments: 2 points obtained tracing the photons + // Returns: intersection point with detector (PC) edges + + double xmin = (p1.X() < p2.X()) ? p1.X() : p2.X(); + double xmax = (p1.X() < p2.X()) ? p2.X() : p1.X(); + double ymin = (p1.Y() < p2.Y()) ? p1.Y() : p2.Y(); + double ymax = (p1.Y() < p2.Y()) ? p2.Y() : p1.Y(); + + double m = TMath::Tan((p2 - p1).Phi()); + TVector2 pint; + // intersection with low X + pint.Set((double)(p1.X() + (0 - p1.Y()) / m), 0.); + if (pint.X() >= 0 && pint.X() <= fParam->sizeAllX() && + pint.X() >= xmin && pint.X() <= xmax && + pint.Y() >= ymin && pint.Y() <= ymax) { + return pint; + } + // intersection with high X + pint.Set((double)(p1.X() + (fParam->sizeAllY() - p1.Y()) / m), (double)(fParam->sizeAllY())); + if (pint.X() >= 0 && pint.X() <= fParam->sizeAllX() && + pint.X() >= xmin && pint.X() <= xmax && + pint.Y() >= ymin && pint.Y() <= ymax) { + return pint; + } + // intersection with left Y + pint.Set(0., (double)(p1.Y() + m * (0 - p1.X()))); + if (pint.Y() >= 0 && pint.Y() <= fParam->sizeAllY() && + pint.Y() >= ymin && pint.Y() <= ymax && + pint.X() >= xmin && pint.X() <= xmax) { + return pint; + } + // intersection with righ Y + pint.Set((double)(fParam->sizeAllX()), (double)(p1.Y() + m * (fParam->sizeAllX() - p1.X()))); // ef: Set->SetCoordinates + if (pint.Y() >= 0 && pint.Y() <= fParam->sizeAllY() && + pint.Y() >= ymin && pint.Y() <= ymax && + pint.X() >= xmin && pint.X() <= xmax) { + return pint; + } + return p1; +} // IntWithEdge() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::findRingCkov(int) +{ + // Loops on all Ckov candidates and estimates the best Theta Ckov for a ring formed by those candidates. Also estimates an error for that Theat Ckov + // collecting errors for all single Ckov candidates thetas. (Assuming they are independent) + // Arguments: iNclus- total number of clusters in chamber for background estimation + // Return: best estimation of track Theta ckov + + Double_t wei = 0.; + Double_t weightThetaCerenkov = 0.; + + Double_t ckovMin = 9999., ckovMax = 0.; + Double_t sigma2 = 0; // to collect error squared for this ring + + for (Int_t i = 0; i < fPhotCnt; i++) { // candidates loop + if (fPhotFlag[i] == 2) { + if (fPhotCkov[i] < ckovMin) { + ckovMin = fPhotCkov[i]; + } // find max and min Theta ckov from all candidates within probable window + if (fPhotCkov[i] > ckovMax) { + ckovMax = fPhotCkov[i]; + } + weightThetaCerenkov += fPhotCkov[i] * fPhotWei[i]; + wei += fPhotWei[i]; // collect weight as sum of all candidate weghts + + sigma2 += 1. / fParam->sigma2(fTrkDir.Theta(), fTrkDir.Phi(), fPhotCkov[i], fPhotPhi[i]); + } + } // candidates loop + + if (sigma2 > 0) { + fCkovSigma2 = 1. / sigma2; + } else { + fCkovSigma2 = 1e10; + } + + if (wei != 0.) { + weightThetaCerenkov /= wei; + } else { + weightThetaCerenkov = 0.; + } + return weightThetaCerenkov; + +} // FindCkovRing() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +int Recon::flagPhot(double ckov, const std::vector clusters, float* photChargeVec) +// int Recon::flagPhot(double ckov, const std::vector clusters) +{ + // Flag photon candidates if their individual ckov angle is inside the window around ckov angle returned by HoughResponse() + // Arguments: ckov- value of most probable ckov angle for track as returned by HoughResponse() + // Returns: number of photon candidates happened to be inside the window + + // Photon Flag: Flag = 0 initial set; + // Flag = 1 good candidate (charge compatible with photon); + // Flag = 2 photon used for the ring; + + Int_t steps = (Int_t)((ckov) / fDTheta); // how many times we need to have fDTheta to fill the distance between 0 and thetaCkovHough + + Double_t tmin = (Double_t)(steps - 1) * fDTheta; + Double_t tmax = (Double_t)(steps)*fDTheta; + Double_t tavg = 0.5 * (tmin + tmax); + + tmin = tavg - 0.5 * fWindowWidth; + tmax = tavg + 0.5 * fWindowWidth; + + Int_t iInsideCnt = 0; // count photons which Theta ckov inside the window + for (Int_t i = 0; i < fPhotCnt; i++) { // photon candidates loop + fPhotFlag[i] = 0; + if (fPhotCkov[i] >= tmin && fPhotCkov[i] <= tmax) { + fPhotFlag[i] = 2; + o2::hmpid::Cluster cluster = clusters.at(fPhotClusIndex[i]); + float charge = cluster.q(); + if (iInsideCnt < 10) { + photChargeVec[iInsideCnt] = charge; + } // AddObjectToFriends(pCluLst,i,pTrk); + iInsideCnt++; + } + } + + return iInsideCnt; + +} // FlagPhot() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +TVector2 Recon::tracePhot(double ckovThe, double ckovPhi) const +{ + // Trace a single Ckov photon from emission point somewhere in radiator up to photocathode taking into account ref indexes of materials it travereses + // Arguments: ckovThe,ckovPhi- photon ckov angles in TRS, [rad] + // Returns: distance between photon point on PC and track projection + + double theta, phi; + TVector3 dirTRS, dirLORS; + dirTRS.SetMagThetaPhi(1, ckovThe, ckovPhi); // photon in TRS + trs2Lors(dirTRS, theta, phi); + dirLORS.SetMagThetaPhi(1, theta, phi); // photon in LORS + return traceForward(dirLORS); // now foward tracing + +} // tracePhot() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::propagate(const TVector3 dir, TVector3& pos, double z) const +{ + // Finds an intersection point between a line and XY plane shifted along Z. + // Arguments: dir,pos - vector along the line and any point of the line + // z - z coordinate of plain + // Returns: none + // On exit: pos is the position if this intesection if any + static TVector3 nrm(0, 0, 1); + TVector3 pnt(0, 0, z); + + TVector3 diff = pnt - pos; + double sint = (nrm * diff) / (nrm * dir); + pos += sint * dir; +} // Propagate() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +void Recon::refract(TVector3& dir, double n1, double n2) const +{ + // Refract direction vector according to Snell law + // Arguments: + // n1 - ref idx of first substance + // n2 - ref idx of second substance + // Returns: none + // On exit: dir is new direction + + double sinref = (n1 / n2) * TMath::Sin(dir.Theta()); + if (TMath::Abs(sinref) > 1.) { + dir.SetXYZ(-999, -999, -999); + } else { + dir.SetTheta(TMath::ASin(sinref)); + } +} +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::houghResponse() +{ + // fIdxMip = mipId; + + Double_t kThetaMax = 0.75; + Int_t nChannels = (Int_t)(kThetaMax / fDTheta + 0.5); + TH1D* phots = new TH1D("Rphot", "phots", nChannels, 0, kThetaMax); + TH1D* photsw = new TH1D("RphotWeighted", "photsw", nChannels, 0, kThetaMax); + TH1D* resultw = new TH1D("resultw", "resultw", nChannels, 0, kThetaMax); + Int_t nBin = (Int_t)(kThetaMax / fDTheta); + Int_t nCorrBand = (Int_t)(fWindowWidth / (2 * fDTheta)); + + for (Int_t i = 0; i < fPhotCnt; i++) { // photon cadidates loop + Double_t angle = fPhotCkov[i]; + if (angle < 0 || angle > kThetaMax) { + continue; + } + phots->Fill(angle); + Int_t bin = (Int_t)(0.5 + angle / (fDTheta)); + Double_t weight = 1.; + if (fIsWEIGHT) { + Double_t lowerlimit = ((Double_t)bin) * fDTheta - 0.5 * fDTheta; + Double_t upperlimit = ((Double_t)bin) * fDTheta + 0.5 * fDTheta; + findRingGeom(lowerlimit); + Double_t areaLow = getRingArea(); + findRingGeom(upperlimit); + Double_t areaHigh = getRingArea(); + Double_t diffArea = areaHigh - areaLow; + if (diffArea > 0) { + weight = 1. / diffArea; + } + } + photsw->Fill(angle, weight); + fPhotWei[i] = weight; + } // photon candidates loop + + for (Int_t i = 1; i <= nBin; i++) { + Int_t bin1 = i - nCorrBand; + Int_t bin2 = i + nCorrBand; + if (bin1 < 1) { + bin1 = 1; + } + if (bin2 > nBin) { + bin2 = nBin; + } + Double_t sumPhots = phots->Integral(bin1, bin2); + if (sumPhots < 3) { + continue; + } // if less then 3 photons don't trust to this ring + Double_t sumPhotsw = photsw->Integral(bin1, bin2); + if ((Double_t)((i + 0.5) * fDTheta) > 0.7) { + continue; + } + resultw->Fill((Double_t)((i + 0.5) * fDTheta), sumPhotsw); + } + // evaluate the "BEST" theta ckov as the maximum value of histogramm + Double_t* pVec = resultw->GetArray(); + Int_t locMax = TMath::LocMax(nBin, pVec); + delete phots; + delete photsw; + delete resultw; // Reset and delete objects + + return (Double_t)(locMax * fDTheta + 0.5 * fDTheta); // final most probable track theta ckov + +} // HoughResponse() +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +double Recon::findRingExt(double ckov, Int_t ch, double xPc, double yPc, double thRa, double phRa) +{ + // To find the acceptance of the ring even from external inputs. + // + // + double xRa = xPc - (fParam->radThick() + fParam->winThick() + fParam->gapThick()) * TMath::Cos(phRa) * TMath::Tan(thRa); // just linear extrapolation back to RAD + double yRa = yPc - (fParam->radThick() + fParam->winThick() + fParam->gapThick()) * TMath::Sin(phRa) * TMath::Tan(thRa); + + int nStep = 500; + int nPhi = 0; + + Int_t ipc, ipadx, ipady; + + if (ckov > 0) { + setTrack(xRa, yRa, thRa, phRa); + for (int j = 0; j < nStep; j++) { + TVector2 pos; + pos = tracePhot(ckov, j * TMath::TwoPi() / (double)(nStep - 1)); + if (Param::isInDead(pos.X(), pos.Y())) { + continue; + } + fParam->lors2Pad(pos.X(), pos.Y(), ipc, ipadx, ipady); + ipadx += (ipc % 2) * fParam->kPadPcX; + ipady += (ipc / 2) * fParam->kPadPcY; + if (ipadx < 0 || ipady > 160 || ipady < 0 || ipady > 144 || ch < 0 || ch > 6) { + continue; + } + if (Param::isDeadPad(ipadx, ipady, ch)) { + continue; + } + nPhi++; + } // point loop + return ((double)nPhi / (double)nStep); + } // if + return -1; +} diff --git a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h index b55ba3b6c7353..9e9a78914049e 100644 --- a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h +++ b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h @@ -55,6 +55,7 @@ class Detector : public o2::base::DetImpl void EndOfEvent() override { Reset(); } // for the geometry sub-parts + TGeoVolume* createAbsorber(float tickness); TGeoVolume* createChamber(int number); TGeoVolume* CreateCradle(); TGeoVolume* CradleBaseVolume(TGeoMedium* med, double l[7], const char* name); diff --git a/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h b/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h index 9d843feda64bc..b01935f4cac5d 100644 --- a/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h +++ b/Detectors/HMPID/simulation/include/HMPIDSimulation/HMPIDDigitizer.h @@ -59,7 +59,7 @@ class HMPIDDigitizer } } } - + // void setOrbit(double timeNS) {mOrbit = } uint32_t getOrbit() { return mOrbit; }; uint16_t getBc() { return mBc; }; @@ -99,7 +99,7 @@ class HMPIDDigitizer std::vector mDigits; // internal store for digits constexpr static double TRACKHOLDTIME = 1200; // defines the window for pile-up after a trigger received in nanoseconds - constexpr static double BUSYTIME = 22000; // the time for which no new trigger can be received in nanoseconds + constexpr static double BUSYTIME = 40000; // the time for which no new trigger can be received in nanoseconds std::map mIndexForPad; //! logarithmic mapping of pad to digit index diff --git a/Detectors/HMPID/simulation/src/Detector.cxx b/Detectors/HMPID/simulation/src/Detector.cxx index 674bf7e85441d..83bab71e7177d 100644 --- a/Detectors/HMPID/simulation/src/Detector.cxx +++ b/Detectors/HMPID/simulation/src/Detector.cxx @@ -73,20 +73,20 @@ bool Detector::ProcessHits(FairVolume* v) Int_t volID = fMC->CurrentVolID(copy); auto stack = (o2::data::Stack*)fMC->GetStack(); - //Treat photons - //photon (Ckov or feedback) hits on module PC (Hpad) + // Treat photons + // photon (Ckov or feedback) hits on module PC (Hpad) if ((fMC->TrackPid() == 50000050 || fMC->TrackPid() == 50000051) && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - if (fMC->Edep() > 0) { //photon survided QE test i.e. produces electron + if (fMC->Edep() > 0) { // photon survided QE test i.e. produces electron if (IsLostByFresnel()) { fMC->StopTrack(); return false; - } //photon lost due to fersnel reflection on PC - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID - Float_t etot = fMC->Etot(); //total hpoton energy, [GeV] + } // photon lost due to fersnel reflection on PC + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID + Float_t etot = fMC->Etot(); // total hpoton energy, [GeV] Double_t x[3]; - fMC->TrackPosition(x[0], x[1], x[2]); //take MARS position at entrance to PC - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(x[0], x[1], x[2]); // take MARS position at entrance to PC + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time Int_t idch; // chamber number if (volID == mHpad0VolID) { idch = 0; @@ -104,20 +104,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); //take LORS position - AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); //HIT for photon, position at P, etot will be set to Q - GenFee(etot); //generate feedback photons etot is modified in hit ctor to Q of hit + o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); // take LORS position + AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); // HIT for photon, position at P, etot will be set to Q + GenFee(etot); // generate feedback photons etot is modified in hit ctor to Q of hit stack->addHit(GetDetId()); - } //photon hit PC and DE >0 + } // photon hit PC and DE >0 return kTRUE; - } //photon hit PC + } // photon hit PC - //Treat charged particles - static Float_t eloss; //need to store mip parameters between different steps + // Treat charged particles + static Float_t eloss; // need to store mip parameters between different steps static Double_t in[3]; if (fMC->IsTrackEntering() && fMC->TrackCharge() && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - //Trackref stored when entering in the pad volume + // Trackref stored when entering in the pad volume o2::TrackReference tr(*fMC, GetDetId()); tr.setTrackID(stack->GetCurrentTrackNumber()); // tr.setUserId(lay); @@ -126,18 +126,18 @@ bool Detector::ProcessHits(FairVolume* v) if (fMC->TrackCharge() && (volID == mHcel0VolID || volID == mHcel1VolID || volID == mHcel2VolID || volID == mHcel3VolID || volID == mHcel4VolID || volID == mHcel5VolID || volID == mHcel6VolID)) { // charged particle in amplification gap (Hcel) - if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { //entering or newly created - eloss = 0; //reset Eloss collector - fMC->TrackPosition(in[0], in[1], in[2]); //take position at the entrance - } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { //exiting or disappeared - eloss += fMC->Edep(); //take into account last step Eloss - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID + if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { // entering or newly created + eloss = 0; // reset Eloss collector + fMC->TrackPosition(in[0], in[1], in[2]); // take position at the entrance + } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { // exiting or disappeared + eloss += fMC->Edep(); // take into account last step Eloss + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID Double_t out[3]; - fMC->TrackPosition(out[0], out[1], out[2]); //take MARS position at exit - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(out[0], out[1], out[2]); // take MARS position at exit + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time out[0] = 0.5 * (out[0] + in[0]); // - out[1] = 0.5 * (out[1] + in[1]); //take hit position at the anod plane + out[1] = 0.5 * (out[1] + in[1]); // take hit position at the anod plane out[2] = 0.5 * (out[2] + in[2]); Int_t idch; // chamber number if (volID == mHcel0VolID) { @@ -156,20 +156,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); //take LORS position + o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); // take LORS position if (eloss > 0) { // HIT for MIP, position near anod plane, eloss will be set to Q AddHit(out[0], out[1], out[2], hitTime, eloss, tid, idch); - GenFee(eloss); //generate feedback photons + GenFee(eloss); // generate feedback photons stack->addHit(GetDetId()); eloss = 0; } } else { - //just going inside - eloss += fMC->Edep(); //collect this step eloss + // just going inside + eloss += fMC->Edep(); // collect this step eloss } return kTRUE; - } //MIP in GAP + } // MIP in GAP // later on return true if there was a hit! return false; @@ -187,14 +187,14 @@ void Detector::GenFee(Float_t qtot) // eloss=0 means photon so only pulse height distribution is to be analysed. TLorentzVector x4; fMC->TrackPosition(x4); - Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); //# of feedback photons is proportional to the charge of hit - //AliDebug(1,Form("N photons=%i",iNphotons)); + Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); // # of feedback photons is proportional to the charge of hit + // AliDebug(1,Form("N photons=%i",iNphotons)); Int_t j; Float_t cthf, phif, enfp = 0, sthf, e1[3], e2[3], e3[3], vmod, uswop, dir[3], phi, pol[3], mom[4]; - //Generate photons - for (Int_t i = 0; i < iNphotons; i++) { //feedbacks loop + // Generate photons + for (Int_t i = 0; i < iNphotons; i++) { // feedbacks loop Double_t ranf[2]; - fMC->GetRandom()->RndmArray(2, ranf); //Sample direction + fMC->GetRandom()->RndmArray(2, ranf); // Sample direction cthf = ranf[0] * 2 - 1.0; if (cthf < 0) { continue; @@ -276,8 +276,8 @@ void Detector::GenFee(Float_t qtot) } fMC->Gdtom(pol, pol, 2); Int_t outputNtracksStored; - } //feedbacks loop -} //GenerateFeedbacks() + } // feedbacks loop +} // GenerateFeedbacks() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Bool_t Detector::IsLostByFresnel() { @@ -301,7 +301,7 @@ Bool_t Detector::IsLostByFresnel() } else { return kFALSE; } -} //IsLostByFresnel() +} // IsLostByFresnel() //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) { @@ -325,8 +325,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t cn = csin[j] + ((csin[j + 1] - csin[j]) / 0.1) * (xe - en[j]); Float_t ck = csik[j] + ((csik[j + 1] - csik[j]) / 0.1) * (xe - en[j]); - //FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR - //W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 + // FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR + // W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 Float_t sinin = TMath::Sqrt((1. - pdoti) * (1. + pdoti)); Float_t tanin = sinin / pdoti; @@ -340,8 +340,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t rp = rs * ((aO - sinin * tanin) * (aO - sinin * tanin) + b2) / ((aO + sinin * tanin) * (aO + sinin * tanin) + b2); - //CORRECTION FACTOR FOR SURFACE ROUGHNESS - //B.J. STAGG APPLIED OPTICS, 30(1991),4113 + // CORRECTION FACTOR FOR SURFACE ROUGHNESS + // B.J. STAGG APPLIED OPTICS, 30(1991),4113 Float_t sigraf = 18.; Float_t lamb = 1240 / ene; @@ -351,7 +351,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) (4 * TMath::Pi() * pdoti * sigraf / lamb)); if (pola) { - Float_t pdotr = 0.8; //DEGREE OF POLARIZATION : 1->P , -1->S + Float_t pdotr = 0.8; // DEGREE OF POLARIZATION : 1->P , -1->S fresn = 0.5 * (rp * (1 + pdotr) + rs * (1 - pdotr)); } else { fresn = 0.5 * (rp + rs); @@ -359,7 +359,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) fresn = fresn * rO; return fresn; -} //Fresnel() +} // Fresnel() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Detector::Register() { FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true); } @@ -537,7 +537,15 @@ void Detector::createMaterials() Material(++matId, "Ar", aAr, zAr, dAr, radAr, absAr); Medium(kAr, "Ar", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); } - +//************************************************************************************************** +TGeoVolume* Detector::createAbsorber(float tickness) +{ + double cm = 1, mm = 0.1 * cm, um = 0.001 * mm; // default is cm + auto& matmgr = o2::base::MaterialManager::Instance(); + TGeoMedium* al = matmgr.getTGeoMedium("HMP_Al"); + TGeoVolume* abs = gGeoManager->MakeBox("Habs", al, tickness * mm / 2, 1300.00 * mm / 2, 1300 * mm / 2); + return abs; +} //************************************************************************************************** TGeoVolume* Detector::createChamber(int number) @@ -1252,6 +1260,25 @@ void Detector::ConstructGeometry() TGeoVolume* hmpcradle = CreateCradle(); + TGeoVolume* hmpidabs_cham2 = createAbsorber(40.0); + TGeoVolume* hmpidabs_cham4 = createAbsorber(80.0); + + double theta = 33.5; + + TGeoHMatrix* pMatrixAbs2 = new TGeoHMatrix; + const double trans2[] = {435.5, 0., -155.}; + pMatrixAbs2->SetTranslation(trans2); + pMatrixAbs2->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham2, 0, pMatrixAbs2); + + TGeoHMatrix* pMatrixAbs4 = new TGeoHMatrix; + const double trans4[] = {435., 0., 155.}; + pMatrixAbs4->SetTranslation(trans4); + pMatrixAbs4->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham4, 0, pMatrixAbs4); + for (Int_t iCh = 0; iCh <= 6; iCh++) { // place 7 chambers TGeoVolume* hmpid = createChamber(iCh); TGeoHMatrix* pMatrix = new TGeoHMatrix; diff --git a/Detectors/HMPID/workflow/CMakeLists.txt b/Detectors/HMPID/workflow/CMakeLists.txt index 886e54dda8391..b532e6304fe96 100644 --- a/Detectors/HMPID/workflow/CMakeLists.txt +++ b/Detectors/HMPID/workflow/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(HMPIDWorkflow src/DigitsToClustersSpec.cxx src/DigitsWriterSpec.cxx src/ClustersWriterSpec.cxx + src/HMPMatchedWriterSpec.cxx src/DumpDigitsSpec.cxx src/PedestalsCalculationSpec.cxx src/RawToDigitsSpec.cxx @@ -25,16 +26,19 @@ o2_add_library(HMPIDWorkflow src/WriteRawFileSpec.cxx src/EntropyEncoderSpec.cxx src/EntropyDecoderSpec.cxx - - PUBLIC_LINK_LIBRARIES O2::Framework - O2::CCDB - O2::DPLUtils - O2::DetectorsRaw - O2::HMPIDBase - O2::DataFormatsHMP - O2::DataFormatsDCS - O2::HMPIDSimulation - O2::HMPIDReconstruction) + src/HMPMatchedReaderSpec.cxx + src/HMPMatchedWriterSpec.cxx + + PUBLIC_LINK_LIBRARIES + O2::Framework + O2::CCDB + O2::DPLUtils + O2::DetectorsRaw + O2::HMPIDBase + O2::DataFormatsHMP + O2::DataFormatsDCS + O2::HMPIDSimulation + O2::HMPIDReconstruction) o2_add_executable(digits-reader-workflow COMPONENT_NAME hmpid diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h deleted file mode 100644 index 6102ec481c97c..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ -#define STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace hmpid -{ - -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); - -} // end namespace hmpid -} // end namespace o2 - -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h deleted file mode 100644 index eea9b134bd911..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file DigitReader.h - -#ifndef O2_HMPID_DIGITREADER -#define O2_HMPID_DIGITREADER - -#include "TFile.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsHMP/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace hmpid -{ - -class DigitReader : public o2::framework::Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(o2::framework::InitContext& ic) final; - void run(o2::framework::ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr mFile = nullptr; - - std::vector mDigits, *mPdigits = &mDigits; - - o2::dataformats::MCTruthContainer mLabels, *mPlabels = &mLabels; -}; - -/// read simulated HMPID digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace hmpid -} // namespace o2 - -#endif /* O2_HMPID_DIGITREADER */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h index 8c64f326a6878..d03a30ab905e5 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyDecoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h index 2fb9fd301f13b..9c2c4eb5b4fb0 100644 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/EntropyEncoderSpec.h @@ -24,7 +24,7 @@ namespace hmpid { /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h new file mode 100644 index 0000000000000..ec459aa94ab3e --- /dev/null +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedReaderSpec.h @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedReaderSpec.h + +#ifndef O2_HMP_MATCHINFOREADER +#define O2_HMP_MATCHINFOREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/TrackTPCTOF.h" +#include "SimulationDataFormat/MCCompLabel.h" + +namespace o2 +{ +namespace hmpid +{ + +class HMPMatchedReader : public o2::framework::Task +{ + public: + HMPMatchedReader(bool useMC) : mUseMC(useMC) {} + ~HMPMatchedReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + private: + void connectTree(const std::string& filename); + + bool mUseMC = false; + // int mMode = 1; + + std::string mInFileName{"o2match_hmp.root"}; + std::string mInTreeName{"matchHMP"}; + std::unique_ptr mFile = nullptr; + std::unique_ptr mTree = nullptr; + std::vector mMatches, *mMatchesPtr = &mMatches; + // std::vector *mMatchesPtr = nullptr; + std::vector mLabelHMP, *mLabelHMPPtr = &mLabelHMP; +}; + +/// create a processor spec +/// read matched HMP clusters from a ROOT file +framework::DataProcessorSpec getHMPMatchedReaderSpec(bool useMC); + +} // namespace hmpid +} // namespace o2 + +#endif /* O2_HMP_MATCHINFOREADER */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h new file mode 100644 index 0000000000000..f951a0fb05f0b --- /dev/null +++ b/Detectors/HMPID/workflow/include/HMPIDWorkflow/HMPMatchedWriterSpec.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedWriterSpec.h + +#ifndef HMPWORKFLOW_HMPMATCHEDWRITER_H_ +#define HMPWORKFLOW_HMPMATCHEDWRITER_H_ + +#include "Framework/DataProcessorSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ + +/// create a processor spec +/// write HMP matching info in a root file +o2::framework::DataProcessorSpec getHMPMatchedWriterSpec(bool useMC, const char* outdef = "o2match_hmpid.root"); // int mode = 0, bool strict = false); + +} // namespace hmpid +} // namespace o2 + +#endif /* HMPWORKFLOW_HMPMATCHEDWRITER_H_ */ diff --git a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx index e71cd42845dd4..9ac5074acb505 100644 --- a/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/ClustersReaderSpec.cxx @@ -71,8 +71,8 @@ void ClusterReaderTask::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - pc.outputs().snapshot(Output{"HMP", "CLUSTERS", 0, Lifetime::Timeframe}, mClustersFromFile); - pc.outputs().snapshot(Output{"HMP", "INTRECORDS1", 0, Lifetime::Timeframe}, mClusterTriggersFromFile); + pc.outputs().snapshot(Output{"HMP", "CLUSTERS", 0}, mClustersFromFile); + pc.outputs().snapshot(Output{"HMP", "INTRECORDS1", 0}, mClusterTriggersFromFile); mClustersReceived += mClustersFromFile.size(); LOG(info) << "[HMPID ClusterReader - run() ] clusters = " << mClustersFromFile.size(); diff --git a/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx b/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx index fae72edfa275a..3d5cdb19be338 100644 --- a/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx +++ b/Detectors/HMPID/workflow/src/ClustersWriterSpec.cxx @@ -33,8 +33,7 @@ o2::framework::DataProcessorSpec getClusterWriterSpec() return MakeRootTreeWriterSpec("HMPClustersWriter", "hmpidclusters.root", - "o2hmp", - 1, + MakeRootTreeWriterSpec::TreeAttributes{"o2hmp", "Tree with HMPID clusters"}, BranchDefinition>{InputSpec{"hmpclusterinput", "HMP", "CLUSTERS"}, "HMPIDclusters"}, BranchDefinition>{InputSpec{"hmpinteractionrecords", "HMP", "INTRECORDS1"}, "InteractionRecords"})(); } diff --git a/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx b/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx index e1e7e58d381f8..63f6552e70242 100644 --- a/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/DataDecoderSpec.cxx @@ -87,8 +87,8 @@ void DataDecoderTask::run(framework::ProcessingContext& pc) // decodeReadout(pc); // decodeRawFile(pc); - pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mDigits); - pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mIntReco); + pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0}, mDeco->mDigits); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0}, mDeco->mIntReco); LOG(debug) << "Writing Digitis=" << mDeco->mDigits.size() << "/" << mTotalDigits << " Frame=" << mTotalFrames << " IntRec " << mDeco->mIntReco; mExTimer.elapseMes("Decoding... Digits decoded = " + std::to_string(mTotalDigits) + " Frames received = " + std::to_string(mTotalFrames)); @@ -131,7 +131,7 @@ void DataDecoderTask::endOfStream(framework::EndOfStreamContext& ec) } } char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mRootStatFile.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mRootStatFile.c_str()); mDeco->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDeco->getAverageEventSize(e); @@ -178,7 +178,7 @@ void DataDecoderTask::decodeTF(framework::ProcessingContext& pc) if (payloadSize == 0) { auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } @@ -280,7 +280,7 @@ void DataDecoderTask::decodeRawFile(framework::ProcessingContext& pc) o2::framework::DataProcessorSpec getDecodingSpec(bool askDISTSTF) { std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx b/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx index 0721682ccb7a3..62a46ca5f30d8 100644 --- a/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx +++ b/Detectors/HMPID/workflow/src/DataDecoderSpec2.cxx @@ -93,8 +93,8 @@ void DataDecoderTask2::run(framework::ProcessingContext& pc) // Output the Digits/Triggers vector orderTriggers(); - pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0, o2::framework::Lifetime::Timeframe}, mDeco->mDigits); - pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0, o2::framework::Lifetime::Timeframe}, mTriggers); + pc.outputs().snapshot(o2::framework::Output{"HMP", "DIGITS", 0}, mDeco->mDigits); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS", 0}, mTriggers); mExTimer.elapseMes("Decoding... Digits decoded = " + std::to_string(mTotalDigits) + " Frames received = " + std::to_string(mTotalFrames)); return; @@ -140,7 +140,7 @@ void DataDecoderTask2::endOfStream(framework::EndOfStreamContext& ec) } } char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mRootStatFile.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mRootStatFile.c_str()); mDeco->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDeco->getAverageEventSize(e); @@ -187,7 +187,7 @@ void DataDecoderTask2::decodeTF(framework::ProcessingContext& pc) if (payloadSize == 0) { auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", + LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } @@ -213,7 +213,7 @@ void DataDecoderTask2::decodeTF(framework::ProcessingContext& pc) // The stream end ! LOG(debug) << "End Page decoding !"; } - // std::cout << ">>>>" << pointerToTheFirst << "," << mDeco->mDigits.size() << std::endl; + // std::cout << " fDigit=" << pointerToTheFirst << " lDigit=," << mDeco->mDigits.size() << " nDigit=" << mDeco->mDigits.size()-pointerToTheFirst << std::endl; mTriggers.push_back(o2::hmpid::Trigger(mDeco->mIntReco, pointerToTheFirst, mDeco->mDigits.size() - pointerToTheFirst)); mTotalFrames++; } @@ -336,7 +336,7 @@ void DataDecoderTask2::orderTriggers() o2::framework::DataProcessorSpec getDecodingSpec2(bool askDISTSTF) { std::vector inputs; - inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Optional); + inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"HMP", "RAWDATA"}, o2::framework::Lifetime::Timeframe); if (askDISTSTF) { inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); } diff --git a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx index ed83db476b01a..88f6df2bce2e7 100644 --- a/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx +++ b/Detectors/HMPID/workflow/src/DigitsReaderSpec.cxx @@ -115,8 +115,8 @@ void DigitReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - pc.outputs().snapshot(Output{"HMP", "DIGITS", 0, Lifetime::Timeframe}, mDigitsFromFile); - pc.outputs().snapshot(Output{"HMP", "INTRECORDS", 0, Lifetime::Timeframe}, mTriggersFromFile); + pc.outputs().snapshot(Output{"HMP", "DIGITS", 0}, mDigitsFromFile); + pc.outputs().snapshot(Output{"HMP", "INTRECORDS", 0}, mTriggersFromFile); mDigitsReceived += mDigitsFromFile.size(); LOG(info) << "[HMPID DigitsReader - run() ] digits = " << mDigitsFromFile.size(); diff --git a/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx b/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx index 0f6d7d0cd5a6c..81c04a9875c51 100644 --- a/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx +++ b/Detectors/HMPID/workflow/src/DigitsToClustersSpec.cxx @@ -108,30 +108,19 @@ void DigitsToClustersTask::run(framework::ProcessingContext& pc) size_t(trig.getNumberOfObjects())}; size_t clStart = clusters.size(); mRec->Dig2Clu(trigDigits, clusters, mSigmaCut, true); - clusterTriggers.emplace_back(trig.getIr(), clStart, - clusters.size() - clStart); + clusterTriggers.emplace_back(trig.getIr(), clStart, clusters.size() - clStart); } } LOGP(info, "Received {} triggers with {} digits -> {} triggers with {} clusters", - triggers.size(), digits.size(), clusterTriggers.size(), - clusters.size()); - + triggers.size(), digits.size(), clusterTriggers.size(), clusters.size()); mDigitsReceived += digits.size(); mClustersReceived += clusters.size(); - pc.outputs().snapshot( - o2::framework::Output{"HMP", "CLUSTERS", 0, - o2::framework::Lifetime::Timeframe}, - clusters); - pc.outputs().snapshot( - o2::framework::Output{"HMP", "INTRECORDS1", 0, - o2::framework::Lifetime::Timeframe}, - clusterTriggers); - - mExTimer.elapseMes("Clusterization of Digits received = " + - std::to_string(mDigitsReceived)); - mExTimer.elapseMes("Clusterization of Clusters received = " + - std::to_string(mClustersReceived)); + pc.outputs().snapshot(o2::framework::Output{"HMP", "CLUSTERS", 0}, clusters); + pc.outputs().snapshot(o2::framework::Output{"HMP", "INTRECORDS1", 0}, clusterTriggers); + + mExTimer.elapseMes("Clusterization of Digits received = " + std::to_string(mDigitsReceived)); + mExTimer.elapseMes("Clusterization of Clusters received = " + std::to_string(mClustersReceived)); } void DigitsToClustersTask::endOfStream(framework::EndOfStreamContext& ec) diff --git a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx index 7c22c22c4927d..9ec05efc846fb 100644 --- a/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx @@ -26,11 +26,10 @@ namespace o2 { namespace hmpid { - class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -42,12 +41,13 @@ class EntropyDecoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); mCTFCoder.setSupportBCShifts(true); + mCTFCoder.setDictBinding("ctfdict_HMP"); } void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) @@ -68,8 +68,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc); - auto buff = pc.inputs().get>("ctf"); + mCTFCoder.updateTimeDependentParams(pc, true); + auto buff = pc.inputs().get>("ctf_HMP"); auto& triggers = pc.outputs().make>(OutputRef{"triggers"}); auto& digits = pc.outputs().make>(OutputRef{"digits"}); @@ -90,7 +90,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"triggers"}, "HMP", "INTRECORDS", 0, Lifetime::Timeframe}, @@ -98,17 +98,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) OutputSpec{{"ctfrep"}, "HMP", "CTFDECREP", 0, Lifetime::Timeframe}}; std::vector inputs; - inputs.emplace_back("ctf", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionary")); + inputs.emplace_back("ctf_HMP", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_HMP", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "hmpid-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx index d9f032ec5ef7f..c29c1cee459bc 100644 --- a/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/HMPID/workflow/src/EntropyEncoderSpec.cxx @@ -27,11 +27,10 @@ namespace o2 { namespace hmpid { - class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR = false); + EntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -44,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task TStopwatch mTimer; }; -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -66,13 +65,14 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); mTimer.Start(false); - mCTFCoder.updateTimeDependentParams(pc); + mCTFCoder.updateTimeDependentParams(pc, true); auto triggers = pc.inputs().get>("triggers"); auto digits = pc.inputs().get>("digits"); if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{"HMP", "CTFDATA", 0, Lifetime::Timeframe}); + auto& buffer = pc.outputs().make>(Output{"HMP", "CTFDATA", 0}); + auto iosize = mCTFCoder.encode(buffer, triggers, digits); pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { @@ -88,12 +88,15 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("triggers", "HMP", "INTRECORDS", 0, Lifetime::Timeframe); inputs.emplace_back("digits", "HMP", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionary")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -102,12 +105,11 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{"HMP", "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "HMP", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, - {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}}}; + {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, + {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 diff --git a/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx b/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx new file mode 100644 index 0000000000000..3ddb71e282a96 --- /dev/null +++ b/Detectors/HMPID/workflow/src/HMPMatchedReaderSpec.cxx @@ -0,0 +1,97 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file HMPMatchedReaderSpec.cxx + +#include + +#include "TTree.h" +#include "TFile.h" + +#include "HMPIDWorkflow/HMPMatchedReaderSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Headers/DataHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/MatchingType.h" +#include "CommonUtils/StringUtils.h" +#include "CommonUtils/NameConf.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ +void HMPMatchedReader::init(InitContext& ic) +{ + // get the option from the init context + LOG(debug) << "Init HMPID matching info reader!"; + connectTree(mInFileName); +} + +void HMPMatchedReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mInTreeName.c_str())); + assert(mTree); + mTree->SetBranchAddress("HMPMatchInfo", &mMatchesPtr); + if (mUseMC) { + mTree->SetBranchAddress("MatchHMPMCTruth", &mLabelHMPPtr); + } + LOG(debug) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +void HMPMatchedReader::run(ProcessingContext& pc) +{ + auto currEntry = mTree->GetReadEntry() + 1; + assert(currEntry < mTree->GetEntries()); // this should not happen + mTree->GetEntry(currEntry); + LOG(debug) << "Pushing " << mMatches.size() << " HMP matchings at entry " << currEntry; + + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MATCHES", 0}, mMatches); + if (mUseMC) { + pc.outputs().snapshot(Output{o2::header::gDataOriginHMP, "MCLABELS", 0}, mLabelHMP); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +DataProcessorSpec getHMPMatchedReaderSpec(bool useMC) +{ + std::vector outputs; + outputs.emplace_back(o2::header::gDataOriginHMP, "MATCHES", 0, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(o2::header::gDataOriginHMP, "MCLABELS", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "HMPMatchedReader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask(useMC)}, + /*Options{ + {"hmp-matched-infile", VariantType::String, "o2match_hmp.root", {"Name of the input file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}, + {"treename", VariantType::String, "matchHMP", {"Name of top-level TTree"}}, + }*/ + }; +} +} // namespace hmpid +} // namespace o2 diff --git a/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx b/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx new file mode 100644 index 0000000000000..4c07ec2f5fc08 --- /dev/null +++ b/Detectors/HMPID/workflow/src/HMPMatchedWriterSpec.cxx @@ -0,0 +1,59 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TOFMatchedWriterSpec.cxx + +#include "HMPIDWorkflow/HMPMatchedWriterSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "Headers/DataHeader.h" +#include +#include +#include "ReconstructionDataFormats/MatchInfoHMP.h" +#include "ReconstructionDataFormats/MatchingType.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +#include "DataFormatsHMP/Cluster.h" +#include "CommonUtils/StringUtils.h" +#include + +using namespace o2::framework; + +namespace o2 +{ +namespace hmpid +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using MatchInfo = std::vector; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getHMPMatchedWriterSpec(bool useMC, const char* outdef) //, bool writeTracks, int mode, bool strict) +{ + + const char* taskName = "HMPMatchedWriter"; + + return MakeRootTreeWriterSpec(taskName, + outdef, + "matchHMP", + BranchDefinition{InputSpec{"hmpmatching", gDataOriginHMP, "MATCHES", 0}, + "HMPMatchInfo", + "HMPMatchInfo-branch-name"}, + BranchDefinition{InputSpec{"matchhmplabels", gDataOriginHMP, "MCLABELS", 0}, + "MatchHMPMCTruth", + "MatchHMPMCTruth-branch-name", + (useMC ? 1 : 0)})(); +} +} // namespace hmpid +} // namespace o2 diff --git a/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx b/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx index d94085917db73..bcdf722005c80 100644 --- a/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx +++ b/Detectors/HMPID/workflow/src/RawToDigitsSpec.cxx @@ -302,7 +302,7 @@ void RawToDigitsTask::writeResults() theObj[Geo::N_MODULES]->Branch("Average_Busy_Time", &avgBusyTime, "F"); char summaryFileName[254]; - sprintf(summaryFileName, "%s_stat.txt", mBaseFileName.c_str()); + snprintf(summaryFileName, 254, "%s_stat.txt", mBaseFileName.c_str()); mDecod->writeSummaryFile(summaryFileName); for (int e = 0; e < numEqui; e++) { avgEventSize = mDecod->getAverageEventSize(e); diff --git a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx index fde5e0183abd6..4a1883233b605 100644 --- a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::hmpid::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/ITSMFT/ITS/CMakeLists.txt b/Detectors/ITSMFT/ITS/CMakeLists.txt index 760c4a2775d6c..708556ec8b7ec 100644 --- a/Detectors/ITSMFT/ITS/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/CMakeLists.txt @@ -15,5 +15,6 @@ add_subdirectory(simulation) add_subdirectory(reconstruction) add_subdirectory(tracking) add_subdirectory(workflow) +add_subdirectory(postprocessing) add_subdirectory(macros) add_subdirectory(QC) diff --git a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx index d6ef8c947ed49..90ed033ed67da 100644 --- a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx +++ b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx @@ -139,14 +139,14 @@ void TestDataReader::run(ProcessingContext& pc) if (mDiffFolderName.size() == 0) { cout << "No New Run -- No Need to Reset" << endl; mResetCommand = 0; - pc.outputs().snapshot(Output{"ITS", "TEST", 0, Lifetime::Timeframe}, mResetCommand); + pc.outputs().snapshot(Output{"ITS", "TEST", 0}, mResetCommand); } // New folders found, send the reset signal and reload configuration if (mDiffFolderName.size() > 0) { cout << "New Run Started -- Reset All Histograms" << endl; mResetCommand = 1; - pc.outputs().snapshot(Output{"ITS", "TEST", 0, Lifetime::Timeframe}, mResetCommand); + pc.outputs().snapshot(Output{"ITS", "TEST", 0}, mResetCommand); for (int i = 0; i < sNError; i++) { mErrors[i] = 0; } @@ -210,7 +210,7 @@ void TestDataReader::run(ProcessingContext& pc) size_t pos = mNowFolderNames[i].find_last_of("/"); - if (pos != string::npos) { + if (pos != std::string::npos) { mRunID = mNowFolderNames[i].substr(pos + 1); } @@ -232,7 +232,7 @@ void TestDataReader::run(ProcessingContext& pc) // Getting the FileID string FileIDS; pos = mDiffFileNames[i][0].find_last_of("/"); - if (pos != string::npos) { + if (pos != std::string::npos) { FileIDS = mDiffFileNames[i][0].substr(pos + 1); } @@ -264,11 +264,11 @@ void TestDataReader::run(ProcessingContext& pc) mErrorsVecTest.push_back(mErrors); mFileDone = 1; mFileInfo = mFileDone + mFileRemain * 10; - pc.outputs().snapshot(Output{"ITS", "Run", 0, Lifetime::Timeframe}, mRunNumber); - pc.outputs().snapshot(Output{"ITS", "File", 0, Lifetime::Timeframe}, mFileID); - pc.outputs().snapshot(Output{"ITS", "Error", 0, Lifetime::Timeframe}, mErrorsVecTest[0]); - pc.outputs().snapshot(Output{"ITS", "Finish", 0, Lifetime::Timeframe}, mFileInfo); - pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mMultiDigitsTest); + pc.outputs().snapshot(Output{"ITS", "Run", 0}, mRunNumber); + pc.outputs().snapshot(Output{"ITS", "File", 0}, mFileID); + pc.outputs().snapshot(Output{"ITS", "Error", 0}, mErrorsVecTest[0]); + pc.outputs().snapshot(Output{"ITS", "Finish", 0}, mFileInfo); + pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mMultiDigitsTest); mNewFileInj = 0; mErrorsVecTest.clear(); mDigitsTest.clear(); @@ -422,10 +422,10 @@ void TestDataReader::run(ProcessingContext& pc) cout << "RunIDS = " << mRunNumber << " FileIDS = " << mFileID << endl; - pc.outputs().snapshot(Output{"ITS", "Run", 0, Lifetime::Timeframe}, mRunNumber); - pc.outputs().snapshot(Output{"ITS", "File", 0, Lifetime::Timeframe}, mFileID); + pc.outputs().snapshot(Output{"ITS", "Run", 0}, mRunNumber); + pc.outputs().snapshot(Output{"ITS", "File", 0}, mFileID); - pc.outputs().snapshot(Output{"ITS", "Error", 0, Lifetime::Timeframe}, mErrorsVec[j]); + pc.outputs().snapshot(Output{"ITS", "Error", 0}, mErrorsVec[j]); mIndexPushEx = mIndexPush + mNDigits[j]; LOG(debug) << "IndexPushEx = " << mIndexPushEx << " mDigits.size() " << mDigits.size(); if (mIndexPushEx > mDigits.size() - 5) { @@ -436,11 +436,11 @@ void TestDataReader::run(ProcessingContext& pc) mFileInfo = mFileDone + mFileRemain * 10; - pc.outputs().snapshot(Output{"ITS", "Finish", 0, Lifetime::Timeframe}, mFileInfo); + pc.outputs().snapshot(Output{"ITS", "Finish", 0}, mFileInfo); LOG(debug) << "mIndexPush = " << mIndexPush << " Chip ID Pushing " << mDigits[mIndexPush].getChipIndex(); - pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mMultiDigits); + pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mMultiDigits); mMultiDigits.clear(); mIndexPush = mIndexPush + mNDigits[j]; @@ -453,7 +453,7 @@ void TestDataReader::run(ProcessingContext& pc) // << "mIndexPush = " << mIndexPush << " mDigits.size() = " << mDigits.size(); // while (mIndexPush < mDigits.size()) { // LOG(debug) << "mDigits.size() = " << mDigits.size(); - // pc.outputs().snapshot(Output{"ITS", "DIGITS", 0, Lifetime::Timeframe}, mDigits[mIndexPush++]); + // pc.outputs().snapshot(Output{"ITS", "DIGITS", 0}, mDigits[mIndexPush++]); // if (mIndexPush % 100000 == 0) // LOG(debug) << "mIndexPush = " << mIndexPush << " Chip ID Pushing " << mDigits[mIndexPush].getChipIndex(); // } @@ -483,7 +483,7 @@ void TestDataReader::run(ProcessingContext& pc) j = 0; mNDigits.clear(); mFileDone = 1; - pc.outputs().snapshot(Output{"TST", "Finish", 0, Lifetime::Timeframe}, mFileDone); + pc.outputs().snapshot(Output{"TST", "Finish", 0}, mFileDone); PercentDone = 0; mErrorsVec.clear(); } @@ -509,7 +509,6 @@ void TestDataReader::run(ProcessingContext& pc) std::vector TestDataReader::GetFName(std::string folder) { - DIR* dirp; char cstr[folder.size() + 1]; diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h index 7e3032695ea00..5c7f7c665b5ba 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/DescriptorInnerBarrel.h @@ -36,7 +36,7 @@ class DescriptorInnerBarrel : public TObject DescriptorInnerBarrel(const DescriptorInnerBarrel& src) = delete; DescriptorInnerBarrel& operator=(const DescriptorInnerBarrel& geom) = delete; - double radii2Turbo(double rMin, double rMid, double rMax, double sensW) + double radii2Turbo(double rMin, double rMid, double rMax, double sensW) const { // compute turbo angle from radii and sensor width return TMath::ASin((rMax * rMax - rMin * rMin) / (2 * rMid * sensW)) * TMath::RadToDeg(); @@ -44,8 +44,9 @@ class DescriptorInnerBarrel : public TObject int getNumberOfLayers() const { return mNumLayers; } double getSensorThickness() const { return mSensorLayerThickness; } - void getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan); - TGeoTube* defineWrapperVolume(); + void getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) const; + void setConfigurationWrapperVolume(double minradius, double maxradius, double zspan); + TGeoTube* defineWrapperVolume() const; protected: int mNumLayers{3}; @@ -70,4 +71,4 @@ class DescriptorInnerBarrel : public TObject } // namespace its } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 3e4dde5ee1a56..c8ef445e273d3 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -25,14 +25,15 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsCommonDataFormats/DetID.h" #include "ITSMFTBase/GeometryTGeo.h" -#include "MathUtils/Utils.h" #include "Rtypes.h" // for Int_t, Double_t, Bool_t, UInt_t, etc +#ifdef ENABLE_UPGRADES +#include "ITS3Base/SpecsV2.h" +#endif + class TGeoPNEntry; -namespace o2 -{ -namespace its +namespace o2::its { /// GeometryTGeo is a simple interface class to TGeoManager. It is used in the simulation /// and reconstruction in order to query the TGeo ITS geometry. @@ -43,7 +44,7 @@ namespace its class GeometryTGeo : public o2::itsmft::GeometryTGeo { public: - typedef o2::math_utils::Transform3D Mat3D; + using Mat3D = o2::math_utils::Transform3D; using DetMatrixCache::getMatrixL2G; using DetMatrixCache::getMatrixT2GRot; using DetMatrixCache::getMatrixT2L; @@ -58,28 +59,39 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo return nullptr; // TODO: DR: Obviously wrong, but to make it compile for now #else if (!sInstance) { - sInstance = std::unique_ptr(new GeometryTGeo(true, 0)); + sInstance = std::make_unique(true, 0); } return sInstance.get(); #endif } - // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) - static void adopt(GeometryTGeo* raw); + static bool instanceExist() + { +#ifdef GPUCA_STANDALONE + return false; +#else + return sInstance.get() != nullptr; +#endif + } + // Adopt the unique instance from external raw pointer (to be used only to read saved instance from file) + // When adopting an object owned by the CCDB cache we should not delete it + static void adopt(GeometryTGeo* raw, bool canDelete = false); // constructor // ATTENTION: this class is supposed to behave as a singleton, but to make it root-persistent // we must define public default constructor. // NEVER use it, it will throw exception if the class instance was already created // Use GeometryTGeo::Instance() instead - GeometryTGeo(bool build = kFALSE, int loadTrans = 0 - /*o2::base::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, // default transformations to load - o2::math_utils::TransformType::T2G, - o2::math_utils::TransformType::L2G)*/ - ); + GeometryTGeo(bool build = kFALSE, int loadTrans = 0); - /// Default destructor + /// Default destructor, don't use ~GeometryTGeo() override; + void destroy() + { +#ifndef GPUCA_STANDALONE + sInstance.reset(); // TODO: DR: See above +#endif + } GeometryTGeo(const GeometryTGeo& src) = delete; GeometryTGeo& operator=(const GeometryTGeo& geom) = delete; @@ -97,7 +109,12 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo int getNumberOfChipRowsPerModule(int lay) const { return mNumberOfChipRowsPerModule[lay]; } int getNumberOfChipColsPerModule(int lay) const { - return mNumberOfChipRowsPerModule[lay] ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; +#ifdef ENABLE_UPGRADES + if (mIsLayerITS3[lay]) { + return o2::its3::constants::pixelarray::nCols; + } +#endif + return mNumberOfChipRowsPerModule[lay] != 0 ? mNumberOfChipsPerModule[lay] / mNumberOfChipRowsPerModule[lay] : -1; } int getNumberOfChipsPerModule(int lay) const { return mNumberOfChipsPerModule[lay]; } @@ -159,7 +176,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo bool getChipId(int index, int& lay, int& hba, int& sta, int& ssta, int& mod, int& chip) const; /// Get chip layer, from 0 - int getLayer(int index) const; + int getLayer(int index) const final; /// Get chip half barrel, from 0 int getHalfBarrel(int index) const; @@ -199,7 +216,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo return getSymbolicName(getChipIndex(lay, hba, sta, det)); } - /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by quering the TGeoManager + /// Get the transformation matrix for a given chip (NOT A SENSOR!!!) 'index' by querying the TGeoManager TGeoHMatrix* getMatrix(int index) const { return o2::base::GeometryManager::getMatrix(getDetID(), index); } TGeoHMatrix* getMatrix(int lay, int hba, int sta, int sens) const { return getMatrix(getChipIndex(lay, hba, sta, sens)); } bool getOriginalMatrix(int index, TGeoHMatrix& m) const @@ -218,7 +235,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo const Mat3D& getMatrixT2L(int lay, int hba, int sta, int det) const { return getMatrixT2L(getChipIndex(lay, hba, sta, det)); } const Mat3D& getMatrixSensor(int index) const { return getMatrixL2G(index); } - const Mat3D& getMatrixSensor(int lay, int hba, int sta, int det) + const Mat3D& getMatrixSensor(int lay, int hba, int sta, int det) const { // get positioning matrix of the sensor, alias to getMatrixL2G return getMatrixSensor(getChipIndex(lay, hba, sta, det)); @@ -280,8 +297,24 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* getITS3ModulePattern() { return sModuleNameITS3.c_str(); } static const char* getITS3ChipPattern() { return sChipNameITS3.c_str(); } static const char* getITS3SensorPattern() { return sSensorNameITS3.c_str(); } + + static const char* getITS3LayerPatternRaw() { return sLayerNameITS3.c_str(); }; + static const char* getITS3LayerPattern(int layer) { return Form("%s%d", getITS3LayerPatternRaw(), layer); }; + static const char* getITS3CarbonFormPatternRaw() { return sHalfBarrelNameITS3.c_str(); }; + static const char* getITS3CarbonFormPattern(int layer) { return Form("%s%d", getITS3CarbonFormPatternRaw(), layer); }; + static const char* getITS3ChipPatternRaw() { return sStaveNameITS3.c_str(); }; + static const char* getITS3ChipPattern(int layer) { return Form("%s%d", getITS3ChipPatternRaw(), layer); }; + static const char* getITS3SegmentPatternRaw() { return sHalfStaveNameITS3.c_str(); }; + static const char* getITS3SegmentPattern(int layer) { return Form("%s%d", getITS3SegmentPatternRaw(), layer); }; + static const char* getITS3RSUPatternRaw() { return sModuleNameITS3.c_str(); }; + static const char* getITS3RSUPattern(int layer) { return Form("%s%d", getITS3RSUPatternRaw(), layer); }; + static const char* getITS3TilePatternRaw() { return sChipNameITS3.c_str(); }; + static const char* getITS3TilePattern(int layer) { return Form("%s%d", getITS3TilePatternRaw(), layer); }; + static const char* getITS3PixelArrayPatternRaw() { return sSensorNameITS3.c_str(); }; + static const char* getITS3PixelArrayPattern(int layer) { return Form("%s%d", getITS3PixelArrayPatternRaw(), layer); }; + /// sym name of the layer - static const char* composeSymNameITS(bool isITS3 = false); + static const char* composeSymNameITS(); /// sym name of the layer static const char* composeSymNameLayer(int lr, bool isITS3 = false); @@ -300,15 +333,10 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo /// Sym name of the chip in the given layer/halfbarrel/stave/substave/module static const char* composeSymNameChip(int lr, int hba, int sta, int ssta, int mod, int chip, bool isITS3 = false); - // get tracking frame alpha for ITS3 clusters in global coordinates - float getAlphaFromGlobalITS3(int isn, o2::math_utils::Point3D gloXYZ); - - // create matrix for transformation from tracking frame to local one for ITS3 - const Mat3D getT2LMatrixITS3(int isn, float alpha); + TString getMatrixPath(int index) const; - protected: /// Get the transformation matrix of the SENSOR (not necessary the same as the chip) - /// for a given chip 'index' by quering the TGeoManager + /// for a given chip 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(int index) const; // create matrix for transformation from sensor local frame to global one @@ -317,6 +345,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo // get sensor tracking frame alpha and x void extractSensorXAlpha(int isn, float& x, float& alp); + protected: /// This routine computes the layer number a given the chip index /// \param int index The chip index number, starting from zero. /// \param int indexInLr The chip index inside a layer, starting from zero. @@ -378,7 +407,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo std::vector mNumberOfChipsPerStave; ///< number of chips per stave std::vector mNumberOfChipsPerHalfBarrel; ///< number of chips per halfbarrel std::vector mNumberOfChipsPerLayer; ///< number of chips per stave - std::vector mLastChipIndex; ///< max ID of the detctor in the layer + std::vector mLastChipIndex; ///< max ID of the detector in the layer std::array mIsLayerITS3; ///< flag with the information of the ITS version (ITS2 or ITS3) std::array mLayerToWrapper; ///< Layer to wrapper correspondence @@ -395,22 +424,21 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static std::string sSensorName; ///< Sensor name static std::string sWrapperVolumeName; ///< Wrapper volume name - static std::string sLayerNameITS3; ///< Layer name for ITS3 - static std::string sHalfBarrelNameITS3; ///< HalfBarrel name for ITS3 - static std::string sStaveNameITS3; ///< Stave name for ITS3 - static std::string sHalfStaveNameITS3; ///< HalfStave name for ITS3 - static std::string sModuleNameITS3; ///< Module name for ITS3 - static std::string sChipNameITS3; ///< Chip name for ITS3 - static std::string sSensorNameITS3; ///< Sensor name for ITS3 + static const std::string sLayerNameITS3; ///< Layer name for ITS3 + static const std::string sHalfBarrelNameITS3; ///< HalfBarrel name for ITS3 + static const std::string sStaveNameITS3; ///< Stave name for ITS3 + static const std::string sHalfStaveNameITS3; ///< HalfStave name for ITS3 + static const std::string sModuleNameITS3; ///< Module name for ITS3 + static const std::string sChipNameITS3; ///< Chip name for ITS3 + static const std::string sSensorNameITS3; ///< Sensor name for ITS3 private: #ifndef GPUCA_STANDALONE static std::unique_ptr sInstance; ///< singletone instance #endif - ClassDefOverride(GeometryTGeo, 1); // ITS geometry based on TGeo + ClassDefOverride(GeometryTGeo, 2); // ITS geometry based on TGeo }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx b/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx index 3c9f93ec155df..aa7d1abd54e5e 100644 --- a/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx +++ b/Detectors/ITSMFT/ITS/base/src/DescriptorInnerBarrel.cxx @@ -52,7 +52,7 @@ DescriptorInnerBarrel::DescriptorInnerBarrel(int nlayers) : TObject(), mNumLayer } //________________________________________________________________ -void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) +void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, double& maxradius, double& zspan) const { minradius = mWrapperMinRadius; maxradius = mWrapperMaxRadius; @@ -60,8 +60,17 @@ void DescriptorInnerBarrel::getConfigurationWrapperVolume(double& minradius, dou } //________________________________________________________________ -TGeoTube* DescriptorInnerBarrel::defineWrapperVolume() +void DescriptorInnerBarrel::setConfigurationWrapperVolume(double minradius, double maxradius, double zspan) +{ + mWrapperMinRadius = minradius; + mWrapperMaxRadius = maxradius; + mWrapperZSpan = zspan; +} + +//________________________________________________________________ +TGeoTube* DescriptorInnerBarrel::defineWrapperVolume() const { TGeoTube* wrap = new TGeoTube(mWrapperMinRadius, mWrapperMaxRadius, mWrapperZSpan / 2.); + LOGP(debug, "Creating IB Wrappervolume with Rmin={}, Rmax={}, ZSpan={}", mWrapperMinRadius, mWrapperMaxRadius, mWrapperZSpan); return wrap; -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 8885510d77447..5dc499d05037e 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -23,7 +23,7 @@ #include "MathUtils/Cartesian.h" #ifdef ENABLE_UPGRADES -#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITS3Base/SpecsV2.h" #endif #include // for TGeoBBox @@ -43,6 +43,7 @@ #include // for isdigit #include // for snprintf, NULL, printf #include // for strstr, strlen +#include using namespace TMath; using namespace o2::its; @@ -50,14 +51,16 @@ using namespace o2::detectors; using Segmentation = o2::itsmft::SegmentationAlpide; -#ifdef ENABLE_UPGRADES -using SegmentationITS3 = o2::its3::SegmentationSuperAlpide; -#endif - ClassImp(o2::its::GeometryTGeo); std::unique_ptr GeometryTGeo::sInstance; -o2::its::GeometryTGeo::~GeometryTGeo() = default; +o2::its::GeometryTGeo::~GeometryTGeo() +{ + if (!mOwner) { + mOwner = true; + sInstance.release(); + } +} std::string GeometryTGeo::sVolumeName = "ITSV"; ///< Mother volume name std::string GeometryTGeo::sLayerName = "ITSULayer"; ///< Layer name @@ -69,13 +72,13 @@ std::string GeometryTGeo::sChipName = "ITSUChip"; ///< Chip name std::string GeometryTGeo::sSensorName = "ITSUSensor"; ///< Sensor name std::string GeometryTGeo::sWrapperVolumeName = "ITSUWrapVol"; ///< Wrapper volume name -std::string GeometryTGeo::sLayerNameITS3 = "ITS3Layer"; ///< Layer name for ITS3 -std::string GeometryTGeo::sHalfBarrelNameITS3 = "ITS3HalfBarrel"; ///< HalfBarrel name for ITS3 -std::string GeometryTGeo::sStaveNameITS3 = "ITS3Stave"; ///< Stave name for ITS3 -std::string GeometryTGeo::sHalfStaveNameITS3 = "ITS3HalfStave"; ///< HalfStave name for ITS3 -std::string GeometryTGeo::sModuleNameITS3 = "ITS3Module"; ///< Module name for ITS3 -std::string GeometryTGeo::sChipNameITS3 = "ITS3Chip"; ///< Chip name for ITS3 -std::string GeometryTGeo::sSensorNameITS3 = "ITS3Sensor"; ///< Sensor name for ITS3 +const std::string GeometryTGeo::sLayerNameITS3 = "ITS3Layer"; ///< Layer name for ITS3 +const std::string GeometryTGeo::sHalfBarrelNameITS3 = "ITS3CarbonForm"; ///< HalfBarrel name for ITS3 +const std::string GeometryTGeo::sStaveNameITS3 = "ITS3Chip"; ///< Stave name for ITS3 +const std::string GeometryTGeo::sHalfStaveNameITS3 = "ITS3Segment"; ///< HalfStave name for ITS3 +const std::string GeometryTGeo::sModuleNameITS3 = "ITS3RSU"; ///< Module name for ITS3 +const std::string GeometryTGeo::sChipNameITS3 = "ITS3Tile"; ///< Chip name for ITS3 +const std::string GeometryTGeo::sSensorNameITS3 = "ITS3PixelArray"; ///< Sensor name for ITS3 //__________________________________________________________________________ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo(DetID::ITS) @@ -87,22 +90,21 @@ GeometryTGeo::GeometryTGeo(bool build, int loadTrans) : o2::itsmft::GeometryTGeo // throw std::runtime_error("Invalid use of public constructor: o2::its::GeometryTGeo instance exists"); } - for (int i = MAXLAYERS; i--;) { - mLayerToWrapper[i] = -1; - } + mLayerToWrapper.fill(-1); if (build) { Build(loadTrans); } } //__________________________________________________________________________ -void GeometryTGeo::adopt(GeometryTGeo* raw) +void GeometryTGeo::adopt(GeometryTGeo* raw, bool canDelete) { // adopt the unique instance from external raw pointer (to be used only to read saved instance from file) if (sInstance) { LOG(fatal) << "No adoption: o2::its::GeometryTGeo instance exists"; } sInstance = std::unique_ptr(raw); + sInstance->mOwner = canDelete; } //__________________________________________________________________________ @@ -205,7 +207,7 @@ int GeometryTGeo::getModule(int index) const } index -= getFirstChipIndex(lay); index %= mNumberOfChipsPerStave[lay]; - if (mNumberOfHalfStaves[lay]) { + if (mNumberOfHalfStaves[lay] != 0) { index %= mNumberOfChipsPerHalfStave[lay]; } return index / mNumberOfChipsPerModule[lay]; @@ -288,14 +290,8 @@ bool GeometryTGeo::getChipId(int index, int& lay, int& hba, int& sta, int& hsta, } //__________________________________________________________________________ -const char* GeometryTGeo::composeSymNameITS(bool isITS3) +const char* GeometryTGeo::composeSymNameITS() { - if (isITS3) { -#ifdef ENABLE_UPGRADES - return o2::detectors::DetID(o2::detectors::DetID::IT3).getName(); -#endif - } - return o2::detectors::DetID(o2::detectors::DetID::ITS).getName(); } @@ -338,16 +334,8 @@ const char* GeometryTGeo::composeSymNameChip(int lr, int hba, int sta, int subst return Form("%s/%s%d", composeSymNameModule(lr, hba, sta, substave, mod, isITS3), isITS3 ? getITS3ChipPattern() : getITSChipPattern(), chip); } -//__________________________________________________________________________ -TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +TString GeometryTGeo::getMatrixPath(int index) const { - // extract matrix transforming from the PHYSICAL sensor frame to global one - // Note, the if the effective sensitive layer thickness is smaller than the - // total physical sensor tickness, this matrix is biased and connot be used - // directly for transformation from sensor frame to global one. - // - // Therefore we need to add a shift - int lay, hba, stav, sstav, mod, chipInMod; getChipId(index, lay, hba, stav, sstav, mod, chipInMod); @@ -359,26 +347,50 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const path += Form("%s%d_1/", getITSWrapVolPattern(), wrID); } - path += - Form("%s%d_1/", mIsLayerITS3[lay] ? getITS3LayerPattern() : getITSLayerPattern(), lay); - - if (mNumberOfHalfBarrels > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3HalfBarrelPattern() : getITSHalfBarrelPattern(), lay, hba); - } - path += - Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay, stav); - - if (mNumberOfHalfStaves[lay] > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern(), lay, sstav); - } - if (mNumberOfModules[lay] > 0) { - path += Form("%s%d_%d/", mIsLayerITS3[lay] ? getITS3ModulePattern() : getITSModulePattern(), lay, mod); - } if (!mIsLayerITS3[lay]) { + path += + Form("%s%d_1/", getITSLayerPattern(), lay); + if (mNumberOfHalfBarrels > 0) { + path += Form("%s%d_%d/", getITSHalfBarrelPattern(), lay, hba); + } + path += + Form("%s%d_%d/", getITSStavePattern(), lay, stav); + + if (mNumberOfHalfStaves[lay] > 0) { + path += Form("%s%d_%d/", getITSHalfStavePattern(), lay, sstav); + } + if (mNumberOfModules[lay] > 0) { + path += Form("%s%d_%d/", getITSModulePattern(), lay, mod); + } path += Form("%s%d_%d/%s%d_1", getITSChipPattern(), lay, chipInMod, getITSSensorPattern(), lay); } else { - path += Form("%s%d_%d", getITS3ChipPattern(), lay, chipInMod); // for ITS3 currently we might have more sensors than chips, so we have to take the chip to avoid mismatches with chipId + // hba = carbonform + // stav = 0 + // sstav = segment + // mod = rsu + // chipInMod = tile + // sensor = pixelarray + path += Form("%s_0/", getITS3LayerPattern(lay)); + path += Form("%s_%d/", getITS3CarbonFormPattern(lay), hba); + path += Form("%s_0/", getITS3ChipPattern(lay)); + path += Form("%s_%d/", getITS3SegmentPattern(lay), sstav); + path += Form("%s_%d/", getITS3RSUPattern(lay), mod); + path += Form("%s_%d/", getITS3TilePattern(lay), chipInMod); + path += Form("%s_0", getITS3PixelArrayPattern(lay)); } + return path; +} + +//__________________________________________________________________________ +TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const +{ + // extract matrix transforming from the PHYSICAL sensor frame to global one + // Note, the if the effective sensitive layer thickness is smaller than the + // total physical sensor tickness, this matrix is biased and connot be used + // directly for transformation from sensor frame to global one. + // + // Therefore we need to add a shift + auto path = getMatrixPath(index); static TGeoHMatrix matTmp; gGeoManager->PushPath(); @@ -390,56 +402,30 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const } // end if !gGeoManager matTmp = *gGeoManager->GetCurrentMatrix(); // matrix may change after cd + // RSS // printf("%d/%d/%d %s\n", lay, stav, detInSta, path.Data()); // matTmp.Print(); // Restore the modeler state. gGeoManager->PopPath(); - // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor ticknesses - double delta = 0.; - if (mIsLayerITS3[lay]) { -#ifdef ENABLE_UPGRADES - return &matTmp; -#endif - } + static int chipInGlo{0}; - delta = Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff; + // account for the difference between physical sensitive layer (where charge collection is simulated) and effective sensor thicknesses + // in the ITS3 case this accounted by specialized functions + double delta = Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff; static TGeoTranslation tra(0., 0.5 * delta, 0.); - +#ifdef ENABLE_UPGRADES // only apply for non ITS3 OB layers + if (!mIsLayerITS3[getLayer(index)]) { + matTmp *= tra; + } +#else matTmp *= tra; +#endif return &matTmp; } -//__________________________________________________________________________ -float GeometryTGeo::getAlphaFromGlobalITS3(int isn, o2::math_utils::Point3D gloXYZ) -{ - // calculate the tracking alpha of the ITS3 cluster in global coordinates - const TGeoHMatrix* matL2G = extractMatrixSensor(isn); - auto matG2L = matL2G->Inverse(); - auto translation = matG2L.GetTranslation(); // we only need the translation - o2::math_utils::Point3D gloXYZtra{gloXYZ.x() + (float)translation[0], gloXYZ.y() + (float)translation[1], gloXYZ.z() + (float)translation[2]}; - - float alp = ATan2(gloXYZtra.y(), gloXYZtra.x()); - o2::math_utils::bringTo02Pi(alp); - - return alp; -} - -//__________________________________________________________________________ -const o2::math_utils::Transform3D GeometryTGeo::getT2LMatrixITS3(int isn, float alpha) -{ - // create for sensor isn the TGeo matrix for Tracking to Local frame transformations - static TGeoHMatrix t2l; - t2l.Clear(); - t2l.RotateZ(alpha * RadToDeg()); // rotate in direction of normal to the tangent to the cylinder - const TGeoHMatrix* matL2G = extractMatrixSensor(isn); - const TGeoHMatrix& matL2Gi = matL2G->Inverse(); - t2l.MultiplyLeft(&matL2Gi); - return Mat3D(t2l); -} - //__________________________________________________________________________ void GeometryTGeo::Build(int loadTrans) { @@ -448,16 +434,14 @@ void GeometryTGeo::Build(int loadTrans) return; // already initialized } - if (!gGeoManager) { + if (gGeoManager == nullptr) { // RSTODO: in future there will be a method to load matrices from the CDB LOG(fatal) << "Geometry is not loaded"; } - for (int i = 0; i < MAXLAYERS; ++i) { - mIsLayerITS3[i] = false; - } + mIsLayerITS3.fill(false); mNumberOfLayers = extractNumberOfLayers(); - if (!mNumberOfLayers) { + if (mNumberOfLayers == 0) { return; } @@ -486,9 +470,16 @@ void GeometryTGeo::Build(int loadTrans) numberOfChips += mNumberOfChipsPerLayer[i]; mLastChipIndex[i] = numberOfChips - 1; } + +#ifdef ENABLE_UPGRADES + if (std::any_of(mIsLayerITS3.cbegin(), mIsLayerITS3.cend(), [](auto b) { return b; })) { + LOGP(info, "Found active IT3 layers -> Renaming Detector ITS to IT3"); + mDetID = DetID::IT3; + } +#endif + setSize(numberOfChips); fillTrackingFramesCache(); - // fillMatrixCache(loadTrans); } @@ -506,7 +497,7 @@ void GeometryTGeo::fillMatrixCache(int mask) // build matrices if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) && !getCacheL2G().isFilled()) { // Matrices for Local (Sensor!!! rather than the full chip) to Global frame transformation - LOG(info) << "Loading ITS L2G matrices from TGeo"; + LOGP(info, "Loading {} L2G matrices from TGeo; there are {} matrices", getName(), mSize); auto& cacheL2G = getCacheL2G(); cacheL2G.setSize(mSize); @@ -518,7 +509,7 @@ void GeometryTGeo::fillMatrixCache(int mask) if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) && !getCacheT2L().isFilled()) { // matrices for Tracking to Local (Sensor!!! rather than the full chip) frame transformation - LOG(info) << "Loading ITS T2L matrices from TGeo"; + LOGP(info, "Loading {} T2L matrices from TGeo", getName()); auto& cacheT2L = getCacheT2L(); cacheT2L.setSize(mSize); for (int i = 0; i < mSize; i++) { @@ -527,29 +518,34 @@ void GeometryTGeo::fillMatrixCache(int mask) } } - if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) && !getCacheT2G().isFilled()) { - LOG(warning) << "It is faster to use 2D rotation for T2G instead of full Transform3D matrices"; - // matrices for Tracking to Global frame transformation - LOG(info) << "Loading ITS T2G matrices from TGeo"; - auto& cacheT2G = getCacheT2G(); - cacheT2G.setSize(mSize); - - for (int i = 0; i < mSize; i++) { - TGeoHMatrix& mat = createT2LMatrix(i); - mat.MultiplyLeft(extractMatrixSensor(i)); - cacheT2G.setMatrix(Mat3D(mat), i); - } - } - if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2GRot)) && !getCacheT2GRot().isFilled()) { // 2D rotation matrices for Tracking frame to Global rotations - LOG(info) << "Loading ITS T2G rotation 2D matrices"; + LOGP(info, "Loading {} T2G rotation 2D matrices", getName()); auto& cacheT2Gr = getCacheT2GRot(); cacheT2Gr.setSize(mSize); for (int i = 0; i < mSize; i++) { cacheT2Gr.setMatrix(Rot2D(getSensorRefAlpha(i)), i); } } + + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2G)) && !getCacheT2G().isFilled()) { + LOG(debug) << "It is faster to use 2D rotation for T2G instead of full Transform3D matrices"; + // matrices for Tracking to Global frame transformation + LOGP(info, "Creating {} T2G matrices from TGeo", getName()); + auto& cacheT2G = getCacheT2G(); + cacheT2G.setSize(mSize); + + for (int i = 0; i < mSize; i++) { + /* + TGeoHMatrix& mat = createT2LMatrix(i); + mat.MultiplyLeft(extractMatrixSensor(i)); + */ + Rot2D r(getSensorRefAlpha(i)); + Mat3D mat{}; + mat.SetComponents(r.getCos(), -r.getSin(), 0., 0., r.getSin(), r.getCos(), 0., 0., 0., 0., 1., 0.); + cacheT2G.setMatrix(mat, i); + } + } } //__________________________________________________________________________ @@ -572,47 +568,42 @@ int GeometryTGeo::extractNumberOfLayers() int numberOfLayers = 0; TGeoVolume* itsV = gGeoManager->GetVolume(getITSVolPattern()); - if (!itsV) { - LOG(fatal) << "ITS volume " << getITSVolPattern() << " is not in the geometry"; + if (itsV == nullptr) { + LOG(fatal) << getName() << " volume " << getITSVolPattern() << " is not in the geometry"; } // Loop on all ITSV nodes, count Layer volumes by checking names // Build on the fly layer - wrapper correspondence TObjArray* nodes = itsV->GetNodes(); int nNodes = nodes->GetEntriesFast(); - for (int j = 0; j < nNodes; j++) { int lrID = -1; - TGeoNode* nd = (TGeoNode*)nodes->At(j); + auto nd = dynamic_cast(nodes->At(j)); const char* name = nd->GetName(); - if (strstr(name, getITSLayerPattern()) || strstr(name, getITS3LayerPattern())) { + if ((strstr(name, getITSLayerPattern()) != nullptr) || (strstr(name, getITS3LayerPattern()) != nullptr)) { numberOfLayers++; if ((lrID = extractVolumeCopy(name, GeometryTGeo::getITSLayerPattern())) < 0) { if ((lrID = extractVolumeCopy(name, GeometryTGeo::getITS3LayerPattern())) < 0) { LOG(fatal) << "Failed to extract layer ID from the " << name; - exit(1); } mIsLayerITS3[lrID] = true; } - mLayerToWrapper[lrID] = -1; // not wrapped - } else if (strstr(name, getITSWrapVolPattern())) { // this is a wrapper volume, may cointain layers + mLayerToWrapper[lrID] = -1; // not wrapped + } else if (strstr(name, getITSWrapVolPattern()) != nullptr) { // this is a wrapper volume, may cointain layers int wrID = -1; if ((wrID = extractVolumeCopy(name, GeometryTGeo::getITSWrapVolPattern())) < 0) { LOG(fatal) << "Failed to extract wrapper ID from the " << name; - exit(1); } - TObjArray* nodesW = nd->GetNodes(); int nNodesW = nodesW->GetEntriesFast(); for (int jw = 0; jw < nNodesW; jw++) { - TGeoNode* ndW = (TGeoNode*)nodesW->At(jw); - if (strstr(ndW->GetName(), getITSLayerPattern()) || strstr(ndW->GetName(), getITS3LayerPattern())) { - if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getITSLayerPattern())) < 0) { - if ((lrID = extractVolumeCopy(ndW->GetName(), GeometryTGeo::getITS3LayerPattern())) < 0) { - LOG(fatal) << "Failed to extract layer ID from the " << name; - exit(1); + auto ndW = dynamic_cast(nodesW->At(jw))->GetName(); + if ((strstr(ndW, getITSLayerPattern()) != nullptr) || (strstr(ndW, getITS3LayerPattern()) != nullptr)) { + if ((lrID = extractVolumeCopy(ndW, GeometryTGeo::getITSLayerPattern())) < 0) { + if ((lrID = extractVolumeCopy(ndW, GeometryTGeo::getITS3LayerPattern())) < 0) { + LOGP(fatal, "Failed to extract layer ID from wrapper volume '{}' from one of its nodes '{}'", name, ndW); } mIsLayerITS3[lrID] = true; } @@ -645,8 +636,8 @@ int GeometryTGeo::extractNumberOfStaves(int lay) const snprintf(hbarnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3HalfBarrelPattern() : getITSHalfBarrelPattern(), lay); } TGeoVolume* volHb = gGeoManager->GetVolume(hbarnam); - if (!volHb) { - LOG(fatal) << "can't find " << hbarnam << " volume"; + if (volHb == nullptr) { + LOGP(fatal, "Can't find '{}' volume (ITS3={})", hbarnam, mIsLayerITS3[lay]); return -1; } @@ -656,7 +647,7 @@ int GeometryTGeo::extractNumberOfStaves(int lay) const // LOG(info) << "L" << lay << " " << j << " of " << nNodes << " " // << volHb->GetNodes()->At(j)->GetName() << " " // << mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern() << " -> " << numberOfStaves; - if (strstr(volHb->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern())) { + if (strstr(volHb->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern()) != nullptr) { numberOfStaves++; } } @@ -673,13 +664,13 @@ int GeometryTGeo::extractNumberOfHalfStaves(int lay) const char stavnam[30]; snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay); TGeoVolume* volLd = gGeoManager->GetVolume(stavnam); - if (!volLd) { + if (volLd == nullptr) { LOG(fatal) << "can't find volume " << stavnam; } // Loop on all stave nodes, count Chip volumes by checking names int nNodes = volLd->GetNodes()->GetEntries(); for (int j = 0; j < nNodes; j++) { - if (strstr(volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern())) { + if (strstr(volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern()) != nullptr) { nSS++; } } @@ -724,22 +715,31 @@ int GeometryTGeo::extractNumberOfModules(int lay) const //__________________________________________________________________________ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const { +#ifdef ENABLE_UPGRADES + // FS: TODO + // For now we hardcode ITS3 number of chips is eq. to the number of tiles per RSU + // The test in the end does not work for ITS3. + if (mIsLayerITS3[lay]) { + nrow = o2::its3::constants::pixelarray::nRows; + return o2::its3::constants::rsu::nTiles; + } +#endif int numberOfChips = 0; char stavnam[30]; TGeoVolume* volLd = nullptr; if (!sModuleName.empty()) { - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3ModulePattern() : getITSModulePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSModulePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } if (!volLd) { // no modules on this layer, check substaves if (!sHalfStaveName.empty()) { - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3HalfStavePattern() : getITSHalfStavePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSHalfStavePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } } if (!volLd) { // no substaves on this layer, check staves - snprintf(stavnam, 30, "%s%d", mIsLayerITS3[lay] ? getITS3StavePattern() : getITSStavePattern(), lay); + snprintf(stavnam, 30, "%s%d", getITSStavePattern(), lay); volLd = gGeoManager->GetVolume(stavnam); } if (!volLd) { @@ -754,9 +754,8 @@ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const double dx = -1, dz = -1; for (int j = 0; j < nNodes; j++) { - LOGP(debug, "layer={}, node/nodes={}/{}, node name={}, pattern={}, number of chips={}, is ITS3 layer={}", lay, j, nNodes, volLd->GetNodes()->At(j)->GetName(), mIsLayerITS3[lay] ? getITS3ChipPattern() : getITSChipPattern(), numberOfChips, mIsLayerITS3[lay]); TGeoNodeMatrix* node = (TGeoNodeMatrix*)volLd->GetNodes()->At(j); - if (!strstr(node->GetName(), mIsLayerITS3[lay] ? getITS3ChipPattern() : getITSChipPattern())) { + if (strstr(node->GetName(), getITSChipPattern()) == nullptr) { continue; } node->LocalToMaster(loc, lab); @@ -794,7 +793,9 @@ int GeometryTGeo::extractNumberOfChipsPerModule(int lay, int& nrow) const if (nrow * ncol != numberOfChips) { LOG(error) << "Inconsistency between Nchips=" << numberOfChips << " and Nrow*Ncol=" << nrow << "*" << ncol << "->" << nrow * ncol << "\n" - << "Extracted chip dimensions (x,z): " << dx << " " << dz << " Module Span: " << spanX << " " << spanZ; + << "Extracted chip dimensions (x,z): " << dx << " " << dz << " Module Span: " << spanX << " " << spanZ << "\n" + << "xmin=" << xmin << " xmax=" << xmax + << " zmin=" << zmin << " zmax=" << zmax; } return numberOfChips; } @@ -815,19 +816,21 @@ int GeometryTGeo::extractLayerChipType(int lay) const //__________________________________________________________________________ void GeometryTGeo::Print(Option_t*) const { - printf("NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); if (!isBuilt()) { + LOGF(info, "Geometry not built yet!"); return; } + LOGF(info, "Summary of GeometryTGeo: %s", getName()); + LOGF(info, "NLayers:%d NChips:%d\n", mNumberOfLayers, getNumberOfChips()); for (int i = 0; i < mNumberOfLayers; i++) { - printf( - "Lr%2d\tNStav:%2d\tNChips:%2d " - "(%dx%-2d)\tNMod:%d\tNSubSt:%d\tNSt:%3d\tChip#:%5d:%-5d\tWrapVol:%d\n", - i, mNumberOfStaves[i], mNumberOfChipsPerModule[i], mNumberOfChipRowsPerModule[i], - mNumberOfChipRowsPerModule[i] ? mNumberOfChipsPerModule[i] / mNumberOfChipRowsPerModule[i] : 0, - mNumberOfModules[i], mNumberOfHalfStaves[i], mNumberOfStaves[i], getFirstChipIndex(i), getLastChipIndex(i), - mLayerToWrapper[i]); + LOGF(info, + "Lr%2d\tNStav:%2d\tNChips:%2d " + "(%dx%-2d)\tNMod:%d\tNSubSt:%d\tNSt:%3d\tChip#:%5d:%-5d\tWrapVol:%d", + i, mNumberOfStaves[i], mNumberOfChipsPerModule[i], mNumberOfChipRowsPerModule[i], + mNumberOfChipRowsPerModule[i] ? mNumberOfChipsPerModule[i] / mNumberOfChipRowsPerModule[i] : 0, + mNumberOfModules[i], mNumberOfHalfStaves[i], mNumberOfStaves[i], getFirstChipIndex(i), getLastChipIndex(i), + mLayerToWrapper[i]); } } @@ -839,26 +842,39 @@ void GeometryTGeo::extractSensorXAlpha(int isn, float& x, float& alp) const TGeoHMatrix* matL2G = extractMatrixSensor(isn); double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; - int iLayer = getLayer(isn); + double xp{0}, yp{0}; - if (mIsLayerITS3[iLayer]) { - // in this case we need the line tangent to the circumference - double radius = 0.; #ifdef ENABLE_UPGRADES - SegmentationITS3 seg(iLayer); - radius = seg.mRadii[iLayer]; -#endif - locA[1] = radius; - locB[1] = radius; - } - + if (int iLayer = getLayer(isn); mIsLayerITS3[iLayer]) { + // For a TGeoTubeSeg the local coordinate system is defined at the origin + // of the circle of the side, since in our implementation we rotated the geometry a bit + const auto radius = o2::its3::constants::radii[iLayer]; + const auto phi1 = o2::its3::constants::tile::width / radius; + const auto phi2 = o2::its3::constants::pixelarray::width / radius + phi1; + const auto phi3 = (phi2 - phi1) / 2.; // mid-point in phi + locA[0] = radius * std::cos(phi3); + locA[1] = radius * std::sin(phi3); + matL2G->LocalToMaster(locA, gloA); + xp = gloA[0]; + yp = gloA[1]; + } else { + matL2G->LocalToMaster(locA, gloA); + matL2G->LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; + } +#else // just ITS2 part matL2G->LocalToMaster(locA, gloA); matL2G->LocalToMaster(locB, gloB); double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); - double xp = gloB[0] - dx * t, yp = gloB[1] - dy * t; - x = Sqrt(xp * xp + yp * yp); - alp = ATan2(yp, xp); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; +#endif + x = std::hypot(xp, yp); + alp = std::atan2(yp, xp); o2::math_utils::bringTo02Pi(alp); } diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx index 04a87f06480f3..3ffa500b8516e 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibrator.cxx @@ -32,7 +32,7 @@ bool NoiseCalibrator::processTimeFrameClusters(gsl::span const& rofs) { static int nTF = 0; - LOG(info) << "Processing TF# " << nTF++ << " of " << clusters.size() << " clusters in" << rofs.size() << " ROFs"; + LOG(detail) << "Processing TF# " << nTF++ << " of " << clusters.size() << " clusters in" << rofs.size() << " ROFs"; // extract hits auto pattIt = patterns.begin(); mChipIDs.clear(); @@ -104,7 +104,7 @@ bool NoiseCalibrator::processTimeFrameDigits(gsl::span gsl::span const& rofs) { static int nTF = 0; - LOG(info) << "Processing TF# " << nTF++ << " of " << digits.size() << " digits in " << rofs.size() << " ROFs"; + LOG(detail) << "Processing TF# " << nTF++ << " of " << digits.size() << " digits in " << rofs.size() << " ROFs"; mChipIDs.clear(); for (const auto& rof : rofs) { int chipID = -1; diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx index 479dcad6e4b28..e50f5be3edf5e 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseCalibratorSpec.cxx @@ -105,13 +105,13 @@ void NoiseCalibratorSpec::run(ProcessingContext& pc) done = (++mNPartsDone == partInfo[1]); mStrobeCounter += partInfo[2]; mCalibrator->setNStrobes(mStrobeCounter); - LOGP(info, "Received accumulated map {} of {} with {} ROFs, total number of maps = {} and strobes = {}", partInfo[0] + 1, partInfo[1], partInfo[2], mNPartsDone, mCalibrator->getNStrobes()); + LOGP(important, "Received accumulated map {} of {} with {} ROFs, total number of maps = {} and strobes = {}", partInfo[0] + 1, partInfo[1], partInfo[2], mNPartsDone, mCalibrator->getNStrobes()); } if (done || pc.transitionState() == TransitionHandlingState::Requested) { if (done) { - LOG(info) << "Minimum number of noise counts has been reached !"; + LOG(important) << "Minimum number of noise counts has been reached !"; } else { - LOG(info) << "Run stop is requested, sending output"; + LOG(important) << "Run stop is requested, sending output"; } if (mMode == ProcessingMode::Full || mMode == ProcessingMode::Normalize) { sendOutput(pc.outputs()); @@ -134,7 +134,7 @@ void NoiseCalibratorSpec::sendAccumulatedMap(DataAllocator& output) outInf.push_back(mCalibrator->getNInstances()); outInf.push_back(mCalibrator->getNStrobes()); output.snapshot(Output{"ITS", "NOISEMAPPARTINF", (unsigned int)mCalibrator->getInstanceID()}, outInf); - LOGP(info, "Sending accumulated map with {} ROFs processed", mCalibrator->getNStrobes()); + LOGP(important, "Sending accumulated map with {} ROFs processed", mCalibrator->getNStrobes()); } void NoiseCalibratorSpec::sendOutput(DataAllocator& output) @@ -271,9 +271,9 @@ DataProcessorSpec getNoiseCalibratorSpec(bool useClusters, int pmode) inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); } } else { - useClusters = false; // not needed for normalization - inputs.emplace_back("mapspart", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPART"}, Lifetime::Timeframe); // for normalization of multiple inputs only - inputs.emplace_back("mapspartInfo", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPARTINF"}, Lifetime::Timeframe); // for normalization of multiple inputs only + useClusters = false; // not needed for normalization + inputs.emplace_back("mapspart", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPART"}, Lifetime::Sporadic); // for normalization of multiple inputs only + inputs.emplace_back("mapspartInfo", ConcreteDataTypeMatcher{"ITS", "NOISEMAPPARTINF"}, Lifetime::Sporadic); // for normalization of multiple inputs only } if (md == NoiseCalibratorSpec::ProcessingMode::Full || md == NoiseCalibratorSpec::ProcessingMode::Normalize) { inputs.emplace_back("confdbmap", "ITS", "CONFDBMAP", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/Confdbmap")); diff --git a/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx b/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx index a687da5eda53e..c4faf788f6afe 100644 --- a/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx +++ b/Detectors/ITSMFT/ITS/calibration/src/NoiseSlotCalibrator.cxx @@ -29,7 +29,7 @@ bool NoiseSlotCalibrator::processTimeFrame(gsl::span const& rofs) { calibration::TFType nTF = rofs[0].getBCData().orbit / 256; - LOG(info) << "Processing TF# " << nTF; + LOG(detail) << "Processing TF# " << nTF; auto& slotTF = getSlotForTF(nTF); auto& noiseMap = *(slotTF.getContainer()); diff --git a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt index 8c6767cfdad89..d651d94fbc07a 100644 --- a/Detectors/ITSMFT/ITS/macros/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(EVE) add_subdirectory(test) +add_subdirectory(DCS) diff --git a/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt new file mode 100644 index 0000000000000..5850bebbeae05 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/DCS/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro( + makeITSCCDBEntryForDCS.C + PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB) + +install( + FILES makeITSCCDBEntryForDCS.C + DESTINATION share/macro/) diff --git a/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C b/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C new file mode 100644 index 0000000000000..482f48132f80b --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/DCS/makeITSCCDBEntryForDCS.C @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "CommonUtils/NameConf.h" +#endif +#include +#include +#include "TFile.h" +#include "DetectorsDCS/AliasExpander.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include +#include +#include "CCDB/CcdbApi.h" + +using DPID = o2::dcs::DataPointIdentifier; + +int makeITSCCDBEntryForDCS(std::string ccdb_path = o2::base::NameConf::getCCDBServer()) +{ + + std::unordered_map dpid2DataDesc; + std::vector aliases; + + // fill aliases + int nStaves[] = {12, 16, 20, 24, 30, 42, 48}; + for (int iL = 0; iL < 7; iL++) { + for (int iS = 0; iS < nStaves[iL]; iS++) { + std::string stv = iS > 9 ? std::to_string(iS) : std::string(1, '0').append(std::to_string(iS)); + aliases.push_back("ITS_L" + std::to_string(iL) + "_" + stv + "_STROBE"); + } + } + + std::vector expaliases = o2::dcs::expandAliases(aliases); + + DPID dpidtmp; + for (size_t i = 0; i < expaliases.size(); ++i) { + DPID::FILL(dpidtmp, expaliases[i], o2::dcs::DeliveryType::DPVAL_INT); + dpid2DataDesc[dpidtmp] = "ITSDATAPOINTS"; + } + + o2::ccdb::CcdbApi api; + api.init(ccdb_path); + std::map md; + md["comment"] = "uploaded with O2 makeITSCCDBEntryForDCS.C"; + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + api.storeAsTFileAny(&dpid2DataDesc, "ITS/Config/DCSDPconfig", md, ts, ts + 365L * 10 * 24 * 3600 * 1000); // validity is 10 years + + return 0; +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index 550e0373e4f44..dd6aacf65db99 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -18,7 +18,7 @@ o2_add_test_root_macro(CheckClusters.C PUBLIC_LINK_LIBRARIES O2::ITSBase O2::DataFormatsITSMFT O2::ITSMFTSimulation O2::MathUtils O2::SimulationDataFormat - LABELS its) + LABELS its COMPILE_ONLY) o2_add_test_root_macro(CheckDigits.C PUBLIC_LINK_LIBRARIES O2::ITSBase @@ -27,7 +27,7 @@ o2_add_test_root_macro(CheckDigits.C O2::MathUtils O2::SimulationDataFormat O2::DetectorsBase - LABELS its) + LABELS its COMPILE_ONLY) #o2_add_test_root_macro(CheckLUtime.C # PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction @@ -41,7 +41,7 @@ o2_add_test_root_macro(CreateDictionaries.C O2::ITSMFTSimulation O2::DataFormatsITSMFT O2::SimulationDataFormat - LABELS its) + LABELS its COMPILE_ONLY) #o2_add_test_root_macro(CheckCOG.C # PUBLIC_LINK_LIBRARIES O2::MathUtils @@ -64,7 +64,7 @@ o2_add_test_root_macro(CheckTracksCA.C O2::ITSBase O2::DataFormatsITS O2::DataFormatsITSMFT - LABELS its) + LABELS its COMPILE_ONLY) o2_add_test_root_macro(CheckSquasher.C PUBLIC_LINK_LIBRARIES O2::SimulationDataFormat @@ -108,3 +108,13 @@ o2_add_test_root_macro(ITSMisaligner.C o2_add_test_root_macro(CompareArtefacts.C LABELS its) + +o2_add_test_root_macro(CheckDuplicates.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + LABELS its) + +o2_add_test_root_macro(CheckDROF.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + LABELS its) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C index 3f17e42b3a712..99c2b091002af 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckClusters.C @@ -1,6 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckClusters.C /// \brief Simple macro to check ITSU clusters +#include +#include +#include +#include +#include +#include + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITSMFTSimulation/Hit.h" #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include @@ -191,7 +214,7 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile auto z0 = locHsta.Z(), dltz = locH.Z() - z0; auto r = (0.5 * (Segmentation::SensorLayerThickness - Segmentation::SensorLayerThicknessEff) - y0) / dlty; locH.SetXYZ(x0 + r * dltx, y0 + r * dlty, z0 + r * dltz); - //locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); + // locH.SetXYZ(0.5 * (locH.X() + locHsta.X()), 0.5 * (locH.Y() + locHsta.Y()), 0.5 * (locH.Z() + locHsta.Z())); std::array data = {(float)lab.getEventID(), (float)trID, locH.X(), locH.Z(), dltx / dlty, dltz / dlty, gloC.X(), gloC.Y(), gloC.Z(), @@ -209,6 +232,18 @@ void CheckClusters(std::string clusfile = "o2clus_its.root", std::string hitfile new TCanvas; nt.Draw("dz:tz", "abs(dz)<0.005 && abs(tz)<2"); + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); + canvdXdZ->Divide(2, 2); + canvdXdZ->cd(1)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_IB(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id < 3120", "colz"); + canvdXdZ->cd(2)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_OB(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3120", "colz"); + canvdXdZ->cd(3)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_IB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id < 3120 && abs(cgz) < 2", "colz"); + canvdXdZ->cd(4)->SetLogz(); + nt.Draw("dx:dz>>h_dx_vs_dz_OB_z(1000, -0.01, 0.01, 1000, -0.01, 0.01)", "id >= 3120 && abs(cgz) < 2", "colz"); + canvdXdZ->SaveAs("itsclusters_dx_vs_dz.pdf"); + auto c1 = new TCanvas("p1", "pullX"); c1->cd(); c1->SetLogy(); diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C new file mode 100644 index 0000000000000..21428ea4fcbc2 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C @@ -0,0 +1,1426 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include "TH1F.h" +#include +#include "TH2D.h" +#include "TH3D.h" +#include +#include +#include +#include +#include +#include + +#include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "DetectorsBase/Propagator.h" +#include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/DigitizationContext.h" + +#endif + +using namespace std; +using Vertex = o2::dataformats::Vertex>; + +void plotHistos(TFile* fWO, TFile* f, const char* append = ""); + +struct ParticleInfo { // particle level information for tracks + int event; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float pvx{}; + float pvy{}; + float pvz{}; + float dcaxy; + float dcaz; + unsigned short clusters = 0u; + unsigned char isReco = 0u; + unsigned char isFake = 0u; + bool isPrimary = false; + int bcInROF{-1}; + int rofId{-1}; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + o2::its::TrackITS track; + + void print() const + { + LOGP(info, "event={} pdg={} pt={} eta={} phi={} mother={} clusters={:7b} isReco={} isFake={} isPrimary={} bcInROF={} rofId={} | {}", event, pdg, pt, eta, phi, mother, clusters, isReco, isFake, isPrimary, bcInROF, rofId, track.asString()); + } + + int getNClusters() const noexcept + { + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(ParticleInfo::clusters) * 8; ++bit) { + nCl += bool(clusters & (1 << bit)); + } + return nCl; + } + + bool isReconstructable() const noexcept + { + return isPrimary && (7 == getNClusters()) && bcInROF >= 0; + } +}; +#pragma link C++ class ParticleInfo + ; +#pragma link C++ class std::vector < ParticleInfo> + ; + +struct VertexInfo { // Vertex level info + float purity; // fraction of main cont. labels to all + Vertex vertex; // reconstructed vertex + int bcInROF{-1}; + int rofId{-1}; + int event{-1}; // corresponding MC event + std::vector labels; // contributor labels + o2::MCCompLabel mainLabel; // main label + + void computeMain() + { + std::unordered_map freq; + size_t totalSet = 0; + + // Count frequencies of set labels + for (auto const& lab : labels) { + if (lab.isSet()) { + ++freq[lab]; + ++totalSet; + } + } + if (totalSet == 0) { + return; + } + // Find the label with maximum count + auto best = std::max_element(freq.begin(), freq.end(), [](auto const& a, auto const& b) { return a.second < b.second; }); + size_t maxCount = best->second; + + // If there's no majority (all counts == 1), fall back to first set label + o2::MCCompLabel mainLab; + if (maxCount == 1) { + for (auto const& lab : labels) { + if (lab.isSet()) { + mainLab = lab; + break; + } + } + } else { + mainLab = best->first; + } + purity = (float)maxCount / (float)labels.size(); + } +}; +#pragma link C++ class VertexInfo + ; + +using namespace o2::itsmft; +using namespace o2::its; + +void CheckDROF(bool plot = false, bool write = false, const std::string& tracfile = "o2trac_its.root", + const std::string& magfile = "o2sim_grp.root", + const std::string& clusfile = "o2clus_its.root", + const std::string& kinefile = "o2sim_Kine.root") +{ + constexpr int64_t roFrameLengthInBC = 198; // for pp=198 + constexpr int64_t roFrameBiasInBC = 64; // ITS delay accounted for in digitization + constexpr float roFbins{roFrameLengthInBC + 2.f}; + constexpr int bcValStart{60}, bcValEnd{140}; // adjustable region of validation train + + if (!plot) { + int trackID, evID, srcID; + bool fake; + + // Magnetic field and Propagator + o2::base::Propagator::initFieldFromGRP(magfile); + float bz = o2::base::Propagator::Instance()->getNominalBz(); + + // Geometry + o2::base::GeometryManager::loadGeometry(); + auto gman = o2::its::GeometryTGeo::Instance(); + + // MC tracks + TFile* file0 = TFile::Open(kinefile.data()); + TTree* mcTree = (TTree*)gFile->Get("o2sim"); + mcTree->SetBranchStatus("*", 0); // disable all branches + mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); + std::vector* mcArr = nullptr; + mcTree->SetBranchAddress("MCTrack", &mcArr); + o2::dataformats::MCEventHeader* mcEvent = nullptr; + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); + + auto* dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); + const auto& irs = dc->getEventRecords(); + dc->printCollisionSummary(false, 20); + + // Clusters + TFile::Open(clusfile.data()); + TTree* clusTree = (TTree*)gFile->Get("o2sim"); + std::vector* clusArr = nullptr; + clusTree->SetBranchAddress("ITSClusterComp", &clusArr); + + // Cluster MC labels + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + clusTree->SetBranchAddress("ITSClusterMCTruth", &clusLabArr); + + // Reconstructed tracks + TFile* file1 = TFile::Open(tracfile.data()); + TTree* recTree = (TTree*)gFile->Get("o2sim"); + std::vector* recArr = nullptr; + recTree->SetBranchAddress("ITSTrack", &recArr); + // Track MC labels + std::vector* trkLabArr = nullptr; + recTree->SetBranchAddress("ITSTrackMCTruth", &trkLabArr); + std::vector rofRecVec, *rofRecVecP = &rofRecVec; + recTree->SetBranchAddress("ITSTracksROF", &rofRecVecP); + // Vertices + std::vector* recVerArr = nullptr; + recTree->SetBranchAddress("Vertices", &recVerArr); + std::vector* recVerROFArr = nullptr; + recTree->SetBranchAddress("VerticesROF", &recVerROFArr); + std::vector* recVerLabelsArr = nullptr; + recTree->SetBranchAddress("ITSVertexMCTruth", &recVerLabelsArr); + std::vector* recVerPurityArr = nullptr; + recTree->SetBranchAddress("ITSVertexMCPurity", &recVerPurityArr); + + std::cout << "** Filling particle table ... " << std::flush; + int lastEventIDcl = -1, cf = 0; + const int nev = mcTree->GetEntriesFast(); + std::vector> info; + info.resize(nev); + TH1D* hZvertex = new TH1D("hZvertex", "Z vertex", 100, -20, 20); + for (int n = 0; n < nev; n++) { // loop over MC events + mcTree->GetEvent(n); + info[n].resize(mcArr->size()); + hZvertex->Fill(mcEvent->GetZ()); + const auto& ir = irs[mcEvent->GetEventID() - 1]; // event id start from 1 + for (unsigned int mcI{0}; mcI < mcArr->size(); ++mcI) { + auto part = mcArr->at(mcI); + info[n][mcI].event = n; + info[n][mcI].pdg = part.GetPdgCode(); + info[n][mcI].pvx = mcEvent->GetX(); + info[n][mcI].pvy = mcEvent->GetY(); + info[n][mcI].pvz = mcEvent->GetZ(); + info[n][mcI].pt = part.GetPt(); + info[n][mcI].phi = part.GetPhi(); + info[n][mcI].eta = part.GetEta(); + info[n][mcI].isPrimary = part.isPrimary(); + if (!ir.isDummy()) { + info[n][mcI].bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + info[n][mcI].rofId = (ir.toLong() - roFrameBiasInBC) / roFrameLengthInBC; + } + } + } + std::cout << "done." << std::endl; + + std::cout << "** Creating particle/clusters correspondance ... " << std::flush; + for (int frame = 0; frame < clusTree->GetEntriesFast(); frame++) { // Cluster frames + if (!clusTree->GetEvent(frame)) { + continue; + } + + for (unsigned int iClus{0}; iClus < clusArr->size(); ++iClus) { + auto lab = (clusLabArr->getLabels(iClus))[0]; + if (!lab.isValid() || lab.getSourceID() != 0 || !lab.isCorrect()) { + continue; + } + + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || evID >= (int)info.size()) { + std::cout << "Cluster MC label eventID out of range" << std::endl; + continue; + } + if (trackID < 0 || trackID >= (int)info[evID].size()) { + std::cout << "Cluster MC label trackID out of range" << std::endl; + continue; + } + + const CompClusterExt& c = (*clusArr)[iClus]; + auto layer = gman->getLayer(c.getSensorID()); + info[evID][trackID].clusters |= 1 << layer; + } + } + std::cout << "done." << std::endl; + + std::cout << "** Analysing tracks... " << std::flush; + int unaccounted{0}, good{0}, fakes{0}, total{0}, length{0}; + for (int frame = 0; frame < recTree->GetEntriesFast(); frame++) { // Cluster frames + if (!recTree->GetEvent(frame)) { + continue; + } + total += trkLabArr->size(); + for (unsigned int iTrack{0}; iTrack < trkLabArr->size(); ++iTrack) { + auto lab = trkLabArr->at(iTrack); + if (!lab.isSet()) { + unaccounted++; + continue; + } + lab.get(trackID, evID, srcID, fake); + if (evID < 0 || evID >= (int)info.size()) { + unaccounted++; + continue; + } + if (trackID < 0 || trackID >= (int)info[evID].size()) { + unaccounted++; + continue; + } + info[evID][trackID].isReco += !fake; + info[evID][trackID].isFake += fake; + /// We keep the best track we would keep in the data + if (recArr->at(iTrack).isBetter(info[evID][trackID].track, 1.e9)) { + info[evID][trackID].track = recArr->at(iTrack); + info[evID][trackID].storedStatus = fake; + static float ip[2]{0., 0.}; + info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, info[evID][trackID].pvy, info[evID][trackID].pvz, bz, ip); + info[evID][trackID].dcaxy = ip[0]; + info[evID][trackID].dcaz = ip[1]; + } + + fakes += fake; + good += !fake; + if (!fake) { + for (unsigned int bit{0}; bit < 7; ++bit) { + length += bool(info[evID][trackID].clusters & (1 << bit)); + } + } + } + } + std::cout << "done." << std::endl; + std::cout << "** Some statistics:" << std::endl; + std::cout << "\t- Total number of tracks: " << total << std::endl; + std::cout << "\t- Total number of tracks not corresponding to particles: " << unaccounted << " (" << unaccounted * 100. / total << "%)" << std::endl; + std::cout << "\t- Total number of fakes: " << fakes << " (" << fakes * 100. / total << "%)" << std::endl; + std::cout << "\t- Total number of good: " << good << " (" << good * 100. / total << "%)" << std::endl; + std::cout << "\t- Average length of good tracks: " << (double)length / (double)good << std::endl; + + TFile* fOut{nullptr}; + if (write) { + fOut = TFile::Open("checkDROF.root", "RECREATE"); + } + + const int nb = 100; + double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + ////////////////////// + // Eff Tracks + { + auto num = new TH2D("num", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + num->Sumw2(); + auto fak = new TH2D("fak", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fak->Sumw2(); + auto multiFak = new TH2D("multiFak", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFak->Sumw2(); + auto clone = new TH2D("clone", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + clone->Sumw2(); + auto den = new TH2D("den", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + den->Sumw2(); + auto numMC = new TH2D("numMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMC->Sumw2(); + auto fakMC = new TH2D("fakMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMC->Sumw2(); + auto multiFakMC = new TH2D("multiFakMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMC->Sumw2(); + auto cloneMC = new TH2D("cloneMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMC->Sumw2(); + auto denMC = new TH2D("denMC", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMC->Sumw2(); + + auto numVal = new TH2D("numVal", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numVal->Sumw2(); + auto fakVal = new TH2D("fakVal", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakVal->Sumw2(); + auto multiFakVal = new TH2D("multiFakVal", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakVal->Sumw2(); + auto cloneVal = new TH2D("cloneVal", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneVal->Sumw2(); + auto denVal = new TH2D("denVal", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denVal->Sumw2(); + auto numMCVal = new TH2D("numMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMCVal->Sumw2(); + auto fakMCVal = new TH2D("fakMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMCVal->Sumw2(); + auto multiFakMCVal = new TH2D("multiFakMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMCVal->Sumw2(); + auto cloneMCVal = new TH2D("cloneMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMCVal->Sumw2(); + auto denMCVal = new TH2D("denMCVal", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMCVal->Sumw2(); + + auto numMig = new TH2D("numMig", ";#it{p}_{T} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMig->Sumw2(); + auto fakMig = new TH2D("fakMig", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMig->Sumw2(); + auto multiFakMig = new TH2D("multiFakMig", ";#it{p}_{T} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMig->Sumw2(); + auto cloneMig = new TH2D("cloneMig", ";#it{p}_{T} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMig->Sumw2(); + auto denMig = new TH2D("denMig", ";#it{p}_{T} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMig->Sumw2(); + auto numMCMig = new TH2D("numMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Efficiency (fake-track rate)", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + numMCMig->Sumw2(); + auto fakMCMig = new TH2D("fakMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + fakMCMig->Sumw2(); + auto multiFakMCMig = new TH2D("multiFakMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Fak", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + multiFakMCMig->Sumw2(); + auto cloneMCMig = new TH2D("cloneMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Clone", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + cloneMCMig->Sumw2(); + auto denMCMig = new TH2D("denMCMig", ";#it{p}_{T,MC} (GeV/#it{c});NCls;Den", nb, xbins, 4, 4 - 0.5, 8 - 0.5); + denMCMig->Sumw2(); + + TProfile* avgClsZ = new TProfile("avgClsZ", "good attachment;z_{MC};", 25, -20, 20); + avgClsZ->SetLineColor(kBlack); + TProfile* avgClsZGood = new TProfile("avgClsZGood", "good attachment;z_{MC};", 25, -20, 20); + avgClsZGood->SetLineColor(kBlue); + TProfile* avgClsZFake = new TProfile("avgClsZFake", "fake attachment;z_{MC};", 25, -20, 20); + avgClsZFake->SetLineColor(kRed); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if (!part.isReconstructable()) { + continue; + } + den->Fill(part.track.getPt(), part.track.getNClusters()); + denMC->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + num->Fill(part.track.getPt(), part.track.getNClusters()); + numMC->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + clone->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMC->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fak->Fill(part.track.getPt(), part.track.getNClusters()); + fakMC->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFak->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMC->Fill(part.pt, part.track.getNClusters()); + } + } + } + + // sep into validation and migration region + if (bcValStart < part.bcInROF && part.bcInROF < bcValEnd) { + denVal->Fill(part.track.getPt(), part.track.getNClusters()); + denMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + numVal->Fill(part.track.getPt(), part.track.getNClusters()); + numMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + cloneVal->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMCVal->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fakVal->Fill(part.track.getPt(), part.track.getNClusters()); + fakMCVal->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFakVal->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMCVal->Fill(part.pt, part.track.getNClusters()); + } + } + } + } else { + denMig->Fill(part.track.getPt(), part.track.getNClusters()); + denMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isReco) { + numMig->Fill(part.track.getPt(), part.track.getNClusters()); + numMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + cloneMig->Fill(part.track.getPt(), part.track.getNClusters()); + cloneMCMig->Fill(part.pt, part.track.getNClusters()); + } + } + } + if (part.isFake) { + fakMig->Fill(part.track.getPt(), part.track.getNClusters()); + fakMCMig->Fill(part.pt, part.track.getNClusters()); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFakMig->Fill(part.track.getPt(), part.track.getNClusters()); + multiFakMCMig->Fill(part.pt, part.track.getNClusters()); + } + } + } + } + + int nCl = part.getNClusters(); + avgClsZ->Fill(part.pvz, nCl); + if (part.isReco) { + avgClsZGood->Fill(part.pvz, nCl); + } + if (part.isFake) { + avgClsZFake->Fill(part.pvz, nCl); + } + } + } + + auto sum = (TH2D*)num->Clone("sum"); + auto sumMC = (TH2D*)numMC->Clone("sumMC"); + sum->Add(fak); + sumMC->Add(fakMC); + sum->SetLineColor(kBlack); + sumMC->SetLineColor(kBlack); + fak->SetLineColor(2); + fakMC->SetLineColor(2); + multiFak->SetLineColor(kRed + 1); + multiFakMC->SetLineColor(kRed + 1); + + auto sumVal = (TH2D*)numVal->Clone("sumVal"); + auto sumMCVal = (TH2D*)numMCVal->Clone("sumMCVal"); + sumVal->Add(fakVal); + sumMCVal->Add(fakMCVal); + sumVal->SetLineColor(kBlack); + sumMCVal->SetLineColor(kBlack); + fakVal->SetLineColor(2); + fakMCVal->SetLineColor(2); + multiFakVal->SetLineColor(kRed + 1); + multiFakMCVal->SetLineColor(kRed + 1); + + auto sumMig = (TH2D*)numMig->Clone("sumMig"); + auto sumMCMig = (TH2D*)numMCMig->Clone("sumMCMig"); + sumMig->Add(fakMig); + sumMCMig->Add(fakMCMig); + sumMig->SetLineColor(kBlack); + sumMCMig->SetLineColor(kBlack); + fakMig->SetLineColor(2); + fakMCMig->SetLineColor(2); + multiFakMig->SetLineColor(kRed + 1); + multiFakMCMig->SetLineColor(kRed + 1); + + if (write) { + num->Write(); + den->Write(); + sum->Write(); + fak->Write(); + multiFak->Write(); + numMC->Write(); + denMC->Write(); + sumMC->Write(); + fakMC->Write(); + multiFakMC->Write(); + + numVal->Write(); + denVal->Write(); + sumVal->Write(); + fakVal->Write(); + multiFakVal->Write(); + numMCVal->Write(); + denMCVal->Write(); + sumMCVal->Write(); + fakMCVal->Write(); + multiFakMCVal->Write(); + + numMig->Write(); + denMig->Write(); + sumMig->Write(); + fakMig->Write(); + multiFakMig->Write(); + numMCMig->Write(); + denMCMig->Write(); + sumMCMig->Write(); + fakMCMig->Write(); + multiFakMCMig->Write(); + } else { + TCanvas* c1 = new TCanvas; + c1->SetLogx(); + c1->SetGrid(); + gPad->DrawFrame(ptcutl, 0.05, ptcuth, 1.03, ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)"); + + auto denp = den->ProjectionX(); + auto nump = num->ProjectionX(); + auto fakp = fak->ProjectionX(); + auto multiFakp = multiFak->ProjectionX(); + auto sump = sum->ProjectionX(); + auto clonep = clone->ProjectionX(); + + sump->Divide(sump, denp, 1, 1, "B"); + sump->Draw("hist;same"); + nump->Divide(nump, denp, 1, 1, "B"); + nump->Draw("hist;same"); + fakp->Divide(fakp, denp, 1, 1, "B"); + fakp->Draw("hist;same"); + multiFakp->Divide(multiFakp, denp, 1, 1, "B"); + multiFakp->Draw("hist;same"); + clonep->Divide(clonep, denp, 1, 1, "B"); + clonep->SetLineColor(3); + clonep->Draw("hist;same"); + + TCanvas* c2 = new TCanvas; + c2->Divide(2, 1); + c2->cd(1); + hZvertex->Draw(); + c2->cd(2); + avgClsZ->Draw(); + avgClsZGood->Draw("same"); + avgClsZFake->Draw("same"); + } + } + + ////////////////////// + // DROF Tracks + { + auto hBC = new TH1F("hBC", "Distance in BC;bcInROF;counts.", roFbins, -0.5, roFbins - 0.5); + auto hBCTracksDen = new TH2F("hBCTracksDen", "BC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNum = new TH2F("hBCTracksNum", "BC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFake = new TH2F("hBCTracksFake", "BC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSum = new TH2F("hBCTracksSum", "BC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + // control region + auto hBCTracksDenVal = new TH2F("hBCTracksDenVal", "Val BC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNumVal = new TH2F("hBCTracksNumVal", "Val BC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFakeVal = new TH2F("hBCTracksFakeVal", "Val BC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSumVal = new TH2F("hBCTracksSumVal", "Val BC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + // migration region + auto hBCTracksDenMig = new TH2F("hBCTracksDenMig", "MigBC Den Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksNumMig = new TH2F("hBCTracksNumMig", "MigBC Num Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksFakeMig = new TH2F("hBCTracksFakeMig", "MigBC Fake Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + auto hBCTracksSumMig = new TH2F("hBCTracksSumMig", "MigBC Sum Tracks;bcInROF;NCls;eff.", roFbins, -0.5, roFbins - 0.5, 4, 4 - 0.5, 8 - 0.5); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if (!part.isReconstructable()) { + continue; + } + hBC->Fill(part.bcInROF); + hBCTracksDen->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNum->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFake->Fill(part.bcInROF, part.track.getNClusters()); + } + + if (bcValStart < part.bcInROF && part.bcInROF < bcValEnd) { + hBCTracksDenVal->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNumVal->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFakeVal->Fill(part.bcInROF, part.track.getNClusters()); + } + } else { + hBCTracksDenMig->Fill(part.bcInROF, part.track.getNClusters()); + if (part.isReco) { + hBCTracksNumMig->Fill(part.bcInROF, part.track.getNClusters()); + } + if (part.isFake) { + hBCTracksFakeMig->Fill(part.bcInROF, part.track.getNClusters()); + } + } + } + } + + hBCTracksSum->Add(hBCTracksNum); + hBCTracksSum->Add(hBCTracksFake); + hBCTracksSum->SetLineColor(kBlack); + hBCTracksFake->SetLineColor(2); + + hBCTracksSumVal->Add(hBCTracksNum); + hBCTracksSumVal->Add(hBCTracksFake); + hBCTracksSumVal->SetLineColor(kBlack); + hBCTracksFakeVal->SetLineColor(2); + + hBCTracksSumMig->Add(hBCTracksNum); + hBCTracksSumMig->Add(hBCTracksFake); + hBCTracksSumMig->SetLineColor(kBlack); + hBCTracksFakeMig->SetLineColor(2); + + if (write) { + hBCTracksDen->Write(); + hBCTracksNum->Write(); + hBCTracksFake->Write(); + hBCTracksSum->Write(); + + hBCTracksDenVal->Write(); + hBCTracksNumVal->Write(); + hBCTracksFakeVal->Write(); + hBCTracksSumVal->Write(); + + hBCTracksDenMig->Write(); + hBCTracksNumMig->Write(); + hBCTracksFakeMig->Write(); + hBCTracksSumMig->Write(); + } else { + auto hBCTracksDenp = hBCTracksDen->ProjectionX(); + auto hBCTracksSump = hBCTracksSum->ProjectionX(); + auto hBCTracksNump = hBCTracksNum->ProjectionX(); + auto hBCTracksFakep = hBCTracksFake->ProjectionX(); + + hBCTracksSump->Divide(hBCTracksSump, hBCTracksDenp, 1., 1., "B"); + hBCTracksNump->Divide(hBCTracksNump, hBCTracksDenp, 1., 1., "B"); + hBCTracksFakep->Divide(hBCTracksFakep, hBCTracksDenp, 1., 1., "B"); + + auto c = new TCanvas; + c->Divide(2, 1); + c->cd(1); + hBC->Draw(); + c->cd(2); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Tracking >4 ITS cls;bcInROF;eff."); + gPad->SetGrid(); + hBCTracksSump->Draw("histe;same"); + hBCTracksNump->Draw("histe;same"); + hBCTracksFakep->Draw("histe;same"); + auto leg = new TLegend; + leg->AddEntry(hBCTracksSump, "Sum"); + leg->AddEntry(hBCTracksNump, "Good"); + leg->AddEntry(hBCTracksFakep, "Fake"); + leg->Draw(); + } + } + + ////////////////////// + // DROF Vertices + if constexpr (false) { + std::vector vertexInfo; + std::cout << "** Creating vertices/particles correspondance ... " << std::flush; + for (int frame = 0; frame < recTree->GetEntriesFast(); frame++) { // Vertices frames + if (!recTree->GetEvent(frame)) { + continue; + } + int contLabIdx{0}; // contributor labels are stored as flat vector + for (size_t iRecord{0}; iRecord < recVerROFArr->size(); ++iRecord) { + auto& rec = recVerROFArr->at(iRecord); + auto verStartIdx = rec.getFirstEntry(), verSize = rec.getNEntries(); + for (int iVertex{rec.getFirstEntry()}; iVertex < verStartIdx + verSize; ++iVertex) { + auto& info = vertexInfo.emplace_back(); + info.vertex = recVerArr->at(iVertex); + info.mainLabel = recVerLabelsArr->at(contLabIdx); + info.purity = recVerPurityArr->at(contLabIdx); + info.event = info.mainLabel.getEventID(); + ++contLabIdx; + if (info.mainLabel.isSet()) { + const auto& ir = irs[info.event]; + // LOGP(info, "iROF={} {} to {}", iRecord, info.mainLabel.asString(), ir.asString()); + if (!ir.isDummy()) { + info.bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + info.rofId = (ir.toLong() - roFrameBiasInBC) / roFrameLengthInBC; + } + } + } + } + } + std::cout << "done." << std::endl; + + auto hMCVtxZ = new TH1F("hMCVtxZ", "MC Vertex;Z", 50, -16, 16); + auto hReVtxZ = new TH1F("hRecoVtxZ", "Reco Vertex;Z", 50, -16, 16); + + auto hBCVtxDen = new TH1F("hBCVtxDen;bcInROF;eff.", "BC Den Vertices", roFbins, -0.5, roFbins - 0.5); + auto hBCVtxNum = new TH1F("hBCVtxNum;bcInROF;eff.", "BC Num Vertices", roFbins, -0.5, roFbins - 0.5); + + auto hBCVtxZDen = new TH2F("hBCVtxDen;bcInROF;z;eff.", "BC Den Vertices vs. z position", roFbins, -0.5, roFbins - 0.5, 40, -20, 20); + auto hBCVtxZNum = new TH2F("hBCVtxNum;bcInROF;z;eff.", "BC Num Vertices vs. z position", roFbins, -0.5, roFbins - 0.5, 40, -20, 20); + + auto pBCPurity = new TProfile("pBCProfile", ";bcInROF;", roFbins, -0.5, roFbins - 0.5); + auto pBCPurityDup = new TProfile("pBCProfileDup", ";bcInROF;", roFbins, -0.5, roFbins - 0.5); + pBCPurityDup->SetLineColor(kRed); + + auto hVtxMCx = new TH2F("hVtxMCx", ";MC_{x};Vtx_{x}", 100, -0.3, 0.3, 100, -0.3, 0.3); + auto hVtxMCy = new TH2F("hVtxMCy", ";MC_{y};Vtx_{y}", 100, -0.3, 0.3, 100, -0.3, 0.3); + auto hVtxMCz = new TH2F("hVtxMCz", ";MC_{z};Vtx_{z}", 100, -20, 20, 100, -20, 20); + + for (int n = 0; n < nev; n++) { // loop over MC events + mcTree->GetEvent(n); + hMCVtxZ->Fill(mcEvent->GetZ()); + const auto& ir = irs[mcEvent->GetEventID() - 1]; // event id start from 1 + if (!ir.isDummy()) { + int bcInROF = (ir.toLong() - roFrameBiasInBC) % roFrameLengthInBC; + hBCVtxDen->Fill(bcInROF); + hBCVtxZDen->Fill(bcInROF, mcEvent->GetZ()); + } + } + std::unordered_map seenMCEvent; + for (const auto& vtx : vertexInfo) { + ++seenMCEvent[vtx.mainLabel]; + } + // for (const auto& [k, f] : seenMCEvent) { + // LOGP(info, "{}:{} -> {} ({:.1f}%)", k.getSourceID(), k.getEventID(), f, 100.f * ((float)f / (float)vertexInfo.size())); + // } + LOGP(info, "received {} unique vertices", seenMCEvent.size()); + for (const auto& vtx : vertexInfo) { + if (!vtx.mainLabel.isValid() || vtx.bcInROF < 0 || vtx.event < 0) { + continue; + } + mcTree->GetEvent(vtx.event); + hVtxMCx->Fill(mcEvent->GetX(), vtx.vertex.getX()); + hVtxMCy->Fill(mcEvent->GetY(), vtx.vertex.getY()); + hVtxMCz->Fill(mcEvent->GetZ(), vtx.vertex.getZ()); + if (seenMCEvent[vtx.mainLabel] > 1) { + pBCPurityDup->Fill(vtx.bcInROF, vtx.purity); + } else { + hReVtxZ->Fill(vtx.vertex.getZ()); + hBCVtxNum->Fill(vtx.bcInROF); + hBCVtxZNum->Fill(vtx.bcInROF, vtx.vertex.getZ()); + pBCPurity->Fill(vtx.bcInROF, vtx.purity); + } + } + + auto hBCVtxNumClone = (TH1F*)hBCVtxNum->Clone(); + hBCVtxNumClone->SetTitle("unique Vertex;bcInROF;efficiency"); + hBCVtxNum->Divide(hBCVtxNum, hBCVtxDen, 1., 1., "b"); + + auto hBCVtxZNumClone = (TH2F*)hBCVtxZNum->Clone(); + hBCVtxZNumClone->SetTitle("unique Vertex;bcInROF;vtx.z;efficiency"); + hBCVtxZNumClone->Divide(hBCVtxZNum, hBCVtxZDen, 1., 1., "b"); + + hReVtxZ->Sumw2(); + hReVtxZ->SetLineColor(kRed); + + auto c = new TCanvas; + c->Divide(3, 2); + + c->cd(1); + auto hRatioVtxZ = new TRatioPlot(hReVtxZ, hMCVtxZ); + hRatioVtxZ->Draw(); + hRatioVtxZ->GetUpperPad()->cd(); + TLegend* legend = new TLegend(0.3, 0.7, 0.7, 0.85); + legend->SetHeader(Form("MC=%.0f Reco=%.0f", hMCVtxZ->GetEntries(), hReVtxZ->GetEntries())); + legend->AddEntry(hReVtxZ, "Reco", "l"); + legend->AddEntry(hMCVtxZ, "MC", "le"); + legend->Draw(); + gPad->Update(); + double max1 = hReVtxZ->GetMaximum(); + double max2 = hMCVtxZ->GetMaximum(); + double maxY = std::max(max1, max2); + hReVtxZ->GetYaxis()->SetRangeUser(0, maxY * 1.1); + + c->cd(2); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Vertex ;bcInROF;eff."); + hBCVtxNum->Draw("histe;same"); + + c->cd(3); + gPad->DrawFrame(-0.5, 1e-3, roFbins - 0.5, 1.1, "Purity;bcInROF;"); + pBCPurity->Draw(); + pBCPurityDup->Draw("same"); + c->Draw(); + + c->cd(4); + hBCVtxDen->Draw(); + c->cd(5); + hBCVtxNumClone->Draw(); + c->cd(6); + hBCVtxZNumClone->Draw(); + c->Draw(); + + c = new TCanvas; + c->Divide(3, 1); + c->cd(1); + hVtxMCx->Draw("colz"); + c->cd(2); + hVtxMCy->Draw("colz"); + c->cd(3); + hVtxMCz->Draw("colz"); + c->Draw(); + } + ////////////////////// + // Fake clusters + if (write) { + const int nby{4}, nbz{7}; + double ybins[nby + 1], zbins[nbz + 1]; + for (int i{0}; i < nby + 1; ++i) { + ybins[i] = (4 + i) - 0.5; + } + for (int i{0}; i < nbz + 1; ++i) { + zbins[i] = (0 + i) - 0.5; + } + auto hFakVal = new TH3D("fakClsVal", "Fake cluster attachment;#it{p}_{T} (GeV/#it{c});NCls;Fake;(fake-cluster rate)", nb, xbins, nby, ybins, nbz, zbins); + auto hFakMig = new TH3D("fakClsMig", "Fake cluster attachment;#it{p}_{T} (GeV/#it{c});NCls;Fake;(fake-cluster rate)", nb, xbins, nby, ybins, nbz, zbins); + + for (auto& event : info) { + for (auto& part : event) { + if (!part.isReconstructable()) { + continue; + } + + const auto& trk = part.track; + for (int iL{0}; iL < 7; ++iL) { + if (!trk.hasHitOnLayer(iL) || !trk.isFakeOnLayer(iL) || (part.clusters & (0x1 << iL)) == 0) { + continue; + } + if (trk.hasHitInNextROF()) { + hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); + } else { + hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); + } + } + } + } + + hFakMig->Write(); + hFakVal->Write(); + } + if (fOut) { + fOut->Close(); + } + } else { + auto fWO = TFile::Open("checkDROF_wo.root"); + auto f = TFile::Open("checkDROF_w.root"); + plotHistos(fWO, f, ""); + plotHistos(fWO, f, "Val"); + plotHistos(fWO, f, "Mig"); + } +} + +void plotHistos(TFile* fWO, TFile* f, const char* append) +{ + TLegend* leg; + TH1* h; + const int woStyle = 1001; + const int wStyle = 3003; + const int ww{3840}, hh{2160}; + + const char* titlename = ""; + if (strcmp(append, "Val") == 0) { + titlename = ", Validation region"; + } else if (strcmp(append, "Mig") == 0) { + titlename = ", Migration region"; + } + + auto hWODen2 = fWO->Get(Form("den%s", append)); + hWODen2->SetName(Form("%s_wo", hWODen2->GetName())); + auto hWONum2 = fWO->Get(Form("num%s", append)); + hWONum2->SetName(Form("%s_wo", hWONum2->GetName())); + auto hWOFak2 = fWO->Get(Form("fak%s", append)); + hWOFak2->SetName(Form("%s_wo", hWOFak2->GetName())); + auto hWOSum2 = fWO->Get(Form("sum%s", append)); + hWOSum2->SetName(Form("%s_wo", hWOSum2->GetName())); + auto hWOMultiFak2 = fWO->Get(Form("multiFak%s", append)); + hWOMultiFak2->SetName(Form("%s_wo", hWOMultiFak2->GetName())); + + auto hWODenMC2 = fWO->Get(Form("denMC%s", append)); + hWODenMC2->SetName(Form("%s_wo", hWODenMC2->GetName())); + auto hWONumMC2 = fWO->Get(Form("numMC%s", append)); + hWONumMC2->SetName(Form("%s_wo", hWONumMC2->GetName())); + auto hWOFakMC2 = fWO->Get(Form("fakMC%s", append)); + hWOFakMC2->SetName(Form("%s_wo", hWOFakMC2->GetName())); + auto hWOSumMC2 = fWO->Get(Form("sumMC%s", append)); + hWOSumMC2->SetName(Form("%s_wo", hWOSumMC2->GetName())); + auto hWOMultiFakMC2 = fWO->Get(Form("multiFakMC%s", append)); + hWOMultiFakMC2->SetName(Form("%s_wo", hWOMultiFakMC2->GetName())); + + auto hWOBCTracksDen2 = fWO->Get(Form("hBCTracksDen%s", append)); + hWOBCTracksDen2->SetName(Form("%s_wo", hWOBCTracksDen2->GetName())); + auto hWOBCTracksNum2 = fWO->Get(Form("hBCTracksNum%s", append)); + hWOBCTracksNum2->SetName(Form("%s_wo", hWOBCTracksNum2->GetName())); + auto hWOBCTracksFake2 = fWO->Get(Form("hBCTracksFake%s", append)); + hWOBCTracksFake2->SetName(Form("%s_wo", hWOBCTracksFake2->GetName())); + auto hWOBCTracksSum2 = fWO->Get(Form("hBCTracksSum%s", append)); + hWOBCTracksSum2->SetName(Form("%s_wo", hWOBCTracksSum2->GetName())); + + auto setColor = [](TH1* h, EColor c) { + h->SetLineColor(c); + h->SetMarkerColor(c); + }; + auto hDen2 = f->Get(Form("den%s", append)); + setColor(hDen2, kBlack); + auto hNum2 = f->Get(Form("num%s", append)); + setColor(hNum2, kCyan); + auto hFak2 = f->Get(Form("fak%s", append)); + setColor(hFak2, kOrange); + auto hSum2 = f->Get(Form("sum%s", append)); + setColor(hSum2, kGray); + auto hMultiFak2 = f->Get(Form("multiFak%s", append)); + setColor(hMultiFak2, kMagenta); + + auto hDenMC2 = f->Get(Form("denMC%s", append)); + setColor(hDenMC2, kBlack); + auto hNumMC2 = f->Get(Form("numMC%s", append)); + setColor(hNumMC2, kCyan); + auto hFakMC2 = f->Get(Form("fakMC%s", append)); + setColor(hFakMC2, kOrange); + auto hSumMC2 = f->Get(Form("sumMC%s", append)); + setColor(hSumMC2, kGray); + auto hMultiFakMC2 = f->Get(Form("multiFakMC%s", append)); + setColor(hMultiFakMC2, kMagenta); + + auto hBCTracksDen2 = f->Get(Form("hBCTracksDen%s", append)); + setColor(hBCTracksDen2, kBlack); + auto hBCTracksNum2 = f->Get(Form("hBCTracksNum%s", append)); + setColor(hBCTracksNum2, kCyan); + auto hBCTracksFake2 = f->Get(Form("hBCTracksFake%s", append)); + setColor(hBCTracksFake2, kOrange); + auto hBCTracksSum2 = f->Get(Form("hBCTracksSum%s", append)); + setColor(hBCTracksSum2, kGray); + + int k = 0; + TCanvas *cEff = nullptr, *cBC = nullptr, *cCont = nullptr, *cRatio = nullptr; + { + auto plotTrkEff = [&](int i, int j) { + auto hWONum = hWONumMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWONumMC2->GetName(), i, j), i, j); + auto hWODen = hWODenMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWODenMC2->GetName(), i, j), 0, 5); + auto hWOFak = hWOFakMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWOFakMC2->GetName(), i, j), i, j); + auto hWOMultiFak = hWOMultiFakMC2->ProjectionX(Form("%s_%d_%d_eff_px", hWOMultiFakMC2->GetName(), i, j), i, j); + auto hWOSum = (TH1D*)hWONum->Clone(Form("%s_sum_eff__%d", hWONum->GetName(), j)); + hWOSum->Add(hWOFak); + + hWOSum->Divide(hWOSum, hWODen, 1., 1., "B"); + hWOSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOSum->SetFillStyle(woStyle); + hWOSum->Draw("histe;same"); + + hWONum->Divide(hWONum, hWODen, 1., 1., "B"); + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetFillStyle(woStyle); + hWONum->Draw("histe;same"); + + hWOFak->Divide(hWOFak, hWODen, 1., 1., "B"); + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetFillStyle(woStyle); + hWOFak->Draw("histe;same"); + + hWOMultiFak->Divide(hWOMultiFak, hWODen, 1., 1., "B"); + hWOMultiFak->SetLineColor(hWOMultiFak2->GetLineColor()); + hWOMultiFak->SetFillColorAlpha(hWOMultiFak2->GetLineColor(), 0.5); + hWOMultiFak->SetFillStyle(woStyle); + // hWOMultiFak->Draw("histe;same"); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_eff_px", hNumMC2->GetName(), i, j), i, j); + auto hDen = hDen2->ProjectionX(Form("%s_%d_%d_eff_px", hDenMC2->GetName(), i, j), 0, 5); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_eff_px", hFakMC2->GetName(), i, j), i, j); + auto hMultiFak = hMultiFak2->ProjectionX(Form("%s_%d_%d_px", hMultiFakMC2->GetName(), i, j), i, j); + auto hSum = (TH1D*)hNum->Clone(Form("%s_sum_eff_%d", hNum->GetName(), j)); + hSum->Add(hFak); + + hSum->Divide(hSum, hDen, 1., 1., "B"); + hSum->SetFillColor(hSum2->GetLineColor()); + hSum->SetLineColor(hSum2->GetLineColor()); + hSum->SetFillStyle(wStyle); + hSum->Draw("histe;same"); + + hNum->Divide(hNum, hDen, 1., 1., "B"); + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetLineColor(hNum2->GetLineColor()); + hNum->SetFillStyle(wStyle); + hNum->Draw("histe;same"); + + hFak->Divide(hFak, hDen, 1., 1., "B"); + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetLineColor(hFak2->GetLineColor()); + hFak->SetFillStyle(wStyle); + hFak->Draw("histe;same"); + + hMultiFak->Divide(hMultiFak, hDen, 1., 1., "B"); + hMultiFak->SetLineColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillStyle(wStyle); + // hMultiFak->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWOSum, "sum"); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + // leg->AddEntry(hWOMultiFak, "multifake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hSum, "sum"); + leg->AddEntry(hNum, "good"); + leg->AddEntry(hFak, "fake"); + // leg->AddEntry(hMultiFak, "multifake"); + } + }; + + cEff = new TCanvas(Form("pteff%s", append), "", ww, hh); + cEff->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cEff->cd(i + k); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Efficiency #times Fraction (7 MC hits, %d-point " + "tracks%s);#it{p}_{T,MC} GeV/#it{c};eff. (fake-rate)", + 3 + i, titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkEff(i, i); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cEff->cd(3); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Efficiency (7 MC hits, all point " + "tracks%s);#it{p}_{T,MC} GeV/#it{c};eff. (fake-rate)", + titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkEff(1, 4); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cEff->cd(6); + leg->Draw(); + } + + { + auto plotRatios = [&](int i, int j, TPad* upper, TPad* lower) { + auto hWONum = hWONumMC2->ProjectionX(Form("%s_%d_%d_ratio_px", hWONumMC2->GetName(), i, j), i, j); + auto hWOFak = hWOFakMC2->ProjectionX(Form("%s_%d_%d_ratio_px", hWOFakMC2->GetName(), i, j), i, j); + + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetLineColor(hWONum2->GetLineColor()); + // hWONum->SetFillStyle(woStyle); + + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetLineColor(hWOFak2->GetLineColor()); + // hWOFak->SetFillStyle(woStyle); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_ratio_px", hNumMC2->GetName(), i, j), i, j); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_ratio_px", hFakMC2->GetName(), i, j), i, j); + + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetLineColor(hNum2->GetLineColor()); + // hNum->SetFillStyle(wStyle); + + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetLineColor(hFak2->GetLineColor()); + // hFak->SetFillStyle(wStyle); + // + upper->cd(); + upper->SetLogx(); + upper->SetGrid(); + hWONum->Draw("hist"); + hWOFak->Draw("hist same"); + hNum->Draw("hist same"); + hFak->Draw("hist same"); + double ymax = 1.1 * std::max({hNum->GetMaximum(), hFak->GetMaximum(), hWONum->GetMaximum(), hWOFak->GetMaximum()}); + hWONum->GetYaxis()->SetRangeUser(0, ymax); + gPad->RedrawAxis("g"); + + auto rNum = (TH1*)hNum->Clone(Form("rNum_%s_%d_%d", hNum->GetName(), i, j)); + auto rFak = (TH1*)hFak->Clone(Form("rFak_%s_%d_%d", hFak->GetName(), i, j)); + rNum->GetYaxis()->SetTitle("(deltaRof=1) / (deltaRof=0)"); + rNum->Divide(hWONum); + rFak->Divide(hWOFak); + + // rNum->SetMarkerStyle(20); + // rFak->SetMarkerStyle(21); + rNum->SetLineWidth(2); + rFak->SetLineWidth(2); + rNum->SetFillStyle(0); + rFak->SetFillStyle(0); + setColor(rNum, kBlue); + setColor(rFak, kRed); + double ymin = std::min(rNum->GetMinimum(0.0), rFak->GetMinimum(0.0)); + ymax = std::max(rNum->GetMaximum(), rFak->GetMaximum()); + double ypad = 0.1 * (ymax - ymin); + ymin -= ypad; + ymax += ypad; + + lower->cd(); + lower->SetLogx(); + lower->SetGrid(); + rNum->GetYaxis()->SetRangeUser(ymin, ymax); + rNum->Draw("hist"); + rFak->Draw("hist;same"); + gPad->RedrawAxis("g"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hNum, "good"); + leg->AddEntry(hFak, "fake"); + leg->AddEntry((TObject*)0, "Ratios", ""); + leg->AddEntry(rNum, "good", "l"); + leg->AddEntry(rFak, "fake", "l"); + } + }; + + cRatio = new TCanvas(Form("ptratio%s", append), "", ww, hh); + cRatio->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cRatio->cd(i + k); + TPad* up = new TPad(Form("up%d", k), "", 0, 0.5, 1, 1); + TPad* dn = new TPad(Form("dn%d", k), "", 0, 0, 1, 0.5); + up->SetBottomMargin(0); + dn->SetTopMargin(0); + up->Draw(); + dn->Draw(); + + plotRatios(i, i, up, dn); + } + cRatio->cd(3); + TPad* up = new TPad(Form("up_e_%d", k), "", 0, 0.5, 1, 1); + TPad* dn = new TPad(Form("dn_e_%d", k), "", 0, 0, 1, 0.5); + up->SetBottomMargin(0); + dn->SetTopMargin(0); + up->Draw(); + dn->Draw(); + plotRatios(1, 4, up, dn); + + cRatio->cd(6); + leg->Draw(); + } + + { + auto plotTrkCont = [&](int i, int j) { + auto hWONum = hWONum2->ProjectionX(Form("%s_%d_%d_cont_px", hWONum2->GetName(), i, j), i, j); + auto hWODen = hWODen2->ProjectionX(Form("%s_%d_%d_cont_px", hWODen2->GetName(), i, j), 0, 5); + auto hWOFak = hWOFak2->ProjectionX(Form("%s_%d_%d_cont_px", hWOFak2->GetName(), i, j), i, j); + auto hWOMultiFak = hWOMultiFak2->ProjectionX(Form("%s_%d_%d_cont_px", hWOMultiFak2->GetName(), i, j), i, j); + auto hWOSum = (TH1D*)hWONum->Clone(Form("%s_sum_cont_%d", hWONum->GetName(), j)); + hWOSum->Add(hWOFak); + + hWOSum->Divide(hWOSum, hWODen, 1., 1., "B"); + hWOSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOSum->SetFillStyle(woStyle); + // hWOSum->Draw("histe;same"); + + hWONum->Divide(hWONum, hWODen, 1., 1., "B"); + hWONum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWONum->SetFillStyle(woStyle); + hWONum->Draw("histe;same"); + + hWOFak->Divide(hWOFak, hWODen, 1., 1., "B"); + hWOFak->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOFak->SetFillStyle(woStyle); + hWOFak->Draw("histe;same"); + + hWOMultiFak->Divide(hWOMultiFak, hWODen, 1., 1., "B"); + hWOMultiFak->SetLineColor(hWOMultiFak2->GetLineColor()); + hWOMultiFak->SetFillColorAlpha(hWOMultiFak2->GetLineColor(), 0.5); + hWOMultiFak->SetFillStyle(woStyle); + // hWOMultiFak->Draw("histe;same"); + + auto hNum = hNum2->ProjectionX(Form("%s_%d_%d_cont_px", hNum2->GetName(), i, j), i, j); + auto hDen = hDen2->ProjectionX(Form("%s_%d_%d_cont_px", hDen2->GetName(), i, j), 0, 5); + auto hFak = hFak2->ProjectionX(Form("%s_%d_%d_cont_px", hFak2->GetName(), i, j), i, j); + auto hMultiFak = hMultiFak2->ProjectionX(Form("%s_%d_%d_px", hMultiFak2->GetName(), i, j), i, j); + auto hSum = (TH1D*)hNum->Clone(Form("%s_sum_cont_%d", hNum->GetName(), j)); + hSum->Add(hFak); + + hSum->Divide(hSum, hDen, 1., 1., "B"); + hSum->SetFillColor(hSum2->GetLineColor()); + hSum->SetFillStyle(wStyle); + // hSum->Draw("histe;same"); + + hNum->Divide(hNum, hDen, 1., 1., "B"); + hNum->SetFillColor(hNum2->GetLineColor()); + hNum->SetFillStyle(wStyle); + hNum->Draw("histe;same"); + + hFak->Divide(hFak, hDen, 1., 1., "B"); + hFak->SetFillColor(hFak2->GetLineColor()); + hFak->SetFillStyle(wStyle); + hFak->Draw("histe;same"); + + hMultiFak->Divide(hMultiFak, hDen, 1., 1., "B"); + hMultiFak->SetLineColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillColor(hMultiFak2->GetLineColor()); + hMultiFak->SetFillStyle(wStyle); + // hMultiFak->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWONum, "good"); + leg->AddEntry(hWOFak, "fake"); + // leg->AddEntry(hWOMultiFak, "multifake"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hNum, "DROF:good"); + leg->AddEntry(hFak, "DROF:fake"); + // leg->AddEntry(hMultiFak, "DROF:multifake"); + } + }; + + cCont = new TCanvas(Form("ptcont%s", append), "", ww, hh); + cCont->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cCont->cd(i + k); + h = gPad->DrawFrame( + 0.02, 0, 10, 1.02, + Form("Tracking Contribution #times Fraction (7 MC hits, %d-point " + "tracks%s);#it{p}_{T,Reco} GeV/#it{c};contribtution", + 3 + i, titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkCont(i, i); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cCont->cd(3); + h = gPad->DrawFrame(0.02, 0, 10, 1.02, + Form("Tracking Contribution (7 MC hits, all point " + "tracks%s);#it{p}_{T,Reco} GeV/#it{c};contribution", + titlename)); + h->GetXaxis()->SetTitleOffset(1.4); + + plotTrkCont(1, 4); + + gPad->SetLogx(); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cCont->cd(6); + leg->Draw(); + } + + { + auto plotBCEff = [&](int i, int j) { + auto hWOBCTracksNum = hWOBCTracksNum2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksNum2->GetName(), i, j), i, j); + auto hWOBCTracksDen = hWOBCTracksDen2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksDen2->GetName(), i, j), 0, 5); + auto hWOBCTracksFake = hWOBCTracksFake2->ProjectionX(Form("%s_%d_%d_bc_px", hWOBCTracksFake2->GetName(), i, j), i, j); + auto hWOBCTracksSum = (TH1F*)hWOBCTracksNum->Clone(Form("%s_%d_sum", hWOBCTracksNum->GetName(), j)); + hWOBCTracksSum->Add(hWOBCTracksFake); + + hWOBCTracksSum->Divide(hWOBCTracksSum, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksSum->SetLineColor(hWOSum2->GetLineColor()); + hWOBCTracksSum->SetFillColorAlpha(hWOSum2->GetLineColor(), 0.5); + hWOBCTracksSum->SetFillStyle(woStyle); + hWOBCTracksSum->Draw("histe;same"); + + hWOBCTracksNum->Divide(hWOBCTracksNum, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksNum->SetLineColor(hWONum2->GetLineColor()); + hWOBCTracksNum->SetFillColorAlpha(hWONum2->GetLineColor(), 0.5); + hWOBCTracksNum->SetFillStyle(woStyle); + hWOBCTracksNum->Draw("histe;same"); + + hWOBCTracksFake->Divide(hWOBCTracksFake, hWOBCTracksDen, 1., 1., "B"); + hWOBCTracksFake->SetLineColor(hWOFak2->GetLineColor()); + hWOBCTracksFake->SetFillColorAlpha(hWOFak2->GetLineColor(), 0.5); + hWOBCTracksFake->SetFillStyle(woStyle); + hWOBCTracksFake->Draw("histe;same"); + + auto hBCTracksNum = hBCTracksNum2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksNum2->GetName(), i, j), i, j); + auto hBCTracksDen = hBCTracksDen2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksDen2->GetName(), i, j), 0, 5); + auto hBCTracksFake = hBCTracksFake2->ProjectionX(Form("%s_%d_%d_bc_px", hBCTracksFake2->GetName(), i, j), i, j); + auto hBCTracksSum = (TH1F*)hBCTracksNum->Clone(Form("%s_%d_sum", hBCTracksNum->GetName(), j)); + hBCTracksSum->Add(hBCTracksFake); + + hBCTracksSum->Divide(hBCTracksSum, hBCTracksDen, 1., 1., "B"); + hBCTracksSum->SetLineColor(hSum2->GetLineColor()); + hBCTracksSum->SetFillColor(hSum2->GetLineColor()); + hBCTracksSum->SetFillStyle(wStyle); + hBCTracksSum->Draw("histe;same"); + + hBCTracksNum->Divide(hBCTracksNum, hBCTracksDen, 1., 1., "B"); + hBCTracksNum->SetLineColor(hNum2->GetLineColor()); + hBCTracksNum->SetFillColor(hNum2->GetLineColor()); + hBCTracksNum->SetFillStyle(wStyle); + hBCTracksNum->Draw("histe;same"); + + hBCTracksFake->Divide(hBCTracksFake, hBCTracksDen, 1., 1., "B"); + hBCTracksFake->SetLineColor(hFak2->GetLineColor()); + hBCTracksFake->SetFillColor(hFak2->GetLineColor()); + hBCTracksFake->SetFillStyle(wStyle); + hBCTracksFake->Draw("histe;same"); + + if (i == 1 && i == j) { + leg = new TLegend(0.1, 0.1, 0.9, 0.9); + leg->AddEntry((TObject*)0, "deltaRof=0", ""); + leg->AddEntry(hWOBCTracksNum, "good"); + leg->AddEntry(hWOBCTracksFake, "fake"); + leg->AddEntry(hWOBCTracksSum, "sum"); + leg->AddEntry((TObject*)0, "deltaRof=1", ""); + leg->AddEntry(hBCTracksNum, "good"); + leg->AddEntry(hBCTracksFake, "fake"); + leg->AddEntry(hBCTracksSum, "sum"); + } + }; + + cBC = new TCanvas(Form("bceff%s", append), "", ww, hh); + cBC->Divide(3, 2); + k = 0; + for (int i{1}; i <= 4; ++i) { + if (i == 3) { + ++k; + } + cBC->cd(i + k); + gPad->DrawFrame(-0.5, 0, 200 - 0.5, 1.02, + Form("Tracking Efficiency #times Fraction (#it{p}_{T} " + "integrated, %d-point " + "tracks%s);BC in " + "ROF;eff. (fake-rate)", + 3 + i, titlename)); + plotBCEff(i, i); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + } + cBC->cd(3); + gPad->DrawFrame(-0.5, 0, 200 - 0.5, 1.02, + Form("Tracking Efficiency (#it{p}_{T} " + "integrated, all point " + "tracks%s);BC in " + "ROF;eff. (fake-rate)", + titlename)); + plotBCEff(1, 4); + gPad->SetGrid(); + gPad->RedrawAxis("g"); + + cBC->cd(6); + leg->Draw(); + } + + TString outname = TString::Format("trkeff%s.pdf", append); + cEff->cd(); + cEff->Update(); + cEff->Print(TString::Format("%s(", outname.Data()), "Title:Tracking Efficiency"); + cRatio->cd(); + cRatio->Update(); + cRatio->Print(outname.Data(), "Title:Ratios"); + cCont->cd(); + cCont->Update(); + cCont->Print(outname.Data(), "Title:Contribution"); + cBC->cd(); + cBC->Update(); + cBC->Print(TString::Format("%s)", outname.Data()), "Title:BC"); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C index 832839c5bed0d..78503dc79fd48 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDigits.C @@ -1,3 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckDigits.C /// \brief Simple macro to check ITSU digits @@ -91,8 +102,8 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = // >> build min and max MC events used by each ROF for (int imc = MC2ROFRecordArrrayRef.size(); imc--;) { const auto& mc2rof = MC2ROFRecordArrrayRef[imc]; - printf("MCRecord: "); - mc2rof.print(); + // printf("MCRecord: "); + // mc2rof.print(); if (mc2rof.rofRecordID < 0) { continue; // this MC event did not contribute to any ROF @@ -198,10 +209,44 @@ void CheckDigits(std::string digifile = "itsdigits.root", std::string hitfile = } // end loop on ROFRecords array - new TCanvas; - nt->Draw("y:x"); - new TCanvas; - nt->Draw("dx:dz", "abs(dx)<0.01 && abs(dz)<0.01"); + auto canvXY = new TCanvas("canvXY", "", 1600, 1600); + canvXY->Divide(2, 2); + canvXY->cd(1); + nt->Draw("y:x>>h_y_vs_x_IB(1000, -5, 5, 1000, -5, 5)", "id < 3120 ", "colz"); + canvXY->cd(2); + nt->Draw("y:z>>h_y_vs_z_IB(1000, -15, 15, 1000, -5, 5)", "id < 3120 ", "colz"); + canvXY->cd(3); + nt->Draw("y:x>>h_y_vs_x_OB(1000, -50, 50, 1000, -50, 50)", "id >= 3120 ", "colz"); + canvXY->cd(4); + nt->Draw("y:z>>h_y_vs_z_OB(1000, -100, 100, 1000, -50, 50)", "id >= 3120 ", "colz"); + canvXY->SaveAs("itsdigits_y_vs_x_vs_z.pdf"); + + auto canvdXdZ = new TCanvas("canvdXdZ", "", 1600, 800); + canvdXdZ->Divide(2, 2); + canvdXdZ->cd(1); + nt->Draw("dx:dz>>h_dx_vs_dz_IB(500, -0.02, 0.02, 500, -0.01, 0.01)", "id < 3120", "colz"); + auto h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_IB"); + Info("IB", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("IB", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(2); + nt->Draw("dx:dz>>h_dx_vs_dz_OB(500, -0.02, 0.02, 500, -0.02, 0.02)", "id >= 3120", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OB"); + Info("OB", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OB", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(3); + nt->Draw("dx:dz>>h_dx_vs_dz_IB_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 3120 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_IB_z"); + Info("IB |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("IB |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->cd(4); + nt->Draw("dx:dz>>h_dx_vs_dz_OB_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id >= 3120 && abs(z)<2", "colz"); + h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OB_z"); + Info("OB |z|<2", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("OB |z|<2", "RMS(dz)=%.1f mu", h->GetRMS(1) * 1e4); + canvdXdZ->SaveAs("itsdigits_dx_vs_dz.pdf"); + + f->Write(); + f->Close(); f->Write(); f->Close(); diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C b/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C new file mode 100644 index 0000000000000..abe707225ab30 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDuplicates.C @@ -0,0 +1,249 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckDuplicates.C +/// \brief Macro to look for duplicate tracks across a ROF windows + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" +#endif +#include "DataFormatsITSMFT/ROFRecord.h" + +void CheckDuplicates(TString tracfile = "./o2trac_its.root", TString output = ".", TString rootSave = "", int rofStart = 0, int rofEnd = -1, int windowSize = 2, bool includeSame = 1) +{ + gErrorIgnoreLevel = kWarning; + + // Obtain data from files: + TFile* file1 = TFile::Open(tracfile); + TTree* recTree = (TTree*)gFile->Get("o2sim"); + // Extract track information + std::vector* recArr = nullptr; + recTree->SetBranchAddress("ITSTrack", &recArr); + // Extract information on which ROFs contain which tracks + std::vector* rofArr = nullptr; + recTree->SetBranchAddress("ITSTracksROF", &rofArr); + + // Specify plot parameters: + int phiBins{101}, etaBins{101}; // number of bins for dPhi and dEta axes + double_t phiMin{-2 * TMath::Pi()}, phiMax{2 * TMath::Pi()}, etaMin{-4}, etaMax{4}; // dPhi and dEta axes limits + double_t phiLim{0.01}, etaLim{0.01}; // limits for determining duplicate status + auto h1 = new TH2D("h1", "", phiBins, phiMin, phiMax, etaBins, etaMin, etaMax); // Distribution of all comparisons over dPhi and dEta + auto h2 = new TH2D("h2", "All pairs; #||{#Delta#phi} [rad]; #||{#Delta#eta}", phiBins, 0, phiMax, etaBins, 0, etaMax); // Distribution of all comparisons over |dPhi| and |dEta| + auto h3 = new TH2D("dupl", "Duplicates; #Delta#phi; #Delta#eta", 100, -1 * phiLim, phiLim, 100, -1 * etaLim, etaLim); // Distribution of duplicates pairs over dPhi and dEta + auto h4 = new TH2D("h4", "Duplicates; avg. #phi; avg. #eta", 50, 0, phiMax, 50, etaMin, etaMax); // Distribution of duplicate pairs over avg. phi and avg. eta + + int comps{0}, dupl{0}, zeroes{0}, totalROFs{0}; // Collect summary information + double_t eta1, eta2, phi1, phi2, dPhi, dEta, avgPhi, avgEta; + int rofLow, rofHigh, rof1Low, rof1High, rof2Low, rof2High; + int rofFrameEnd = rofEnd; // In case frames have different numbers of ROFs, store the final ROF index separately for each frame inside rofFrameEnd + int totalFrames = recTree->GetEntriesFast(); + for (int frame = 0; frame < totalFrames; frame++) { // Loop over timeframes + LOGP(info, "Proceeding to frame {} of {}", frame + 1, totalFrames); + recTree->GetEvent(frame); + LOGP(info, "{} ROFs in frame {}/{}", rofArr->size(), frame + 1, totalFrames); + totalROFs += rofArr->size(); + if (rofEnd >= (int long)rofArr->size() || rofEnd < 0) { // Check whether rofEnd is greater than the total number of ROFs in this dataset, or negative; if so, set to maximum + LOGP(warn, "Frame {}/{}: currently set upper ROF limit {} out of bounds; set to maximum {} instead.", frame + 1, totalFrames, rofEnd, rofArr->size() - 1); + rofFrameEnd = rofArr->size() - 1; + } + + // First loop: for each pair of ROFs in the first window, compare the tracks within them + for (int rof1 = rofStart; rof1 < rofStart + windowSize - 1; rof1++) { + for (int rof2 = rof1 + 1; rof2 <= rofStart + windowSize - 1; rof2++) { + rof1Low = rofArr->at(rof1).getEntry().getFirstEntry(); + rof1High = rofArr->at(rof1).getEntry().getEntriesBound(); + rof2Low = rofArr->at(rof2).getEntry().getFirstEntry(); + rof2High = rofArr->at(rof2).getEntry().getEntriesBound(); + for (int iTrack1 = rof1Low; iTrack1 < rof1High; ++iTrack1) { + for (int iTrack2 = rof2Low; iTrack2 < rof2High; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof1, rof2, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof1, rof2, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + } + LOGP(info, "Frame {}/{}: initial ROF window {}-{} complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, rofStart, rofStart + windowSize - 1, dupl, comps); + + // Next loops: for each new window, pair the last ROF with the other ROFs and run comparisons. We avoid other ROF pairs since they have already been done + for (int rofWLow = rofStart + 1; rofWLow <= rofFrameEnd - windowSize + 1; rofWLow++) { + int rof2 = rofWLow + windowSize - 1; + for (int rof1 = rofWLow; rof1 < rof2; rof1++) { + rof1Low = rofArr->at(rof1).getEntry().getFirstEntry(); + rof1High = rofArr->at(rof1).getEntry().getEntriesBound(); + rof2Low = rofArr->at(rof2).getEntry().getFirstEntry(); + rof2High = rofArr->at(rof2).getEntry().getEntriesBound(); + for (int iTrack1 = rof1Low; iTrack1 < rof1High; ++iTrack1) { + for (int iTrack2 = rof2Low; iTrack2 < rof2High; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof1, rof2, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof1, rof2, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + } + LOGP(info, "Frame {}/{}: inter-ROF analysis complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, dupl, comps); + + if (includeSame) { // If includeSame == 1, then we also run comparisons between tracks in the same ROF. Otherwise, skip + for (int rof = rofStart; rof <= rofFrameEnd; rof++) { + rofLow = rofArr->at(rof).getEntry().getFirstEntry(); + rofHigh = rofArr->at(rof).getEntry().getEntriesBound(); + for (int iTrack1 = rofLow; iTrack1 < rofHigh; ++iTrack1) { + for (int iTrack2 = iTrack1 + 1; iTrack2 < rofHigh; ++iTrack2) { + phi1 = recArr->at(iTrack1).getPhi(); + phi2 = recArr->at(iTrack2).getPhi(); + eta1 = recArr->at(iTrack1).getEta(); + eta2 = recArr->at(iTrack2).getEta(); + + dPhi = phi1 - phi2; + dEta = eta1 - eta2; + avgPhi = (phi1 + phi2) / 2; + avgEta = (eta1 + eta2) / 2; + h1->Fill(dPhi, dEta); + h2->Fill(abs(dPhi), abs(dEta)); + + if (abs(dPhi) < phiLim && abs(dEta) < etaLim) { + if (dPhi == 0 && dEta == 0) { + LOGP(warning, "Zero found; ROFs: {}, {}. Tracks: {}, {}. dPhi=dEta=0. Not included in plots.", rof, rof, iTrack1, iTrack2); + zeroes++; + } else { + LOGP(info, "Possible duplicate found. ROFs: {}, {}. Tracks: {}, {}. dPhi={}, dEta={}.", rof, rof, iTrack1, iTrack2, dPhi, dEta); + dupl++; + h3->Fill(dPhi, dEta); + h4->Fill(avgPhi, avgEta); + } + } + ++comps; + } + } + } + LOGP(info, "Frame {}/{}: intra-ROF analysis complete. {} possible duplicates out of {} total comparisons.", frame + 1, totalFrames, dupl, comps); + } else + LOGP(info, "Frame {}/{}: intra-ROF analysis not included as per parameters (includeSame={})", frame + 1, totalFrames, includeSame); + } + // NOTE: comparisons between ROFs in different frames are intentionally left out. Only if reconstruction was performed on contiguous CTFs do comparisons between + // ROFs in different frames make sense. + + TString prefix = ""; // insert any more information you'd like to see on the plot title here, like run numbers + h1->SetTitle(Form("%s/%i ROFs/window size %i;#Delta#phi [rad];#Delta#eta", prefix.Data(), totalROFs, windowSize)); + + gStyle->SetOptStat("ne"); + auto c1 = new TCanvas("c1", "", 0, 0, 1200, 800); + h1->SetStats(1); + h1->GetYaxis()->SetTitleOffset(1.4); + h1->GetXaxis()->SetTitleOffset(1.4); + h1->GetXaxis()->CenterTitle(true); + h1->GetYaxis()->CenterTitle(true); + h1->Draw("lego"); + + auto c2 = new TCanvas("c2", "", 0, 0, 1200, 800); + h2->SetStats(0); + h2->Draw("COLZ"); + + auto c3 = new TCanvas("c3", "", 0, 0, 1200, 800); + h3->SetStats(1); + h3->GetXaxis()->SetMaxDigits(1); + h3->GetXaxis()->CenterTitle(true); + h3->GetYaxis()->CenterTitle(true); + TGaxis::SetExponentOffset(0, -0.05, "x"); + h3->GetYaxis()->SetMaxDigits(1); + h3->GetYaxis()->SetTitleOffset(0.8); + h3->Draw("COLZ"); + + auto c4 = new TCanvas("c4", "", 0, 0, 1200, 800); + h4->SetStats(0); + h4->Draw("COLZ"); + + auto c5 = new TCanvas("c5", "", 0, 0, 1200, 800); + TH1D* h5 = h4->ProjectionX("pPhi"); + h5->SetTitle("Duplicate distribution over #phi"); + c5->SetLogy(); + h5->SetStats(0); + h5->Draw(); + + auto c6 = new TCanvas("c6", "", 0, 0, 1200, 800); + TH1D* h6 = h4->ProjectionY("pEta"); + h6->SetTitle("Duplicate distribution over #eta"); + c6->SetLogy(); + h6->SetStats(0); + h6->Draw(); + + if (rootSave.Length() > 0) { + TFile output(Form("%s/projections.root", rootSave.Data()), "RECREATE"); + h5->Write(); + h6->Write(); + output.Close(); + } + + TString filename = Form("%s/plots.pdf", output.Data()); + c1->Print(filename + "["); + c1->Print(filename); + c2->Print(filename); + c3->Print(filename); + c4->Print(filename); + c5->Print(filename); + c6->Print(filename); + c6->Print(filename + "]"); + LOGP(info, "Summary:"); + LOGP(info, "Zeroes: {}; duplicates: {}; total comparisons: {}.", zeroes, dupl, comps); +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C index df26dabf38895..4e4dfcc41b928 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,18 +33,39 @@ #include #endif -void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict); +static bool invPal = false; +void getClusterPatterns(std::vector& pattVec, + std::vector* ITSclus, + std::vector* ITSpatt, + o2::itsmft::TopologyDictionary& mdict); -void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsigned int nRofs = 3, const string fname = "o2clus_its.root") +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed = nullptr); + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame); + +void CheckSquasher1(const uint chipId = 0, const uint startingROF = 0, const uint nRofs = 3, bool showSuperimposed = false, const string fname = "o2clus_its.root") { - TColor::InvertPalette(); + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + gStyle->SetOptStat(0); // Geometry - o2::base::GeometryManager::loadGeometry(""); - auto gman = o2::its::GeometryTGeo::Instance(); + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); + auto* gman = cc.get("ITS/Config/Geometry"); gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + // Topology dictionary - auto& cc = o2::ccdb::BasicCCDBManager::instance(); auto mdict = cc.get("ITS/Calib/ClusterDictionary"); auto fITSclus = TFile::Open(fname.data(), "r"); auto treeITSclus = (TTree*)fITSclus->Get("o2sim"); @@ -60,6 +82,8 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi auto clSpan = gsl::span(ITSclus->data(), ITSclus->size()); std::vector hHitMapsVsFrame(nRofs); + TH2D* hHitMapSuperimposed = nullptr; + hHitMapSuperimposed = new TH2D(Form("chip%i_superimposed", chipId), Form("chip %i superimposed; ; ; Counts", chipId), 1024, -0.5, 1023.5, 512, -0.5, 511.5); treeITSclus->GetEvent(0); LOGP(info, "there are {} rofs in this TF", ITSrof->size()); @@ -69,62 +93,17 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi getClusterPatterns(pattVec, ITSclus, ITSpatt, *mdict); for (unsigned int iR{0}; iR < nRofs; iR++) { - LOGP(info, "Processing rof {}", iR + startingROF); + LOGP(info, "===============\n \tProcessing rof {} \n\t===============", iR + startingROF); hHitMapsVsFrame[iR] = new TH2D(Form("chip%i_rof%i", chipId, startingROF + iR), Form("chip %i rof %i; ; ; Counts", chipId, startingROF + iR), 1024, -0.5, 1023.5, 512, -0.5, 511.5); // work on data const auto& rof = (*ITSrof)[startingROF + iR]; auto clustersInFrame = rof.getROFData(*ITSclus); auto patternsInFrame = rof.getROFData(pattVec); - // auto pattIt = ITSpatt->cbegin(); - - for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { - const auto& clus = clustersInFrame[clusInd]; - auto sID = clus.getSensorID(); - - if (sID == chipId) { - clus.print(); - - // auto labels = clusLabArr->getLabels(clusInd); - // extract pattern info - auto col = clus.getCol(); - auto row = clus.getRow(); - - std::cout << patternsInFrame[clusInd]; - - std::cout << std::endl; - int ic = 0, ir = 0; - - auto colSpan = patternsInFrame[clusInd].getColumnSpan(); - auto rowSpan = patternsInFrame[clusInd].getRowSpan(); - auto nBits = rowSpan * colSpan; - - for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { - unsigned char tempChar = patternsInFrame[clusInd].getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) // checking active pixels - { - hHitMapsVsFrame[iR]->Fill(col + ic, row + ir); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - } + drawClustersInChipInRof(chipId, clustersInFrame, patternsInFrame, hHitMapsVsFrame[iR], hHitMapSuperimposed); } auto canvas = new TCanvas(Form("chip%d", chipId), Form("chip%d", chipId), nRofs * 1000, 600); + auto canvasSuperimposition = new TCanvas(Form("chip%d_superimposed", chipId), Form("chip%d_superimposed", chipId), 600, 600); canvas->Divide(nRofs, 1); for (unsigned int i{0}; i < nRofs; ++i) { @@ -133,6 +112,84 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi gPad->SetGridy(); hHitMapsVsFrame[i]->Draw("colz"); } + canvasSuperimposition->cd(); + gPad->SetGridx(); + gPad->SetGridy(); + if (showSuperimposed) { + hHitMapSuperimposed->Draw("colz"); + } else { + delete canvasSuperimposition; + } +} + +void CheckSquasher(const int staveId, const uint layerId, const uint startingROF = 0, const uint nRofs = 3, const string fname = "o2clus_its.root") +{ + std::array staves{12, 14, 16}; + if (!invPal) { + TColor::InvertPalette(); + invPal = true; + } + + gStyle->SetOptStat(0); + // Geometry + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + cc.setTimestamp(o2::ccdb::getCurrentTimestamp()); + auto* gman = cc.get("ITS/Config/Geometry"); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + + // Topology dictionary + auto mdict = cc.get("ITS/Calib/ClusterDictionary"); + auto fITSclus = TFile::Open(fname.data(), "r"); + auto treeITSclus = (TTree*)fITSclus->Get("o2sim"); + + std::vector* ITSclus = nullptr; + std::vector* ITSrof = nullptr; + std::vector* ITSpatt = nullptr; + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + + treeITSclus->SetBranchAddress("ITSClusterComp", &ITSclus); + treeITSclus->SetBranchAddress("ITSClustersROF", &ITSrof); + treeITSclus->SetBranchAddress("ITSClusterPatt", &ITSpatt); + + auto clSpan = gsl::span(ITSclus->data(), ITSclus->size()); + std::vector hHitMapsVsFrame(nRofs); + TH2D* hHitMapSuperimposed = nullptr; + std::vector hHitMapStaves; + hHitMapStaves.resize(staveId < 0 ? staves[layerId] : 1); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + hHitMapStaves[staveId < 0 ? iStave : 0] = new TH2D(Form("stave_%d", staveId < 0 ? iStave : staveId), "", 1024 * 9, -0.5, 1023.5 * 9, 512, -0.5, 511.5); + } + + treeITSclus->GetEvent(0); + LOGP(info, "there are {} rofs in this TF", ITSrof->size()); + + // Get patterns + std::vector pattVec; + getClusterPatterns(pattVec, ITSclus, ITSpatt, *mdict); + + for (unsigned int iR{0}; iR < nRofs; iR++) { + LOGP(info, " ===============\n \tProcessing rof {} \n\t===============", iR + startingROF); + // work on data + const auto& rof = (*ITSrof)[startingROF + iR]; + auto clustersInFrame = rof.getROFData(*ITSclus); + auto patternsInFrame = rof.getROFData(pattVec); + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + drawClustersInStaveInRof(staveId < 0 ? iStave : staveId, layerId, clustersInFrame, patternsInFrame, hHitMapStaves[staveId < 0 ? iStave : 0]); + } + } + + auto canvas = new TCanvas(Form("stave%d", staveId), Form("stave%d", staveId), 9 * 1000, (staveId < 0 ? staves[layerId] : 1) * 1000); + if (staveId < 0) { + canvas->Divide(1, staves[layerId]); + } + for (int iStave{0}; iStave < (staveId < 0 ? staves[layerId] : 1); ++iStave) { + if (staveId < 0) { + canvas->cd(iStave + 1); + } + gPad->SetGridx(); + gPad->SetGridy(); + hHitMapStaves[staveId < 0 ? iStave : 0]->Draw("colz"); + } } void getClusterPatterns(std::vector& pattVec, std::vector* ITSclus, std::vector* ITSpatt, o2::itsmft::TopologyDictionary& mdict) @@ -159,3 +216,113 @@ void getClusterPatterns(std::vector& pattVec, std::v pattVec.push_back(patt); } } + +void drawClustersInChipInRof(const uint chipId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame, + TH2D* hHitMapSuperimposed) +{ + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + if (sID == chipId) { + LOGP(info, "Processing cluster {}", clusInd); + clus.print(); + + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + std::cout << patternsInFrame[clusInd]; + + std::cout << std::endl; + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + hHitMapsVsFrame->Fill(col + ic, row + ir); + hHitMapSuperimposed == nullptr ?: hHitMapSuperimposed->Fill(col + ic, row + ir); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} + +void drawClustersInStaveInRof(const int staveId, + const int layerId, + gsl::span clustersInFrame, + gsl::span patternsInFrame, + TH2D* hHitMapsVsFrame) +{ + o2::itsmft::ChipMappingITS chipMapping; + int lay, sta, ssta, mod, chipInMod; + std::array sensorIDs; // sIDs in the staves of the IB. + for (unsigned int clusInd{0}; clusInd < clustersInFrame.size(); clusInd++) { + const auto& clus = clustersInFrame[clusInd]; + auto sID = clus.getSensorID(); + + chipMapping.expandChipInfoHW(sID, lay, sta, ssta, mod, chipInMod); + + if (sta == staveId && lay == layerId) { + // extract pattern info + auto col = clus.getCol(); + auto row = clus.getRow(); + + int ic = 0, ir = 0; + + auto colSpan = patternsInFrame[clusInd].getColumnSpan(); + auto rowSpan = patternsInFrame[clusInd].getRowSpan(); + auto nBits = rowSpan * colSpan; + + for (int i = 2; i < patternsInFrame[clusInd].getUsedBytes() + 2; i++) { + unsigned char tempChar = patternsInFrame[clusInd].getByte(i); + int s = 128; // 0b10000000 + while (s > 0) { + if ((tempChar & s) != 0) // checking active pixels + { + // Normalize the chip ID to be within the range [0, 8] + int normalizedChipID = sID % 9; // Hardcode for the ITS IB + double x = col + ic + normalizedChipID * 1024; + double y = row + ir; + hHitMapsVsFrame->Fill(x, y); + } + ic++; + s >>= 1; + if ((ir + 1) * ic == nBits) { + break; + } + if (ic == colSpan) { + ic = 0; + ir++; + } + if ((ir + 1) * ic == nBits) { + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C index 79b2f52ad29df..11b03531d4af3 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracks.C @@ -15,16 +15,16 @@ #include "TGeoGlobalMagField.h" #include "Field/MagneticField.h" -#include "DetectorsBase/Propagator.h" #include "ITSBase/GeometryTGeo.h" -#include "SimulationDataFormat/TrackReference.h" +#include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/TrackReference.h" +#include "DetectorsBase/Propagator.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" +#include "SimulationDataFormat/MCTruthContainer.h" using namespace std; diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C index f1a3d2af4e469..e185be83a389f 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C @@ -1,3 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + /// \file CheckTracksCA.C /// \brief Simple macro to check ITSU tracks @@ -10,24 +21,44 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include "ITSBase/GeometryTGeo.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "DetectorsBase/Propagator.h" #include "SimulationDataFormat/TrackReference.h" +#include "SimulationDataFormat/O2DatabasePDG.h" #include "SimulationDataFormat/MCTrack.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" using namespace std; +// chi2 PDF with amplitude A, degrees of freedom k, scale s +Double_t chi2_pdf(Double_t* x, Double_t* par) +{ + const Double_t xx = x[0]; + const Double_t A = par[0]; + const Double_t k = par[1]; + const Double_t s = par[2]; + if (xx <= 0.0 || k <= 0.0 || s <= 0.0) + return 0.0; + const Double_t coef = 1.0 / (TMath::Power(2.0 * s, k * 0.5) * TMath::Gamma(k * 0.5)); + return A * coef * TMath::Power(xx, k * 0.5 - 1.0) * TMath::Exp(-xx / (2.0 * s)); +} + struct ParticleInfo { int event; int pdg; @@ -36,22 +67,39 @@ struct ParticleInfo { float phi; int mother; int first; + float pvx{}; + float pvy{}; + float pvz{}; + float dcaxy; + float dcaz; unsigned short clusters = 0u; unsigned char isReco = 0u; unsigned char isFake = 0u; bool isPrimary = 0u; unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 o2::its::TrackITS track; + o2::MCTrack mcTrack; }; #pragma link C++ class ParticleInfo + ; -void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its.root", std::string clusfile = "o2clus_its.root", std::string kinefile = "o2sim_Kine.root") +void CheckTracksCA(bool doEffStud = true, + bool doFakeClStud = false, + bool doPullStud = false, + bool createOutput = false, + std::string tracfile = "o2trac_its.root", + std::string magfile = "o2sim_grp.root", + std::string clusfile = "o2clus_its.root", + std::string kinefile = "o2sim_Kine.root") { using namespace o2::itsmft; using namespace o2::its; + // Magnetic field and Propagator + o2::base::Propagator::initFieldFromGRP(magfile); + float bz = o2::base::Propagator::Instance()->getNominalBz(); + // Geometry o2::base::GeometryManager::loadGeometry(); auto gman = o2::its::GeometryTGeo::Instance(); @@ -59,11 +107,14 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its // MC tracks TFile* file0 = TFile::Open(kinefile.data()); TTree* mcTree = (TTree*)gFile->Get("o2sim"); - mcTree->SetBranchStatus("*", 0); //disable all branches + mcTree->SetBranchStatus("*", 0); // disable all branches mcTree->SetBranchStatus("MCTrack*", 1); + mcTree->SetBranchStatus("MCEventHeader*", 1); std::vector* mcArr = nullptr; mcTree->SetBranchAddress("MCTrack", &mcArr); + o2::dataformats::MCEventHeader* mcEvent = nullptr; + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); // Clusters TFile::Open(clusfile.data()); @@ -87,18 +138,28 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its std::cout << "** Filling particle table ... " << std::flush; int lastEventIDcl = -1, cf = 0; int nev = mcTree->GetEntriesFast(); - std::vector> info(nev); + std::vector> info; + info.resize(nev); + TH1D* hZvertex = new TH1D("hZvertex", "Z vertex", 100, -20, 20); for (int n = 0; n < nev; n++) { // loop over MC events mcTree->GetEvent(n); info[n].resize(mcArr->size()); + hZvertex->Fill(mcEvent->GetZ()); for (unsigned int mcI{0}; mcI < mcArr->size(); ++mcI) { - auto part = mcArr->at(mcI); + const auto part = mcArr->at(mcI); + if (!o2::O2DatabasePDG::Instance()->GetParticle(part.GetPdgCode())) { + continue; + } info[n][mcI].event = n; info[n][mcI].pdg = part.GetPdgCode(); + info[n][mcI].pvx = mcEvent->GetX(); + info[n][mcI].pvy = mcEvent->GetY(); + info[n][mcI].pvz = mcEvent->GetZ(); info[n][mcI].pt = part.GetPt(); info[n][mcI].phi = part.GetPhi(); info[n][mcI].eta = part.GetEta(); info[n][mcI].isPrimary = part.isPrimary(); + info[n][mcI].mcTrack = part; } } std::cout << "done." << std::endl; @@ -159,8 +220,12 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its info[evID][trackID].isFake += fake; /// We keep the best track we would keep in the data if (recArr->at(iTrack).isBetter(info[evID][trackID].track, 1.e9)) { - info[evID][trackID].storedStatus = fake; info[evID][trackID].track = recArr->at(iTrack); + info[evID][trackID].storedStatus = fake; + static float ip[2]{0., 0.}; + info[evID][trackID].track.getImpactParams(info[evID][trackID].pvx, info[evID][trackID].pvy, info[evID][trackID].pvz, bz, ip); + info[evID][trackID].dcaxy = ip[0]; + info[evID][trackID].dcaz = ip[1]; } fakes += fake; @@ -175,113 +240,108 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its std::cout << "\t- Total number of fakes: " << fakes << " (" << fakes * 100. / total << "%)" << std::endl; std::cout << "\t- Total number of good: " << good << " (" << good * 100. / total << "%)" << std::endl; - int nb = 100; - double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; - double a = std::log(ptcuth / ptcutl) / nb; - for (int i = 0; i <= nb; i++) - xbins[i] = ptcutl * std::exp(i * a); - TH1D* num = new TH1D("num", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", nb, xbins); - num->Sumw2(); - TH1D* numEta = new TH1D("numEta", ";#eta;Number of tracks", 60, -3, 3); - numEta->Sumw2(); - TH1D* numChi2 = new TH1D("numChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); - - TH1D* fak = new TH1D("fak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); - fak->Sumw2(); - TH1D* multiFak = new TH1D("multiFak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); - multiFak->Sumw2(); - TH1D* fakChi2 = new TH1D("fakChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); - - TH1D* clone = new TH1D("clone", ";#it{p}_{T} (GeV/#it{c});Clone", nb, xbins); - clone->Sumw2(); - - TH1D* den = new TH1D("den", ";#it{p}_{T} (GeV/#it{c});Den", nb, xbins); - den->Sumw2(); - - for (auto& evInfo : info) { - for (auto& part : evInfo) { - if ((part.clusters & 0x7f) != 0x7f) { - // part.clusters != 0x3f && part.clusters != 0x3f << 1 && - // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && - // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { - continue; - } - if (!part.isPrimary) { - continue; - } - den->Fill(part.pt); - if (part.isReco) { - num->Fill(part.pt); - numEta->Fill(part.eta); - if (part.isReco > 1) { - for (int _i{0}; _i < part.isReco - 1; ++_i) { - clone->Fill(part.pt); + TFile* file{nullptr}; + if (createOutput) { + file = TFile::Open("CheckTracksCA.root", "recreate"); + } + + if (doEffStud) { + std::cout << "Calculating efficiencies... "; + const int nb = 100; + double xbins[nb + 1], ptcutl = 0.01, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) + xbins[i] = ptcutl * std::exp(i * a); + TH1D* num = new TH1D("num", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", nb, xbins); + num->Sumw2(); + TH1D* numEta = new TH1D("numEta", ";#eta;Number of tracks", 60, -3, 3); + numEta->Sumw2(); + TH1D* numChi2 = new TH1D("numChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); + + TH1D* fak = new TH1D("fak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); + fak->Sumw2(); + TH1D* multiFak = new TH1D("multiFak", ";#it{p}_{T} (GeV/#it{c});Fak", nb, xbins); + multiFak->Sumw2(); + TH1D* fakChi2 = new TH1D("fakChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); + + TH1D* clone = new TH1D("clone", ";#it{p}_{T} (GeV/#it{c});Clone", nb, xbins); + clone->Sumw2(); + + TH1D* den = new TH1D("den", ";#it{p}_{T} (GeV/#it{c});Den", nb, xbins); + den->Sumw2(); + + for (auto& evInfo : info) { + for (auto& part : evInfo) { + if ((part.clusters & 0x7f) != 0x7f) { + // part.clusters != 0x3f && part.clusters != 0x3f << 1 && + // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && + // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { + continue; + } + if (!part.isPrimary) { + continue; + } + den->Fill(part.pt); + if (part.isReco) { + num->Fill(part.pt); + numEta->Fill(part.eta); + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + clone->Fill(part.pt); + } } } - } - if (part.isFake) { - fak->Fill(part.pt); - if (part.isFake > 1) { - for (int _i{0}; _i < part.isFake - 1; ++_i) { - multiFak->Fill(part.pt); + if (part.isFake) { + fak->Fill(part.pt); + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + multiFak->Fill(part.pt); + } } } } } - } - TCanvas* c1 = new TCanvas; - c1->SetLogx(); - c1->SetGridx(); - c1->SetGridy(); - TH1* sum = (TH1*)num->Clone("sum"); - sum->Add(fak); - sum->Divide(sum, den, 1, 1); - sum->SetLineColor(kBlack); - sum->Draw("hist"); - num->Divide(num, den, 1, 1, "b"); - num->Draw("histesame"); - fak->Divide(fak, den, 1, 1, "b"); - fak->SetLineColor(2); - fak->Draw("histesame"); - multiFak->Divide(multiFak, den, 1, 1, "b"); - multiFak->SetLineColor(kRed + 1); - multiFak->Draw("histsame"); - clone->Divide(clone, den, 1, 1, "b"); - clone->SetLineColor(3); - clone->Draw("histesame"); - - std::cout << "** Streaming output TTree to file ... " << std::flush; - TFile file("CheckTracksCA.root", "recreate"); - TTree tree("ParticleInfo", "ParticleInfo"); - ParticleInfo pInfo; - tree.Branch("particle", &pInfo); - for (auto& event : info) { - for (auto& part : event) { - int nCl{0}; - for (unsigned int bit{0}; bit < sizeof(pInfo.clusters) * 8; ++bit) { - nCl += bool(part.clusters & (1 << bit)); - } - if (nCl < 3) { - continue; - } - pInfo = part; - tree.Fill(); + TCanvas* c1 = new TCanvas; + c1->SetLogx(); + c1->SetGridx(); + c1->SetGridy(); + TH1* sum = (TH1*)num->Clone("sum"); + sum->Add(fak); + sum->Divide(sum, den, 1, 1); + sum->SetLineColor(kBlack); + sum->Draw("hist"); + num->Divide(num, den, 1, 1, "b"); + num->Draw("histesame"); + fak->Divide(fak, den, 1, 1, "b"); + fak->SetLineColor(2); + fak->Draw("histesame"); + multiFak->Divide(multiFak, den, 1, 1, "b"); + multiFak->SetLineColor(kRed + 1); + multiFak->Draw("histsame"); + clone->Divide(clone, den, 1, 1, "b"); + clone->SetLineColor(3); + clone->Draw("histesame"); + TCanvas* c2 = new TCanvas; + c2->SetGridx(); + c2->SetGridy(); + hZvertex->DrawClone(); + + if (createOutput) { + sum->Write("total"); + fak->Write("singleFake"); + num->Write("efficiency"); + numEta->Write("etaDist"); + multiFak->Write("multiFake"); + clone->Write("clones"); } + std::cout << " done." << std::endl; } - tree.Write(); - sum->Write("total"); - fak->Write("singleFake"); - num->Write("efficiency"); - numEta->Write("etaDist"); - multiFak->Write("multiFake"); - clone->Write("clones"); - file.Close(); - std::cout << " done." << std::endl; ////////////////////// // Fake clusters study if (doFakeClStud) { + std::cout << "Creating fake cluster study... "; std::vector histLength, histLength1Fake, histLengthNoCl, histLength1FakeNoCl; std::vector stackLength, stackLength1Fake; std::vector legends, legends1Fake; @@ -321,10 +381,10 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its stackLength1Fake[iH - 4]->Add(histLength1FakeNoCl[iH - 4]); } - for (auto& event : info) { - for (auto& part : event) { + for (const auto& event : info) { + for (const auto& part : event) { int nCl{0}; - for (unsigned int bit{0}; bit < sizeof(pInfo.clusters) * 8; ++bit) { + for (unsigned int bit{0}; bit < sizeof(part.clusters) * 8; ++bit) { nCl += bool(part.clusters & (1 << bit)); } if (nCl < 3) { @@ -366,5 +426,237 @@ void CheckTracksCA(bool doFakeClStud = false, std::string tracfile = "o2trac_its gPad->BuildLegend(); } canvas->SaveAs("fakeClusters.png", "recreate"); + std::cout << " done\n"; + } + + if (doPullStud) { + std::cout << "Creating pull study... "; + const int nBins{30}; + const float xWidth{10}; + // Pulls + auto hYPull = new TH1F("hYPull", "Pull Y", nBins, -xWidth, xWidth); + auto hZPull = new TH1F("hZPull", "Pull Z", nBins, -xWidth, xWidth); + auto hSPhiPull = new TH1F("hSPhiPull", "Pull Sin(Phi)", nBins, -xWidth, xWidth); + auto hTglPull = new TH1F("hTglPull", "Pull Tg(Lambda)", nBins, -xWidth, xWidth); + auto hQoPtPull = new TH1F("hQoPtPull", "Pull Q/Pt", nBins, -xWidth, xWidth); + // Correlation + float maxY2{1e-6}, maxZ2{1e-6}, maxSnp2{2e-6}, maxTgl2{2e-6}, max1Pt2{0.01}; + auto hCorYZ = new TH2F("hCorYZ", ";#sigma_{Z}^{2};#sigma_{Y}^{2}", nBins, 0, maxZ2, nBins, 0, maxY2); + auto hCorYSPhi = new TH2F("hCorYSPhi", ";#sigma_{snp}^{2};#sigma_{Y}^{2}", nBins, 0, maxSnp2, nBins, 0, maxY2); + auto hCorYTgl = new TH2F("hCorYTgl", ";#sigma_{tgl}^{2};#sigma_{Y}^{2}", nBins, 0, maxTgl2, nBins, 0, maxY2); + auto hCorYQoPt = new TH2F("hCorYQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{Y}^{2}", nBins, 0, max1Pt2, nBins, 0, maxY2); + + auto hCorZSPhi = new TH2F("hCorZSPhi", ";#sigma_{snp}^{2};#sigma_{Z}^{2}", nBins, 0, maxSnp2, nBins, 0, maxZ2); + auto hCorZTgl = new TH2F("hCorZTgl", ";#sigma_{tgl}^{2};#sigma_{Z}^{2}", nBins, 0, maxTgl2, nBins, 0, maxZ2); + auto hCorZQoPt = new TH2F("hCorZQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{Z}^{2}", nBins, 0, max1Pt2, nBins, 0, maxZ2); + + auto hCorSPhiTgl = new TH2F("hCorSPhiTgl", ";#sigma_{tgl}^{2};#sigma_{snp}^{2}", nBins, 0, maxTgl2, nBins, 0, maxSnp2); + auto hCorSPhiQoPt = new TH2F("hCorSPhiQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{snp}^{2}", nBins, 0, max1Pt2, nBins, 0, maxSnp2); + + auto hCorTglQoPt = new TH2F("hCorTglQoPt", ";#sigma_{Q/Pt}^{2};#sigma_{tgl}^{2}", nBins, 0, max1Pt2, nBins, 0, maxTgl2); + + auto calcMahalanobisDist = [&](const auto& trk, const auto& mc) -> float { + o2::math_utils::SMatrix> cov; + cov(o2::track::kY, o2::track::kY) = trk.getSigmaY2(); + cov(o2::track::kZ, o2::track::kY) = trk.getSigmaZY(); + cov(o2::track::kZ, o2::track::kZ) = trk.getSigmaZ2(); + cov(o2::track::kSnp, o2::track::kY) = trk.getSigmaSnpY(); + cov(o2::track::kSnp, o2::track::kZ) = trk.getSigmaSnpZ(); + cov(o2::track::kSnp, o2::track::kSnp) = trk.getSigmaSnp2(); + cov(o2::track::kTgl, o2::track::kY) = trk.getSigmaTglY(); + cov(o2::track::kTgl, o2::track::kZ) = trk.getSigmaTglZ(); + cov(o2::track::kTgl, o2::track::kSnp) = trk.getSigmaTglSnp(); + cov(o2::track::kTgl, o2::track::kTgl) = trk.getSigmaTgl2(); + cov(o2::track::kQ2Pt, o2::track::kY) = trk.getSigma1PtY(); + cov(o2::track::kQ2Pt, o2::track::kZ) = trk.getSigma1PtZ(); + cov(o2::track::kQ2Pt, o2::track::kSnp) = trk.getSigma1PtSnp(); + cov(o2::track::kQ2Pt, o2::track::kTgl) = trk.getSigma1PtTgl(); + cov(o2::track::kQ2Pt, o2::track::kQ2Pt) = trk.getSigma1Pt2(); + if (!cov.Invert()) { + return -1.f; + } + o2::math_utils::SVector trkPar(trk.getParams(), o2::track::kNParams), mcPar(mc.getParams(), o2::track::kNParams); + auto res = trkPar - mcPar; + return std::sqrt(ROOT::Math::Similarity(cov, res)); + }; + auto hMahDist = new TH1F("hMahDist", ";Mahalanobis distance;n. entries", 100, 0, 10); + TF1* fchi = new TF1("fchi", chi2_pdf, 0, 6, 3); + fchi->SetParNames("A", "k", "s"); + fchi->SetParameter(0, 1); + fchi->SetParameter(1, 5); + fchi->SetParameter(2, 1); + + for (const auto& event : info) { + for (const auto& part : event) { + if (((part.clusters & 0x7f) != 0x7f) && !part.isPrimary) { + continue; + } + + // prepare mc truth parameters + std::array xyz{(float)part.mcTrack.GetStartVertexCoordinatesX(), (float)part.mcTrack.GetStartVertexCoordinatesY(), (float)part.mcTrack.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)part.mcTrack.GetStartVertexMomentumX(), (float)part.mcTrack.GetStartVertexMomentumY(), (float)part.mcTrack.GetStartVertexMomentumZ()}; + o2::track::TrackPar mcTrack(xyz, pxyz, TMath::Nint(o2::O2DatabasePDG::Instance()->GetParticle(part.mcTrack.GetPdgCode())->Charge() / 3), false); + if (!mcTrack.rotate(part.track.getAlpha()) || + !o2::base::Propagator::Instance()->propagateTo(mcTrack, part.track.getX())) { + continue; + } + + const float sY = part.track.getSigmaY2(); + const float sZ = part.track.getSigmaZ2(); + const float sSnp = part.track.getSigmaSnp2(); + const float sTgl = part.track.getSigmaTgl2(); + const float s1Pt = part.track.getSigma1Pt2(); + + hYPull->Fill((part.track.getY() - mcTrack.getY()) / std::sqrt(part.track.getSigmaY2())); + hZPull->Fill((part.track.getZ() - mcTrack.getZ()) / std::sqrt(part.track.getSigmaZ2())); + hSPhiPull->Fill((part.track.getSnp() - mcTrack.getSnp()) / std::sqrt(part.track.getSigmaSnp2())); + hTglPull->Fill((part.track.getTgl() - mcTrack.getTgl()) / std::sqrt(part.track.getSigmaTgl2())); + hQoPtPull->Fill((part.track.getQ2Pt() - mcTrack.getQ2Pt()) / std::sqrt(part.track.getSigma1Pt2())); + + hCorYZ->Fill(part.track.getSigmaZ2(), part.track.getSigmaY2()); + hCorYSPhi->Fill(part.track.getSigmaSnp2(), part.track.getSigmaY2()); + hCorYTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaY2()); + hCorYQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaY2()); + + hCorZSPhi->Fill(part.track.getSigmaSnp2(), part.track.getSigmaZ2()); + hCorZTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaZ2()); + hCorZQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaZ2()); + + hCorSPhiTgl->Fill(part.track.getSigmaTgl2(), part.track.getSigmaSnp2()); + hCorSPhiQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaSnp2()); + + hCorTglQoPt->Fill(part.track.getSigma1Pt2(), part.track.getSigmaTgl2()); + + hMahDist->Fill(calcMahalanobisDist(part.track, mcTrack)); + } + } + + // normalise, set axis, fit and draw + auto doPullCalc = [](TH1F* h) { + h->Scale(1. / h->Integral("width")); + h->GetYaxis()->SetRangeUser(1e-5, 1.); + gPad->SetLogy(); + h->Draw("hist"); + h->Fit("gaus", "QMR", "", -3, 3); + if (auto f = h->GetFunction("gaus")) { + f->SetLineColor(kRed); + f->SetLineWidth(2); + f->Draw("same"); + const double mean = f->GetParameter(1); + const double sigma = f->GetParameter(2); + TLatex lat; + lat.SetNDC(); + lat.SetTextFont(42); + lat.SetTextSize(0.04); + lat.DrawLatex(0.62, 0.85, Form("#mu = %.4f", mean)); + lat.DrawLatex(0.62, 0.79, Form("#sigma = %.4f", sigma)); + } + }; + hMahDist->Scale(1. / hMahDist->Integral("width")); + TFitResultPtr fitres = hMahDist->Fit(fchi, "RMQS"); + + auto c = new TCanvas("cPull", "", 2000, 1000); + c->Divide(5, 5); + c->cd(1); + doPullCalc(hYPull); + c->cd(2); + hCorYZ->Draw("colz"); + c->cd(3); + hCorYSPhi->Draw("colz"); + c->cd(4); + hCorYTgl->Draw("colz"); + c->cd(5); + hCorYQoPt->Draw("colz"); + + c->cd(7); + doPullCalc(hZPull); + c->cd(8); + hCorZSPhi->Draw("colz"); + c->cd(9); + hCorZTgl->Draw("colz"); + c->cd(10); + hCorZQoPt->Draw("colz"); + + c->cd(13); + doPullCalc(hSPhiPull); + c->cd(14); + hCorSPhiTgl->Draw("colz"); + c->cd(15); + hCorSPhiQoPt->Draw("colz"); + + c->cd(19); + doPullCalc(hTglPull); + c->cd(20); + hCorTglQoPt->Draw("colz"); + + c->cd(); + const double xlow = 0.0; + const double xup = 0.4; + const double ylow = 0.0; + const double yup = 0.4; + auto pMahBig = new TPad("pMahBig", "Mahalanobis Distance", xlow, ylow, xup, yup); + pMahBig->SetFillStyle(4000); + pMahBig->SetBorderMode(0); + pMahBig->SetLeftMargin(0.12); + pMahBig->SetRightMargin(0.02); + pMahBig->SetBottomMargin(0.12); + pMahBig->SetTopMargin(0.05); + pMahBig->Draw(); + pMahBig->cd(); + hMahDist->Draw("hist"); + fchi->SetLineColor(kRed); + fchi->SetLineWidth(2); + fchi->Draw("same"); + const Double_t A_fit = fchi->GetParameter(0); + const Double_t k_fit = fchi->GetParameter(1); + const Double_t s_fit = fchi->GetParameter(2); + const Double_t A_err = fchi->GetParError(0); + const Double_t k_err = fchi->GetParError(1); + const Double_t s_err = fchi->GetParError(2); + const Double_t chi2 = fchi->GetChisquare(); + const Int_t ndf = fchi->GetNDF(); + TLatex lat; + lat.SetNDC(); + lat.SetTextFont(42); + lat.SetTextSize(0.038); + lat.SetTextAlign(11); + const Double_t xText = 0.55; + Double_t yText = 0.85; + const Double_t dy = 0.06; + lat.DrawLatex(xText, yText, Form("A = %.3g #pm %.3g", A_fit, A_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("k (ndf) = %.3f #pm %.3f", k_fit, k_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("scale s = %.3g #pm %.3g", s_fit, s_err)); + yText -= dy; + lat.DrawLatex(xText, yText, Form("#chi^{2}/ndf = %.2f / %d", chi2, ndf)); + yText -= dy; + if (fitres.Get()) { + lat.DrawLatex(xText, yText, Form("Fit status = %d", fitres->Status())); + yText -= dy; + } + + c->cd(25); + doPullCalc(hQoPtPull); + c->Draw(); + std::cout << " done\n"; + } + + if (createOutput) { + std::cout << "** Streaming output TTree to file ... " << std::flush; + TTree tree("ParticleInfo", "ParticleInfo"); + ParticleInfo pInfo; + tree.Branch("particle", &pInfo); + for (const auto& event : info) { + for (const auto& part : event) { + if (((part.clusters & 0x7f) != 0x7f) && !part.isPrimary) { + continue; + } + pInfo = part; + tree.Fill(); + } + } + tree.Write(); + file->Close(); } } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C b/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C index f348272425b77..2da3ed9b97a28 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckVertices.C @@ -29,6 +29,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" #endif +#include "DataFormatsITSMFT/CompCluster.h" o2::MCCompLabel getMainLabel(std::vector& labs); @@ -335,4 +336,4 @@ void CheckVertices(const int dumprof = -1, std::string path = "tf1/", std::strin LOGP(info, "Summary:"); LOGP(info, "Found {} vertices in {} usable out of {} simulated", nvt, nevts, simVerts.size()); LOGP(info, "Average good vertexing efficiency: {}%", (addeff / (float)nroffilled) * 100); -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C b/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C index f0a2ef0de6398..3f906ebc04e17 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C +++ b/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C @@ -140,7 +140,7 @@ void CreateDictionaries(bool saveDeltas = false, } clusTree->GetEntry(0); if (clusTree->GetEntries() > 1 && !hitfile.empty()) { - LOGP(error, "Hits are provided but the cluster tree containes {} entries, looks like real data"); + LOGP(error, "Hits are provided but the cluster tree containes {} entries, looks like real data", clusTree->GetEntries()); return; } diff --git a/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C b/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C index e04c2ca572804..eb6cb7a39b41c 100644 --- a/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C +++ b/Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C @@ -78,7 +78,7 @@ void ITSMisaligner(const std::string& ccdbHost = "http://localhost:8080", long t std::string path = objectPath.empty() ? o2::base::DetectorNameConf::getAlignmentPath(detITS) : objectPath; LOGP(info, "Storing alignment object on {}/{}", ccdbHost, path); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty api.init(ccdbHost.c_str()); // or http://localhost:8080 for a local installation // store abitrary user object in strongly typed manner api.storeAsTFileAny(¶ms, path, metadata, tmin, tmax); diff --git a/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C b/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C index 253726f9c90fe..1c682fc25fff1 100644 --- a/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C +++ b/Detectors/ITSMFT/ITS/macros/test/run_digi2rawVarPage_its.C @@ -12,9 +12,9 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTSimulation/MC2RawEncoder.h" #endif +#include "ITSMFTSimulation/MC2RawEncoder.h" // demo macro the MC->raw conversion with new (variable page size) format void setupLinks(o2::itsmft::MC2RawEncoder& m2r, const std::string& outPrefix); diff --git a/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt new file mode 100644 index 0000000000000..83bb2ec86dbca --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +add_subdirectory(studies) +add_subdirectory(workflow) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt new file mode 100644 index 0000000000000..9794b69631d57 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ITSPostprocessing +SOURCES src/ImpactParameter.cxx + src/AvgClusSize.cxx + src/PIDStudy.cxx + src/ITSStudiesConfigParam.cxx + src/AnomalyStudy.cxx + src/TrackCheck.cxx + src/TrackExtension.cxx + src/Efficiency.cxx + src/Helpers.cxx +PUBLIC_LINK_LIBRARIES O2::GlobalTracking + O2::GlobalTrackingWorkflowReaders + O2::GlobalTrackingWorkflowHelpers + O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + O2::DetectorsBase) + +o2_target_root_dictionary(ITSPostprocessing +HEADERS include/ITSStudies/ITSStudiesConfigParam.h + include/ITSStudies/TrackCuts.h + include/ITSStudies/TrackMethods.h +LINKDEF src/ITSStudiesLinkDef.h) + +add_subdirectory(macros) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h new file mode 100644 index 0000000000000..946b5ef08fd48 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AnomalyStudy.h @@ -0,0 +1,29 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ANOMALY_STUDY_H +#define O2_ANOMALY_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2::its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getAnomalyStudy(mask_t srcClustersMask, bool useMC); +} // namespace study +} // namespace o2::its + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h new file mode 100644 index 0000000000000..766bb1f94ca1f --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AvgClusSize.h +/// \author Tucker Hwang mhwang@cern.ch + +#ifndef O2_AVGCLUSSIZE_STUDY_H +#define O2_AVGCLUSSIZE_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h new file mode 100644 index 0000000000000..b6f43bb772390 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Efficiency.h @@ -0,0 +1,57 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_EFFICIENCY_STUDY_H +#define O2_EFFICIENCY_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getEfficiencyStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); + +float mEtaCuts[2] = {-1.0, 1.0}; +float mPtCuts[2] = {0, 10}; /// no cut for B=0 + +// values obtained from the dca study for B=5 +// float dcaXY[3] = {-0.000326, -0.000217, -0.000187}; +// float dcaZ[3] = {0.000020, -0.000004, 0.000032}; +// float sigmaDcaXY[3] = {0.001375, 0.001279, 0.002681}; +// float sigmaDcaZ[3] = {0.002196, 0.002083, 0.004125}; + +// values obtained from the dca study for B=0 +float dcaXY[3] = {-0.000328, -0.000213, -0.000203}; +float dcaZ[3] = {-0.000000543, -0.000013, 0.000001}; +float sigmaDcaXY[3] = {0.00109, 0.000895, 0.001520}; +float sigmaDcaZ[3] = {0.001366, 0.001149, 0.001868}; + +int dcaCut = 8; + +float mDCACutsXY[3][2] = {{dcaXY[0] - dcaCut * sigmaDcaXY[0], dcaXY[0] + dcaCut* sigmaDcaXY[0]}, {dcaXY[1] - dcaCut * sigmaDcaXY[1], dcaXY[1] + dcaCut* sigmaDcaXY[1]}, {dcaXY[2] - dcaCut * sigmaDcaXY[2], dcaXY[2] + dcaCut* sigmaDcaXY[2]}}; // cuts at 8 sigma for each layer for xy. The values represent m-8sigma and m+8sigma +float mDCACutsZ[3][2] = {{dcaZ[0] - dcaCut * sigmaDcaZ[0], dcaZ[0] + dcaCut* sigmaDcaZ[0]}, {dcaZ[1] - dcaCut * sigmaDcaZ[1], dcaZ[1] + dcaCut* sigmaDcaZ[1]}, {dcaZ[2] - dcaCut * sigmaDcaZ[2], dcaZ[2] + dcaCut* sigmaDcaZ[2]}}; + +/// excluding bad chips in MC that are not present in data: to be checked based on the anchoring +std::vector mExcludedChipMC = {66, 67, 68, 75, 76, 77, 84, 85, 86, 93, 94, 95, 102, 103, 104, 265, 266, 267, 274, 275, 276, 283, 284, 285, 413, 414, 415, 422, 423, 424, 431, 432, 433}; + +} // namespace study +} // namespace its +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h new file mode 100644 index 0000000000000..1df602f7d7f17 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/Helpers.h @@ -0,0 +1,67 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// @author Antonio Palasciano, antonio.palasciano@ba.infn.it +/// + +#ifndef O2_ITS_STUDY_HELPERS_H +#define O2_ITS_STUDY_HELPERS_H + +#include +#include "TH1F.h" +#include "TH2F.h" +#include "TGraphAsymmErrors.h" +#include "TCanvas.h" +#include "TPaveText.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +namespace helpers +{ + +/// Some utility functions for postprocessing ITS +/// + +/// get a vector containing binning info for constant sized bins on a log axis +std::vector makeLogBinning(const int nbins, const double min, const double max); + +/// Set nice style for single 1D histograms +void setStyleHistogram1D(TH1F& histo, int color); +void setStyleHistogram1D(TH1F& histo, int color, TString title, TString titleYaxis, TString titleXaxis); +void setStyleHistogram1DMeanValues(TH1F& histo, int color); +void setStyleHistogram2D(TH2F& histo); + +/// prepare canvas with two TH1F plots +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TH1F& h2, int color2); +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, bool logScale = true); +TCanvas* prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, TString intRate); +TCanvas* prepareSimpleCanvas2DcaMeanValues(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2); + +/// plot canvas with TH2D + TH1D(Mean and Sigma) from slice +TCanvas* plot2DwithMeanAndSigma(TH2F& h2D, TH1F& hMean, TH1F& hSigma, int color); + +/// prepare TPaveText with labels +void paveTextITS(TPaveText* pave, TString intRate); + +/// Convert TH1F in TGraphAsymmetricError +void ConvertTH1ToTGraphAsymmError(TH1F& hMean, TH1F& hSigma, TGraphAsymmErrors*& gr); + +} // namespace helpers +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h new file mode 100644 index 0000000000000..85e114e0fb739 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h @@ -0,0 +1,118 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ITS_STUDY_CONFIG_PARAM_H +#define ITS_STUDY_CONFIG_PARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +struct ITSCheckTracksParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "TrackCheckStudy.root"; + size_t effHistBins = 100; + unsigned short trackLengthMask = 0x7f; + float effPtCutLow = 0.01; + float effPtCutHigh = 10.; + + O2ParamDef(ITSCheckTracksParamConfig, "ITSCheckTracksParam"); +}; + +struct ITSAvgClusSizeParamConfig : public o2::conf::ConfigurableParamHelper { + // Data parameters + double b = 5; // Solenoid field in kG (+/-) + + // K0s ID cuts + std::string targetV0 = "K0"; // target V0; set as "K0" or "Lambda" + float tgV0window = 0.02; // half-width of mass window for target V0 mass hypothesis testing (GeV) + float bgV0window = 0.01; // half-width of mass window for background V0 mass hypothesis testing (GeV) + float Rmin = 0.; // lower limit on V0 decay length (cm?) + float Rmax = 5.4; // upper limit on V0 decay length (cm?) + float cosPAmin = 0.995; // lower limit on cosine of pointing angle + float prongDCAmax = 0.2; // upper limit on DCA between two daughter prongs (cm?) + float dauPVDCAmin = 0.2; // lower limit on DCA between prong and primary vertex (cm?) + float v0PVDCAmax = 0.2; // upper limit on DCA between V0 and primary vertex (cm?) + int dauNClusMin = 0; // lower limit on number of ITS clusters on daughter tracks TODO: not yet implemented + + // Kinematic cut disable flags, false="leave this cut on"; NOTE: may be a better way to implement this with std::bitset<8> + bool disableCosPA = false; + bool disableRmin = false; + bool disableRmax = false; + bool disableProngDCAmax = false; + bool disableDauPVDCAmin = false; + bool disableV0PVDCAmax = false; + bool disableDauNClusmin = false; // TODO: not yet implemented + bool disableMassHypoth = true; // applies to both target and background V0 cuts + + // Plotting options + bool generatePlots = true; // flag to generate plots + std::string outFileName = "o2standalone_cluster_size_study.root"; // filename for the ROOT output of this study + + // Average cluster size plot: eta binning parameters + float etaMin = -1.5; // lower edge of lowest bin for eta binning on average cluster size + float etaMax = 1.5; // upper edge for highest bin for eta binning on average cluster size + int etaNBins = 5; // number of eta bins + + // Average cluster size plot: cluster size binning parameters + float sizeMax = 15; // upper edge of highest bin for average cluster size + int sizeNBins = 20; // number of cluster size bins + + O2ParamDef(ITSAvgClusSizeParamConfig, "ITSAvgClusSizeParam"); +}; + +struct PIDStudyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_PIDStudy.root"; + // default: average 2023 from C. Sonnabend, Nov 2023: ([0.217553 4.02762 0.00850178 2.33324 0.880904 ]) + // to-do: grab from CCDB when available + float mBBpars[5] = {0.217553, 4.02762, 0.00850178, 2.33324, 0.880904}; + float mBBres = 0.07; // default: 7% resolution + O2ParamDef(PIDStudyParamConfig, "PIDStudyParam"); +}; + +struct AnomalyStudyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_AnomalyStudy.root"; + size_t nLayersToProcess = 3; + size_t nTimeFramesOffset = 500; + size_t nRofTimeFrames = 192; + float nPhiBinsMultiplier = 1.f; + float pValueCut = 0.05; + bool doROFAnalysis = false; + + O2ParamDef(AnomalyStudyParamConfig, "AnomalyStudyParam"); +}; + +struct ITSEfficiencyParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "ITS_efficiencyStudy.root"; + double b = 0; // Solenoid field in kG (+/-) + + O2ParamDef(ITSEfficiencyParamConfig, "ITSEfficiencyParam"); +}; + +struct ITSImpactParameterParamConfig : public o2::conf::ConfigurableParamHelper { + std::string outFileName = "its_ImpParameter.root"; + int minNumberOfContributors = 0; + bool applyTrackCuts = false; + bool useAllTracks = false; + bool generatePlots = false; + + O2ParamDef(ITSImpactParameterParamConfig, "ITSImpactParameterParam"); +}; + +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h new file mode 100644 index 0000000000000..43ec3971e7ee3 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ImpactParameter.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_IMPACT_PARAMETER_STUDY_H +#define O2_IMPACT_PARAMETER_STUDY_H + +#include +#include +#include "ITSStudies/ITSStudiesConfigParam.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getImpactParameterStudy(mask_t srcTracksMask, mask_t srcClusMask, bool useMC = false); +} // namespace study +} // namespace its +} // namespace o2 + +#endif // O2_IMPACT_PARAMETER_STUDY_H \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h new file mode 100644 index 0000000000000..54c7e5f387e90 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/PIDStudy.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PIDStudy.h +/// \author Francesco Mazzaschi, fmazzasc@cern.ch + +#ifndef O2_PID_STUDY_H +#define O2_PID_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ + +using mask_t = o2::dataformats::GlobalTrackID::mask_t; + +o2::framework::DataProcessorSpec getPIDStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h new file mode 100644 index 0000000000000..406677796a9b3 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCheck.h @@ -0,0 +1,33 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACK_CHECK_STUDY_H +#define O2_TRACK_CHECK_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its +{ +namespace study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getTrackCheckStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +} // namespace study +} // namespace its +} // namespace o2 +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h new file mode 100644 index 0000000000000..03f52aae380c5 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h @@ -0,0 +1,207 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackCuts.h +/// \brief Class to perform track cuts + +// This class is developed in order to be used for extensive track selections. The selections are done detector wise +//(or for detector combinations). +// sources: https://github.com/AliceO2Group/AliceO2/blob/e988b0c43346ccb24f3515d1a24f058313f14a0f/DataFormats/Reconstruction/include/ReconstructionDataFormats/GlobalTrackID.h#L40 +// +// !!! For further development: +// The main method is isSelected(o2::dataformats::GlobalTrackID, o2::globaltracking::RecoContainer&), which is a boolean +// that returns true only if all the checks are passed. First, based on the global track id, the track detector source is +// inquired(e.g. below for ITS and TPC tracks). The source-specific tracks is initialized in order to access the +// source-specific parameters than one wants to perform selections on. +// For each detector source, the inquiry should be done in such way that “false” should be returned if the checks are not passed, +// moving to the next detector source otherwise. +// (e.g below for TPC tracks, where the track selections used here: https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx#L318 +// are reproduced; +// Moreover, an example of how this class should be used can be found here: https://github.com/AliceO2Group/AliceO2/blob/dev/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx#L184). + +#ifndef O2_TRACK_CUTS_STUDY_H +#define O2_TRACK_CUTS_STUDY_H + +#include "Framework/Logger.h" +#include "Framework/DataTypes.h" +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/MatchInfoTOF.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "DataFormatsTRD/TrackTRD.h" +#include "ITSStudies/TrackMethods.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include +#include +#include "Rtypes.h" +#include +namespace o2 +{ +namespace its +{ +namespace study +{ +using GID = o2::dataformats::GlobalTrackID; +using GIndex = o2::dataformats::VtxTrackIndex; +class TrackCuts +{ + public: + void countTPCClusters(const o2::globaltracking::RecoContainer& data) + { + const auto& tpcTracks = data.getTPCTracks(); + const auto& tpcClusRefs = data.getTPCTracksClusterRefs(); + const auto& tpcClusShMap = data.clusterShMapTPC; + const auto& tpcClusAcc = data.getTPCClusters(); + constexpr int maxRows = 152; + constexpr int neighbour = 2; + int ntr = tpcTracks.size(); + mTPCCounters.clear(); + mTPCCounters.resize(ntr); + for (int itr = 0; itr < ntr; itr++) { + std::array clMap{}, shMap{}; + uint8_t sectorIndex, rowIndex; + uint32_t clusterIndex; + auto& counters = mTPCCounters[itr]; + const auto& track = tpcTracks[itr]; + for (int i = 0; i < track.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; + clMap[rowIndex] = true; + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!shMap[rowIndex]) { + counters.shared++; + } + shMap[rowIndex] = true; + } + } + int last = -1; + for (int i = 0; i < maxRows; i++) { + if (clMap[i]) { + counters.crossed++; + counters.found++; + last = i; + } else if ((i - last) <= neighbour) { + counters.crossed++; + } else { + int lim = std::min(i + 1 + neighbour, maxRows); + for (int j = i + 1; j < lim; j++) { + if (clMap[j]) { + counters.crossed++; + break; + } + } + } + } + } + } + //////////////////////////////// O2 //////////////////////////////// + bool isSelected(GID trackIndex, o2::globaltracking::RecoContainer& data) + { + o2::track::TrackParCov trk; + auto contributorsGID = data.getSingleDetectorRefs(trackIndex); + auto src = trackIndex.getSource(); // make selections depending on track source + // ITS tracks + if (contributorsGID[GIndex::Source::ITS].isIndexSet()) { // ITS tracks selection + isBarrelTrack = true; + const auto& itsTrk = data.getITSTrack(contributorsGID[GID::ITS]); + int ITSnClusters = itsTrk.getNClusters(); + float ITSchi2 = itsTrk.getChi2(); + float itsChi2NCl = ITSnClusters != 0 ? ITSchi2 / (float)ITSnClusters : 0; + uint8_t itsClusterMap = itsTrk.getPattern(); + SetRequireHitsInITSLayers(1, {0, 1, 2}); + if (itsChi2NCl >= mMaxChi2PerClusterITS || + TrackMethods::FulfillsITSHitRequirements(itsClusterMap, mRequiredITSHits) == false) { + // LOGP(info,"FAILURE hits in ITS layers"); + return false; + } + } + // TPC tracks + /* if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { + LOGP(info, "****** INSIDE TPC ********"); + //countTPCClusters(data); + isBarrelTrack = true; + const auto& tpcTrk = data.getTPCTrack(contributorsGID[GID::TPC]); + const auto& tpcClData = mTPCCounters[contributorsGID[GID::TPC]]; + math_utils::Point3D v{}; + std::array dca; + int tpcNClsFindable = tpcTrk.getNClusters(); + float tpcChi2NCl = tpcTrk.getNClusters() ? tpcTrk.getChi2() / tpcTrk.getNClusters() : 0; + double tpcNClsFindableMinusFound = tpcTrk.getNClusters() - tpcClData.found; + double tpcNClsFindableMinusCrossedRows = tpcTrk.getNClusters() - tpcClData.crossed; + float tpcNClsCrossedRows = (float)((int16_t)tpcNClsFindable - tpcNClsFindableMinusCrossedRows)/tpcNClsFindable; + float tpcCrossedRowsOverFindableCl = (float)tpcNClsCrossedRows / (float)tpcNClsFindable; + if(tpcCrossedRowsOverFindableCl < 0.8) { + LOGP(info,"FAILURE tpcCrossedRowsOverFindableCl"); + return false; + } + if(tpcClData.crossed < 70){ + LOGP(info,"FAILURE crossed"); + return false; + } + if(tpcChi2NCl >= mMaxChi2PerClusterTPC){ + LOGP(info,"FAILURE tpcChi2NCl"); + return false; + } + } */ + if (isBarrelTrack) { // track selection for barrel tracks + trk = data.getTrackParam(trackIndex); + if (trk.getPt() < mMinPt && trk.getPt() > mMaxPt && trk.getEta() < mMinEta && trk.getEta() > mMaxEta) { + return false; + } + } + return true; + } + + void SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + { + // layer 0 corresponds to the the innermost ITS layer + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + // LOG(info) << "Track selection, set require hits in ITS layers: " << static_cast(minNRequiredHits); + } + + private: + // counters for TPC clusters + struct TPCCounters { + uint8_t shared = 0; + uint8_t found = 0; + uint8_t crossed = 0; + }; + std::vector mTPCCounters; + + bool isBarrelTrack = true; // all barrel tracks must have either ITS or TPC contribution -> true if ITS || TPC track source condition is passed + // cut values + float mPtTPCCut = 0.1f; + float mEtaTPCCut = 1.4f; + int32_t mNTPCClustersCut = 60; + float mDCACut = 100.f; + float mDCACutY = 10.f; + // kinematic cuts + float mMinPt{0.1f}, mMaxPt{1e10f}; // range in pT + float mMinEta{0.8}, mMaxEta{0.8}; // range in eta + float mBz = o2::base::Propagator::Instance()->getNominalBz(); + + float mMaxChi2PerClusterITS{36.0}; // max its fit chi2 per ITS cluster + float mMaxChi2PerClusterTPC{4.0}; // max its fit chi2 per ITS cluster + int mMinNClustersITS{0}; // min number of ITS clusters + + // vector of ITS requirements (minNRequiredHits in specific requiredLayers) + int8_t minNRequiredHits = 1; + std::set requiredLayers{0, 1, 2}; // one hit in the first three layers + std::vector>> mRequiredITSHits{}; + + ClassDefNV(TrackCuts, 1); +}; +} // namespace study +} // namespace its +} // namespace o2 + +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h new file mode 100644 index 0000000000000..fd5b93b0f9509 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACK_EXTENSION_STUDY_H +#define O2_TRACK_EXTENSION_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace steer +{ +class MCKinematicsReader; +} +namespace its::study +{ +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +o2::framework::DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader); +} // namespace its::study + +} // namespace o2 +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h new file mode 100644 index 0000000000000..55f92843cd14d --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h @@ -0,0 +1,106 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackCuts.h +/// \brief Class to store some methods used in TrackCuts +/// \author antonio.palasciano@ba.infn.it + +#ifndef O2_TRACK_METHODS_STUDY_H +#define O2_TRACK_METHODS_STUDY_H + +#include "DataFormatsTPC/TrackTPC.h" +#include "GPUTPCGMMergedTrackHit.h" +#include "DataFormatsITS/TrackITS.h" +#include "ITSStudies/TrackCuts.h" +#include "ITSStudies/TrackMethods.h" +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +class TrackMethods +{ + public: + static void countTPCClusters(const o2::tpc::TrackTPC& track, + const gsl::span& tpcClusRefs, + const gsl::span& tpcClusShMap, + const o2::tpc::ClusterNativeAccess& tpcClusAcc, + uint8_t& shared, uint8_t& found, uint8_t& crossed) + { + LOGP(info, "tpcClusRefs {}/{}", (void*)tpcClusRefs.data(), tpcClusRefs.size()); + LOGP(info, "tpcClusShMap {}/{}", (void*)tpcClusShMap.data(), tpcClusShMap.size()); + LOGP(info, " tpcClusAcc{}/{}", (void*)tpcClusAcc.clustersLinear, tpcClusAcc.nClustersTotal); + constexpr int maxRows = 152; + constexpr int neighbour = 2; + std::array clMap{}, shMap{}; + uint8_t sectorIndex; + uint8_t rowIndex; + uint32_t clusterIndex; + shared = 0; + for (int i = 0; i < track.getNClusterReferences(); i++) { + o2::tpc::TrackTPC::getClusterReference(tpcClusRefs, i, sectorIndex, rowIndex, clusterIndex, track.getClusterRef()); + unsigned int absoluteIndex = tpcClusAcc.clusterOffset[sectorIndex][rowIndex] + clusterIndex; + clMap[rowIndex] = true; + if (tpcClusShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (!shMap[rowIndex]) { + shared++; + } + shMap[rowIndex] = true; + } + } + + crossed = 0; + found = 0; + int last = -1; + for (int i = 0; i < maxRows; i++) { + if (clMap[i]) { + crossed++; + found++; + last = i; + } else if ((i - last) <= neighbour) { + crossed++; + } else { + int lim = std::min(i + 1 + neighbour, maxRows); + for (int j = i + 1; j < lim; j++) { + if (clMap[j]) { + crossed++; + } + } + } + } + } + static bool FulfillsITSHitRequirements(uint8_t itsClusterMap, std::vector>> mRequiredITSHits) + { + constexpr uint8_t bit = 1; + for (auto& itsRequirement : mRequiredITSHits) { + auto hits = std::count_if(itsRequirement.second.begin(), itsRequirement.second.end(), [&](auto&& requiredLayer) { return itsClusterMap & (bit << requiredLayer); }); + if ((itsRequirement.first == -1) && (hits > 0)) { + return false; // no hits were required in specified layers + } else if (hits < itsRequirement.first) { + return false; // not enough hits found in specified layers + } + } + return true; + } + + private: + std::vector>> mRequiredITSHits{}; + ClassDefNV(TrackMethods, 1); +}; +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt new file mode 100644 index 0000000000000..2d78e4077ec53 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# o2_add_test_root_macro( +# PostTrackExtension.C +# PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::RIO ROOT::Core ROOT::Gpad +# LABELS its-study +# COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest new file mode 100644 index 0000000000000..29f94086aae4c --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest @@ -0,0 +1,629 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TStyle.h" +#include "TFile.h" +#include "TError.h" +#include "TColor.h" +#include "TCanvas.h" +#include "TH2D.h" +#include "TF1.h" +#include "TEfficiency.h" +#include "TMarker.h" +#include "TLegend.h" +#include "TTree.h" +#include "TLatex.h" + +#include +#include +#include +#endif + +static constexpr std::array bitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; +static constexpr std::array bitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; +inline bool bitsCleared(uint8_t b, uint8_t a) { return (a == b) ? true : (b & ~(a & b)) != 0; } +static constexpr std::array patternColors = { + kRed, // Red + kBlue, // Blue + kGreen, // Green + kMagenta, // Magenta + kCyan, // Cyan + kOrange, // Orange + kViolet, // Violet + kYellow, // Yellow + kPink, // Pink + kAzure, // Azure + kSpring, // Spring Green + kTeal, // Teal + kBlack, // Black + kGray, // Gray + kOrange + 7, // Light Orange + kBlue - 9 // Light Blue +}; + +// Marker styles +static constexpr std::array patternMarkers = { + 20, // Full circle + 21, // Full square + 22, // Full triangle up + 23, // Full triangle down + 24, // Open circle + 25, // Open square + 26, // Open triangle up + 27, // Open cross + 28, // Star + 29, // Plus sign + 30, // Open diamond + 31, // Full diamond + 32, // Cross + 33, // Circle with cross + 34, // X sign + 35 // Double open cross +}; + +enum Labels : unsigned int { + eAll = 0, + eGood, + eFake, + eFakeBefore, + eFakeAfter, + eFakeMix, + eTopGood, + eBotGood, + eMixGood, + eTopFake, + eBotFake, + eMixFake, + eN, +}; +static const std::array names{ + "ALL #frac{ext trks}{all trks}", + "GOOD #frac{good ext trks}{all ext trks}", + "FAKE #frac{fake trks}{all ext trks}", + "FAKE BF #frac{fake bf trks}{fake ext trks}", + "FAKE AF #frac{fake af trks}{fake ext trks}", + "FAKE MIX #frac{fake mix trks}{fake ext trks}", + // Good Top/Bot/Mix + "TOP #frac{good top ext trks}{good ext trks}", + "BOT #frac{good bot ext trks}{good ext trks}", + "MIX #frac{good mix ext trks}{good ext trks}", + // Fake Top/Bot/Mix + "TOP #frac{fake top ext trks}{fake ext trks}", + "BOT #frac{fake bot ext trks}{fake ext trks}", + "MIX #frac{fake mix ext trks}{fake ext trks}", +}; +static const std::array colors{kBlack, kGreen, kRed, kCyan, kYellow, kAzure, + // Good Top/Bot/Mix + kBlue, kOrange, kPink, + // Fake Top/Bot/Mix + kBlue, kOrange, kPink}; +static const std::array markers{20, 21, 22, 23, 27, 28, + // Good Top/Bot/Mix + 29, 33, 39, + // Fake Top/Bot/Mix + 29, 33, 39}; +static const char* const texPtX = "#it{p}_{T} (GeV/#it{c})"; +static const char* const texEff = "Efficiency"; +static const char* const texPtRes = "#sigma(#Delta#it{p}_{T}/#it{p}_{T})"; +static const char* const texDCAxyRes = "#sigma(DCA_{#it{xy}}) (#mum)"; +static const char* const texDCAzRes = "#sigma(DCA_{#it{z}}) (#mum)"; +static const char* const fitOpt{"QWMER"}; + +void setStyle(); +TEfficiency* makeEff(TFile*, const char* num, const char* den); + +template +void style(T* t, Labels lab, TLegend* leg = nullptr) +{ + t->SetMarkerStyle(markers[lab]); + t->SetMarkerColor(colors[lab]); + t->SetLineColor(colors[lab]); + if (leg) { + leg->AddEntry(t, names[lab]); + } +} + +template +void stylePattern(T* t, int i, TLegend* leg = nullptr, const char* name = nullptr) +{ + t->SetMarkerStyle(patternMarkers[i]); + t->SetMarkerColor(patternColors[i]); + t->SetLineColor(patternColors[i]); + if (leg) { + leg->AddEntry(t, name); + } +} + +void PostTrackExtension(const char* fileName = "TrackExtensionStudy.root") +{ + setStyle(); + + std::unique_ptr fIn{TFile::Open(fileName, "READ")}; + if (!fIn || fIn->IsZombie()) { + Error("", "Cannot open file %s", fileName); + return; + } + + { // Purity & Fake-Rate + auto c = new TCanvas("cPFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto eff = fIn->Get("eExtension"); + style(eff, eAll, leg); + eff->Draw("same"); + auto effPurity = fIn->Get("eExtensionPurity"); + style(effPurity, eGood, leg); + effPurity->Draw("same"); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_purity_fake.pdf"); + } + + { // FAKE-Rate composition + auto c = new TCanvas("cFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + auto effFakeBf = fIn->Get("eExtensionFakeBefore"); + style(effFakeBf, eFakeBefore, leg); + effFakeBf->Draw("same"); + auto effFakeAf = fIn->Get("eExtensionFakeAfter"); + style(effFakeAf, eFakeAfter, leg); + effFakeAf->Draw("same"); + auto effFakeMi = fIn->Get("eExtensionFakeMix"); + style(effFakeMi, eFakeMix, leg); + effFakeMi->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake.pdf"); + } + + { // GOOD Top/Bot/Mix Purity composition + auto c = new TCanvas("cGC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = makeEff(fIn.get(), "eExtensionTopPurity", "eExtensionPurity"); + style(effTop, eTopGood, leg); + effTop->Draw("same"); + auto effBot = makeEff(fIn.get(), "eExtensionBotPurity", "eExtensionPurity"); + style(effBot, eBotGood, leg); + effBot->Draw("same"); + auto effMix = makeEff(fIn.get(), "eExtensionMixPurity", "eExtensionPurity"); + style(effMix, eMixGood, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_good_comp.pdf"); + } + + { // FAKE Top/Bot/Mix composition + auto c = new TCanvas("cFC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = fIn->Get("eExtensionTopFake"); + style(effTop, eTopFake, leg); + effTop->Draw("same"); + auto effBot = fIn->Get("eExtensionBotFake"); + style(effBot, eBotFake, leg); + effBot->Draw("same"); + auto effMix = fIn->Get("eExtensionMixFake"); + style(effMix, eMixFake, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake_comp.pdf"); + } + + { // Good Patterns + auto c = new TCanvas("cPatGood", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} GOOD Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternGood_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_good_pattern_comp.pdf"); + } + + { // Fake Patterns + auto c = new TCanvas("cPatFake", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} FAKE Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternFake_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_fake_pattern_comp.pdf"); + } + + { // DCA + auto fGaus = new TF1("fGaus", "gaus", -200., 200.); + auto dcaXYVsPtNo = fIn->Get("hDCAxyVsPtResNormal"); + auto dcaXYVsPtYes = fIn->Get("hDCAxyVsPtResExtended"); + auto dcazVsPtNo = fIn->Get("hDCAzVsPtResNormal"); + auto dcazVsPtYes = fIn->Get("hDCAzVsPtResExtended"); + auto bins = dcazVsPtNo->GetXaxis()->GetXbins(); + auto dcaXYResNo = new TH1F("hDcaxyResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaXYResYes = new TH1F("hDcaxyResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResNo = new TH1F("hDcazResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResYes = new TH1F("hDcazResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + TH1* proj; + for (int iPt{1}; iPt <= bins->GetSize(); ++iPt) { + auto ptMin = dcaXYResNo->GetXaxis()->GetBinLowEdge(iPt); + if (ptMin < 0.1) { + continue; + } + float minFit = (ptMin < 1.) ? -200. : -75.; + float maxFit = (ptMin < 1.) ? 200. : 75.; + + proj = dcaXYVsPtNo->ProjectionY(Form("hProjDCAxy_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcaXYVsPtYes->ProjectionY(Form("hProjDCAxy_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResYes->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtNo->ProjectionY(Form("hProjDCAz_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtYes->ProjectionY(Form("hProjDCAz_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResYes->SetBinError(iPt, fGaus->GetParError(2)); + } + + dcaXYResNo->SetLineColor(kRed); + dcaXYResNo->SetMarkerColor(kRed); + dcaXYResYes->SetLineColor(kBlue); + dcaXYResYes->SetMarkerColor(kBlue); + dcaZResNo->SetLineColor(kRed); + dcaZResNo->SetMarkerColor(kRed); + dcaZResYes->SetLineColor(kBlue); + dcaZResYes->SetMarkerColor(kBlue); + + auto c = new TCanvas("cDCA", "", 2 * 800, 600); + c->Divide(2, 1); + c->cd(1); + auto h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAxyRes); + dcaXYResNo->Draw("SAME"); + dcaXYResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + auto leg = new TLegend(0.20, 0.20, 0.40, 0.40); + leg->AddEntry(dcaXYResNo, "Normal"); + leg->AddEntry(dcaXYResYes, "Extended"); + leg->Draw(); + + c->cd(2); + h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAzRes); + dcaZResNo->Draw("SAME"); + dcaZResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + + c->SaveAs("trkExt_dca.pdf"); + } + + return; + { // Kinematic variables + auto t = fIn->Get("tree"); + auto c = new TCanvas("cKG", "", 800, 600); + c->Divide(3, 2); + { + auto p = c->cd(1); + p->SetGrid(); + auto h = p->DrawFrame(-.6, 0., .6, 9.); + h->GetXaxis()->SetTitle("#frac{Q^{2}}{p_{T,TRK}}-#frac{Q^{2}}{p_{T,MC}}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtNo(100,-.6,.6)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hPtNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.04, 0.04); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.55, 8.2, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtYes(100,-.6,.6)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hPtYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.04, 0.04); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.55, 7, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(2); + p->SetGrid(); + auto h = p->DrawFrame(-3, 0., 3, 2.); + h->GetXaxis()->SetTitle("Y_{TRK}-Y_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getY()-mcTrk.getY()>>hYNo(100,-3,3)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hYNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.5, 0.5); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-2, 1.7, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getY()-mcTrk.getY()>>hYYes(100,-3,3)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hYYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.5, 0.5); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-2, 1.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(3); + p->SetGrid(); + auto h = p->DrawFrame(-2, 0., 2, 4.2); + h->GetXaxis()->SetTitle("Z_{TRK}-Z_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getZ()-mcTrk.getZ()>>hZNo(100,-2,2)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hZNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.2, 0.2); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-1.7, 3.8, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getZ()-mcTrk.getZ()>>hZYes(100,-2,2)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hZYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.2, 0.2); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-1.7, 3.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(4); + p->SetGrid(); + auto h = p->DrawFrame(-0.02, 0., 0.02, 370.); + h->GetXaxis()->SetTitle("TGL_{TRK}-TGL_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglNo(100,-0.02,0.02)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hTglNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.003, 0.003); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.018, 330, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglYes(100,-0.02,0.02)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hTglYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.003, 0.003); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.018, 310, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(5); + p->SetGrid(); + auto h = p->DrawFrame(-0.08, 0., 0.08, 80.); + h->GetXaxis()->SetTitle("SNP_{TRK}-SNP_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpNo(100,-0.08,0.08)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hSnpNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.03, 0.03); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.07, 72, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpYes(100,-0.08,0.08)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hSnpYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.03, 0.03); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.07, 66, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(6); + auto legend = new TLegend(0.2, 0.2, 0.8, 0.8); + legend->SetTextSize(0.06); + legend->SetLineWidth(3); + legend->SetHeader("GOOD tracks", "C"); + auto mBlue = new TMarker(); + mBlue->SetMarkerColor(kBlue); + mBlue->SetMarkerSize(4); + legend->AddEntry(mBlue, "extended", "p"); + auto mRed = new TMarker(); + mRed->SetMarkerColor(kRed); + mRed->SetMarkerSize(4); + legend->AddEntry(mRed, "normal", "p"); + legend->SetLineColor(kRed); + legend->Draw(); + } + c->SaveAs("trkExt_kinematics.pdf"); + } +} + +void setStyle() +{ + gStyle->Reset("Plain"); + gStyle->SetOptTitle(0); + gStyle->SetOptStat(0); + gStyle->SetPalette(kRainBow); + gStyle->SetCanvasColor(10); + gStyle->SetCanvasBorderMode(0); + gStyle->SetFrameLineWidth(1); + gStyle->SetFrameFillColor(kWhite); + gStyle->SetPadColor(10); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetHistLineWidth(1); + gStyle->SetHistLineColor(kRed); + gStyle->SetFuncWidth(2); + gStyle->SetFuncColor(kGreen); + gStyle->SetLineWidth(2); + gStyle->SetLabelSize(0.045, "xyz"); + gStyle->SetLabelOffset(0.01, "y"); + gStyle->SetLabelOffset(0.01, "x"); + gStyle->SetLabelColor(kBlack, "xyz"); + gStyle->SetTitleSize(0.05, "xyz"); + gStyle->SetTitleOffset(1.25, "y"); + gStyle->SetTitleOffset(1.2, "x"); + gStyle->SetTitleFillColor(kWhite); + gStyle->SetTextSizePixels(26); + gStyle->SetTextFont(42); + gStyle->SetTickLength(0.04, "X"); + gStyle->SetTickLength(0.04, "Y"); + gStyle->SetLegendBorderSize(0); + gStyle->SetLegendFillColor(kWhite); + gStyle->SetFillColor(kWhite); + gStyle->SetLegendFont(42); +} + +TEfficiency* makeEff(TFile* fIn, const char* num, const char* den) +{ + auto h1 = fIn->Get(num)->GetPassedHistogram(); + auto h2 = fIn->Get(den)->GetPassedHistogram(); + auto e = new TEfficiency(*h1, *h2); + return e; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx new file mode 100644 index 0000000000000..c56c2bff2e0d2 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AnomalyStudy.cxx @@ -0,0 +1,288 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/AnomalyStudy.h" +#include "ITSStudies/ITSStudiesConfigParam.h" +#include "ITSBase/GeometryTGeo.h" + +#include "Framework/Task.h" +#include "ITStracking/IOUtils.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" + +#include +#include +#include +#include + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; +using GTrackID = o2::dataformats::GlobalTrackID; +using ITSCluster = o2::BaseCluster; +class AnomalyStudy : public Task +{ + static constexpr int nChipStavesIB{9}; + + public: + AnomalyStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC) {} + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + + // custom + void prepareOutput(); + void updateTimeDependentParams(ProcessingContext& pc); + void process(o2::globaltracking::RecoContainer& recoData); + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + bool mUseMC; + int mTFCount{0}; + TStopwatch mStopwatch; + const int mNumberOfStaves[7] = {12, 16, 20, 24, 30, 42, 48}; + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + // utils + void getClusterPatterns(gsl::span&, gsl::span&, const o2::itsmft::TopologyDictionary&); + std::vector mPatterns; + o2::its::GeometryTGeo* mGeom; + o2::itsmft::ChipMappingITS mChipMapping; + + // Histos + std::vector> mTFvsPhiHist; + std::vector> mTFvsPhiClusSizeHist; + std::vector> mROFvsPhiHist; + std::vector> mROFvsPhiClusSizeHist; +}; + +void AnomalyStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + mGeom = o2::its::GeometryTGeo::Instance(); + mGeom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void AnomalyStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + prepareOutput(); + + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto nTF = o2::its::study::AnomalyStudyParamConfig::Instance().nTimeFramesOffset; + auto nROF = o2::its::study::AnomalyStudyParamConfig::Instance().nRofTimeFrames; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + + mTFvsPhiHist.resize(nLayProc); + mTFvsPhiClusSizeHist.resize(nLayProc); + if (doROFAnalysis) { + mROFvsPhiHist.resize(nLayProc); + mROFvsPhiClusSizeHist.resize(nLayProc); + } + for (unsigned int i = 0; i < nLayProc; i++) { + int phiBins = o2::its::study::AnomalyStudyParamConfig::Instance().nPhiBinsMultiplier * mNumberOfStaves[i]; + mTFvsPhiHist[i].reset(new TH2F(Form("tf_phi_occup_layer_%d", i), Form("Occupancy layer %d ; #phi ; # TF; Counts", i), phiBins, -TMath::Pi(), TMath::Pi(), nTF, 0.5, nTF + 0.5)); + mTFvsPhiClusSizeHist[i].reset(new TH2F(Form("tf_phi_clsize_layer_%d", i), Form("Cluster size layer %d ; #phi; # TF ; #lt Cluster Size #gt", i), phiBins, -TMath::Pi(), TMath::Pi(), nTF, 0.5, nTF + 0.5)); + if (doROFAnalysis) { + mROFvsPhiHist[i].reset(new TH2F(Form("rof_phi_occup_layer_%d", i), Form("Occupancy layer %d ; #phi; # ROF; Counts", i), phiBins, -TMath::Pi(), TMath::Pi(), nROF * nTF, 0.5, nROF * nTF + 0.5)); + mROFvsPhiClusSizeHist[i].reset(new TH2F(Form("rof_phi_clsize_layer_%d", i), Form("Cluster size layer %d; #phi; # ROF; #lt Cluster Size #gt", i), phiBins, -TMath::Pi(), TMath::Pi(), nROF * nTF, 0.5, nROF * nTF + 0.5)); + } + } +} + +void AnomalyStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void AnomalyStudy::endOfStream(EndOfStreamContext&) +{ + TFile* f = TFile::Open(o2::its::study::AnomalyStudyParamConfig::Instance().outFileName.c_str(), "recreate"); + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + + // Iterate over all the histograms and compute the averages + for (unsigned int i = 0; i < nLayProc; i++) { + mTFvsPhiClusSizeHist[i]->Divide(mTFvsPhiHist[i].get()); + if (doROFAnalysis) { + mROFvsPhiClusSizeHist[i]->Divide(mROFvsPhiHist[i].get()); + } + } + + // Fit slices along x of the 2D histograms + for (unsigned int iLayer = 0; iLayer < nLayProc; ++iLayer) { + int phiBins = o2::its::study::AnomalyStudyParamConfig::Instance().nPhiBinsMultiplier * mNumberOfStaves[iLayer]; + TObjArray aSlices; + auto* f1 = new TF1(Form("f1_%d", iLayer), "pol0", -TMath::Pi(), TMath::Pi()); + auto* hPValue = new TH1F(Form("pValue_%d", iLayer), Form("pValue_%d", iLayer), mTFvsPhiClusSizeHist[iLayer]->GetNbinsY(), 0.5, mTFvsPhiClusSizeHist[iLayer]->GetNbinsY() + 0.5); + mTFvsPhiClusSizeHist[iLayer]->FitSlicesX(f1, 0, -1, 0, "QNR", &aSlices); + auto* hChi2 = (TH1D*)aSlices.At(1); + for (auto iTF{0}; iTF < hChi2->GetEntries(); ++iTF) { + auto pValue = TMath::Prob(hChi2->GetBinContent(iTF + 1) * (phiBins - 1), phiBins - 1); + // LOGP(info, "Layer: {} TF: {} Chi2: {} Pvalue: {}", iLayer, iTF, hChi2->GetBinContent(iTF + 1), pValue); + hPValue->SetBinContent(iTF + 1, pValue); + } + hPValue->Write(); + // Save slices to file + for (unsigned int j = 0; j < aSlices.GetEntries(); ++j) { + auto h = (TH1D*)aSlices.At(j); + h->SetMinimum(0); + h->Write(); + } + + // Do the same for ROFs + if (doROFAnalysis) { + TObjArray aSlicesROF; + auto f1ROF = new TF1(Form("f1ROF_%d", iLayer), "pol0", -TMath::Pi(), TMath::Pi()); + mROFvsPhiClusSizeHist[iLayer]->FitSlicesX(f1ROF, 0, -1, 0, "QNR", &aSlicesROF); + // Save slices to file + for (unsigned int j = 0; j < aSlicesROF.GetEntries(); ++j) { + auto h = (TH1D*)aSlicesROF.At(j); + h->SetMinimum(0); + h->Write(); + } + } + } + + for (unsigned int i = 0; i < nLayProc; i++) { + mTFvsPhiHist[i]->Write(); + mTFvsPhiClusSizeHist[i]->Write(); + if (doROFAnalysis) { + mROFvsPhiHist[i]->Write(); + mROFvsPhiClusSizeHist[i]->Write(); + } + } + f->Close(); +} + +void AnomalyStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +// custom +void AnomalyStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + mStopwatch.Start(); + mTFCount++; + auto nROF = o2::its::study::AnomalyStudyParamConfig::Instance().nRofTimeFrames; + auto nLayProc = o2::its::study::AnomalyStudyParamConfig::Instance().nLayersToProcess; + auto doROFAnalysis = o2::its::study::AnomalyStudyParamConfig::Instance().doROFAnalysis; + int rofCount = 0; + auto clusRofRecords = recoData.getITSClustersROFRecords(); + auto compClus = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + + getClusterPatterns(compClus, clusPatt, *mDict); + + auto pattIt = clusPatt.begin(); + std::vector globalClusters; + o2::its::ioutils::convertCompactClusters(compClus, pattIt, globalClusters, mDict); + + int lay, sta, ssta, mod, chipInMod; + for (auto& rofRecord : clusRofRecords) { + auto clustersInRof = rofRecord.getROFData(compClus); + auto patternsInRof = rofRecord.getROFData(mPatterns); + auto locClustersInRof = rofRecord.getROFData(globalClusters); + for (unsigned int clusInd{0}; clusInd < clustersInRof.size(); clusInd++) { + const auto& compClus = clustersInRof[clusInd]; + auto& locClus = locClustersInRof[clusInd]; + auto& clusPattern = patternsInRof[clusInd]; + auto gloC = locClus.getXYZGlo(*mGeom); + mChipMapping.expandChipInfoHW(compClus.getChipID(), lay, sta, ssta, mod, chipInMod); + if (lay >= nLayProc) { + continue; + } + float phi = TMath::ATan2(gloC.Y(), gloC.X()); + mTFvsPhiHist[lay]->Fill(phi, mTFCount); + mTFvsPhiClusSizeHist[lay]->Fill(phi, mTFCount, clusPattern.getNPixels()); + if (doROFAnalysis) { + mROFvsPhiHist[lay]->Fill(phi, (mTFCount - 1) * nROF + rofCount); + mROFvsPhiClusSizeHist[lay]->Fill(phi, (mTFCount - 1) * nROF + rofCount, clusPattern.getNPixels()); + } + } + ++rofCount; + } + mStopwatch.Stop(); + LOGP(info, "Processed TF: {} in {} s", mTFCount, mStopwatch.RealTime()); +} + +void AnomalyStudy::prepareOutput() +{ +} + +void AnomalyStudy::getClusterPatterns(gsl::span& ITSclus, gsl::span& ITSpatt, const o2::itsmft::TopologyDictionary& mdict) +{ + mPatterns.clear(); + mPatterns.reserve(ITSclus.size()); + auto pattIt = ITSpatt.begin(); + + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + + auto pattID = clus.getPatternID(); + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict.isGroup(pattID)) { + patt.acquirePattern(pattIt); + } else { + patt = mdict.getPattern(pattID); + } + + mPatterns.push_back(patt); + } +} + +// getter +DataProcessorSpec getAnomalyStudy(mask_t srcClustersMask, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestTracks(GTrackID::getSourcesMask(""), useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-anomaly-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} +} // namespace o2::its::study \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx new file mode 100644 index 0000000000000..727d564958935 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx @@ -0,0 +1,676 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AvgClusSize.cxx +/// \brief Study to calculate average cluster size per track in the ITS +/// \author Tucker Hwang mhwang@cern.ch + +#include "ITSStudies/AvgClusSize.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "Steer/MCKinematicsReader.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ITStracking/IOUtils.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsCommonDataFormats/DetID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using PVertex = o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using Track = o2::track::TrackParCov; +using TrackITS = o2::its::TrackITS; +using DCA = o2::dataformats::DCA; +using PID = o2::track::PID; + +class AvgClusSizeStudy : public Task +{ + public: + AvgClusSizeStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC, + std::shared_ptr kineReader) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC), mKineReader(kineReader){}; + ~AvgClusSizeStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + // Other functions + void process(o2::globaltracking::RecoContainer&); + void loadData(o2::globaltracking::RecoContainer&); + + // Helper functions + void prepareOutput(); + void setStyle(); + void updateTimeDependentParams(ProcessingContext& pc); + float getAverageClusterSize(o2::its::TrackITS*); + float calcV0HypoMass(const V0&, PID, PID); + void calcAPVars(const V0&, float*, float*); + void getClusterSizes(std::vector&, const gsl::span, gsl::span::iterator&, const o2::itsmft::TopologyDictionary*); + void saveHistograms(); + void plotHistograms(); + void fillEtaBin(float eta, float clusSize, int i); + + // Running options + bool mUseMC; + + // Data + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + std::vector mClusterSizes; + gsl::span mInputITSidxs; + std::vector mMCTracks; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + // Output plots + std::unique_ptr mDBGOut; + std::unique_ptr mOutputNtupleAll; + std::unique_ptr mOutputNtupleCut; + + std::unique_ptr mMCR{}; + std::unique_ptr mMCCosPA{}; + std::unique_ptr mMCPosACS{}; + std::unique_ptr mMCNegACS{}; + std::unique_ptr mMCDauDCA{}; + std::unique_ptr mMCPosPVDCA{}; + std::unique_ptr mMCNegPVDCA{}; + std::unique_ptr mMCV0PVDCA{}; + + std::unique_ptr mMCRisTg{}; + std::unique_ptr mMCRisBg{}; + std::unique_ptr mMCCosPAisTg{}; + std::unique_ptr mMCCosPAisBg{}; + std::unique_ptr mMCPosACSisTg{}; + std::unique_ptr mMCPosACSisBg{}; + std::unique_ptr mMCNegACSisTg{}; + std::unique_ptr mMCNegACSisBg{}; + std::unique_ptr mMCDauDCAisTg{}; + std::unique_ptr mMCDauDCAisBg{}; + std::unique_ptr mMCPosPVDCAisTg{}; + std::unique_ptr mMCPosPVDCAisBg{}; + std::unique_ptr mMCNegPVDCAisTg{}; + std::unique_ptr mMCNegPVDCAisBg{}; + std::unique_ptr mMCV0PVDCAisTg{}; + std::unique_ptr mMCV0PVDCAisBg{}; + + std::unique_ptr mMCArmPodolisTg{}; + std::unique_ptr mMCArmPodolisBg{}; + + std::unique_ptr mAvgClusSizeCEta{}; + std::vector> mAvgClusSizeCEtaVec{}; + + std::vector mEtaBinUL; // upper edges for eta bins + + // Counters for target V0 identification + int nNotValid = 0; + int nNullptrs = 0; + int nMotherIDMismatch = 0; + int nEvIDMismatch = 0; + int nTargetV0 = 0; + int nNotTargetV0 = 0; + int nV0OutOfEtaRange = 0; + + std::string mOutName; + std::shared_ptr mKineReader; +}; + +void AvgClusSizeStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + LOGP(info, "Starting average cluster size study..."); + + prepareOutput(); + + if (mUseMC) { // for counting the missed K0shorts + mKineReader = std::make_unique("collisioncontext.root"); + for (int iEvent{0}; iEvent < mKineReader->getNEvents(0); iEvent++) { + auto mctrk = mKineReader->getTracks(0, iEvent); + mMCTracks.insert(mMCTracks.end(), mctrk.begin(), mctrk.end()); + } + } + + LOGP(info, "Cluster size study initialized."); +} + +void AvgClusSizeStudy::prepareOutput() +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + mOutName = params.outFileName; + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); + mOutputNtupleAll = std::make_unique("v0_data", "v0 data", "dPosACS:dNegACS:cosPA:V0R:eta:dauDCA:dPospvDCA:dNegpvDCA:v0pvDCA:alpha:pT:K0mass:lambdaMass:antilambdaMass:v0PDGcode"); + mOutputNtupleCut = std::make_unique("cut_v0_data", "v0 data (cut)", "dPosACS:dNegACS:cosPA:V0R:eta:dauDCA:dPospvDCA:dNegpvDCA:v0pvDCA:alpha:pT:K0mass:lambdaMass:antilambdaMass:v0PDGcode"); + + mMCR = std::make_unique("R", "V0 decay length R;R (cm?)"); + mMCCosPA = std::make_unique("cosPA", "cos(#theta_{p})"); + mMCPosACS = std::make_unique("acsPos", "Average cluster size per track;pixels / cluster / track"); + mMCNegACS = std::make_unique("acsNeg", "Average cluster size per track;pixels / cluster / track"); + mMCDauDCA = std::make_unique("dauDCA", "Prong-prong DCA;cm?"); + mMCPosPVDCA = std::make_unique("posPVDCA", "Positive prong-primary vertex DCA;cm?"); + mMCNegPVDCA = std::make_unique("negPVDCA", "Negative prong-primary vertex DCA;cm?"); + mMCV0PVDCA = std::make_unique("v0PVDCA", "V0 reconstructed track-primary vertex DCA;cm?"); + + mMCRisTg = std::make_unique("mcRTg", "target V0", 40, 0, 2); + mMCRisBg = std::make_unique("mcRBg", "background", 100, 0, 2); + mMCCosPAisTg = std::make_unique("mcCosPATg", "target V0", 40, 0, 1); + mMCCosPAisBg = std::make_unique("mcCosPABg", "background", 100, 0, 1); + mMCPosACSisTg = std::make_unique("mcPosACSTg", "target V0", 60, 0, 30); + mMCPosACSisBg = std::make_unique("mcPosACSBg", "background", 150, 0, 30); + mMCNegACSisTg = std::make_unique("mcNegACSTg", "target V0", 60, 0, 30); + mMCNegACSisBg = std::make_unique("mcNegACSBg", "background", 150, 0, 30); + mMCDauDCAisTg = std::make_unique("mcDauDCATg", "target V0", 40, 0, 2); + mMCDauDCAisBg = std::make_unique("mcDauDCABg", "background", 100, 0, 2); + mMCPosPVDCAisTg = std::make_unique("mcPosPVDCATg", "target V0", 40, 0, 15); + mMCPosPVDCAisBg = std::make_unique("mcPosPVDCABg", "background", 100, 0, 15); + mMCNegPVDCAisTg = std::make_unique("mcNegPVDCATg", "target V0", 40, 0, 15); + mMCNegPVDCAisBg = std::make_unique("mcNegPVDCABg", "background", 100, 0, 15); + mMCV0PVDCAisTg = std::make_unique("mcV0PVDCATg", "target V0", 40, 0, 15); + mMCV0PVDCAisBg = std::make_unique("mcV0PVDCABg", "background", 100, 0, 15); + + mMCArmPodolisTg = std::make_unique("apPlotTg", "Armenteros-Podolanski;#alpha_{Arm};p_{T}^{Arm} (GeV/c)", 150, -2, 2, 150, 0, 0.5); + mMCArmPodolisBg = std::make_unique("apPlotBg", "Armenteros-Podolanski;#alpha_{Arm};p_{T}^{Arm} (GeV/c)", 150, -2, 2, 150, 0, 0.5); + + mAvgClusSizeCEta = std::make_unique("avgclussizeeta", "Average cluster size per track;pixels / cluster / track"); // auto-set axis ranges + float binWidth = (params.etaMax - params.etaMin) / (float)params.etaNBins; + mEtaBinUL.reserve(params.etaNBins); + for (int i = 0; i < params.etaNBins; i++) { + mEtaBinUL.emplace_back(params.etaMin + (binWidth * (i + 1))); + mAvgClusSizeCEtaVec.push_back(std::make_unique(Form("avgclussize%i", i), Form("%.2f < #eta < %.2f", mEtaBinUL[i] - binWidth, mEtaBinUL[i]), params.sizeNBins, 0, params.sizeMax)); + mAvgClusSizeCEtaVec[i]->SetDirectory(nullptr); + mAvgClusSizeCEta->Add(mAvgClusSizeCEtaVec[i].get()); + } + + mMCRisTg->SetDirectory(nullptr); + mMCRisBg->SetDirectory(nullptr); + mMCCosPAisTg->SetDirectory(nullptr); + mMCCosPAisBg->SetDirectory(nullptr); + mMCDauDCAisTg->SetDirectory(nullptr); + mMCDauDCAisBg->SetDirectory(nullptr); + mMCPosACSisTg->SetDirectory(nullptr); + mMCPosACSisBg->SetDirectory(nullptr); + mMCNegACSisTg->SetDirectory(nullptr); + mMCNegACSisBg->SetDirectory(nullptr); + mMCPosPVDCAisTg->SetDirectory(nullptr); + mMCPosPVDCAisBg->SetDirectory(nullptr); + mMCNegPVDCAisTg->SetDirectory(nullptr); + mMCNegPVDCAisBg->SetDirectory(nullptr); + mMCV0PVDCAisTg->SetDirectory(nullptr); + mMCV0PVDCAisBg->SetDirectory(nullptr); + + mMCArmPodolisTg->SetDirectory(nullptr); + mMCArmPodolisBg->SetDirectory(nullptr); + + mOutputNtupleAll->SetDirectory(nullptr); + mOutputNtupleCut->SetDirectory(nullptr); + + mMCR->Add(mMCRisTg.get()); + mMCR->Add(mMCRisBg.get()); + mMCCosPA->Add(mMCCosPAisTg.get()); + mMCCosPA->Add(mMCCosPAisBg.get()); + mMCPosACS->Add(mMCPosACSisTg.get()); + mMCPosACS->Add(mMCPosACSisBg.get()); + mMCNegACS->Add(mMCNegACSisTg.get()); + mMCNegACS->Add(mMCNegACSisBg.get()); + mMCDauDCA->Add(mMCDauDCAisTg.get()); + mMCDauDCA->Add(mMCDauDCAisBg.get()); + mMCPosPVDCA->Add(mMCPosPVDCAisTg.get()); + mMCPosPVDCA->Add(mMCPosPVDCAisBg.get()); + mMCNegPVDCA->Add(mMCNegPVDCAisTg.get()); + mMCNegPVDCA->Add(mMCNegPVDCAisBg.get()); + mMCV0PVDCA->Add(mMCV0PVDCAisTg.get()); + mMCV0PVDCA->Add(mMCV0PVDCAisBg.get()); +} + +void AvgClusSizeStudy::setStyle() +{ + gStyle->SetPalette(kRainBow); + std::vector colors = {1, 2, 3, 4, 6, 7, 41, 47}; + std::vector markers = {2, 3, 4, 5, 25, 26, 27, 28, 32}; + for (int i = 0; i < mAvgClusSizeCEtaVec.size(); i++) { + mAvgClusSizeCEtaVec[i]->SetMarkerStyle(markers[i]); + mAvgClusSizeCEtaVec[i]->SetMarkerColor(colors[i]); + mAvgClusSizeCEtaVec[i]->SetLineColor(colors[i]); + } + + mMCRisTg->SetLineColor(kRed); + mMCCosPAisTg->SetLineColor(kRed); + mMCPosACSisTg->SetLineColor(kRed); + mMCNegACSisTg->SetLineColor(kRed); + mMCDauDCAisTg->SetLineColor(kRed); + mMCPosPVDCAisTg->SetLineColor(kRed); + mMCNegPVDCAisTg->SetLineColor(kRed); + mMCV0PVDCAisTg->SetLineColor(kRed); +} + +void AvgClusSizeStudy::run(ProcessingContext& pc) +{ + // auto geom = o2::its::GeometryTGeo::Instance(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void AvgClusSizeStudy::getClusterSizes(std::vector& clusSizeVec, const gsl::span ITSclus, gsl::span::iterator& pattIt, const o2::itsmft::TopologyDictionary* mdict) +{ + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + auto pattID = clus.getPatternID(); + int npix; + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + patt.acquirePattern(pattIt); + npix = patt.getNPixels(); + } else { + npix = mdict->getNpixels(pattID); + patt = mdict->getPattern(pattID); + } + clusSizeVec[iClus] = npix; + } +} + +void AvgClusSizeStudy::loadData(o2::globaltracking::RecoContainer& recoData) +{ + mInputITSidxs = recoData.getITSTracksClusterRefs(); + auto compClus = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + mClusterSizes.resize(compClus.size()); + auto pattIt = clusPatt.begin(); + getClusterSizes(mClusterSizes, compClus, pattIt, mDict); +} + +void AvgClusSizeStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + float dPosACS, dNegACS, dauDCA, cosPA, v0R, eta, dPospvDCA, dNegpvDCA, v0pvDCA, tgV0HypoMass, bgV0HypoMass, alphaArm, pT; // ACS=average cluster size + bool isMCTarget = false; + int targetPDGCode = 310; + TrackITS dPosRecoTrk, dNegRecoTrk; // daughter ITS tracks + DCA dPosDCA, dNegDCA, v0DCA; // DCA object for prong to primary vertex (DCA = o2::dataformats::DCA) + PVertex pv; + + PID targetV0 = PID("K0"); + PID backgroundV0 = PID("Lambda"); + PID tgPos, tgNeg, bgPos, bgNeg; + if (params.targetV0 == "K0") { + bgPos = PID("Proton"); + LOGP(info, "V0 target set to K0-short."); + } else if (params.targetV0 == "Lambda") { + targetV0 = PID("Lambda"); + tgPos = PID("Proton"); + backgroundV0 = PID("K0"); + targetPDGCode = 3122; + LOGP(info, "V0 target set to Lambda."); + } else { + LOGP(warning, "Given V0 target not recognized, defaulting to K0-short."); + bgPos = PID("Proton"); + } + + // Variables for MC analysis + gsl::span mcLabels; + o2::MCCompLabel dPosLab, dNegLab; + const o2::MCTrack *V0mcTrk, *dPosMCTrk, *dNegMCTrk; + int V0PdgCode, mPosTrkId, mNegTrkId; + + loadData(recoData); + auto V0s = recoData.getV0s(); + auto V0sIdx = recoData.getV0sIdx(); + size_t nV0s = V0sIdx.size(); + if (nV0s && nV0s != V0s.size()) { + LOGP(fatal, "This data has not secondary vertices kinematics filled"); + } + + LOGP(info, "Found {} reconstructed V0s.", nV0s); + LOGP(info, "Found {} ITS tracks.", recoData.getITSTracks().size()); + LOGP(info, "Found {} ROFs.", recoData.getITSTracksROFRecords().size()); + if (mUseMC) { + mcLabels = recoData.getITSTracksMCLabels(); + LOGP(info, "Found {} labels.", mcLabels.size()); + } + + for (size_t iv = 0; iv < nV0s; iv++) { + auto v0 = V0s[iv]; + const auto& v0Idx = V0sIdx[iv]; + dPosRecoTrk = recoData.getITSTrack(v0Idx.getProngID(0)); // cannot use v0.getProng() since it returns TrackParCov not TrackITS + dNegRecoTrk = recoData.getITSTrack(v0Idx.getProngID(1)); + + pv = recoData.getPrimaryVertex(v0Idx.getVertexID()); // extract primary vertex + dPosRecoTrk.propagateToDCA(pv, params.b, &dPosDCA); // calculate and store DCA objects for both prongs + dNegRecoTrk.propagateToDCA(pv, params.b, &dNegDCA); + v0.propagateToDCA(pv, params.b, &v0DCA); + + dPospvDCA = std::sqrt(dPosDCA.getR2()); // extract DCA distance from DCA object + dNegpvDCA = std::sqrt(dNegDCA.getR2()); + v0pvDCA = std::sqrt(v0DCA.getR2()); + + eta = v0.getEta(); + dauDCA = v0.getDCA(); + cosPA = v0.getCosPA(); + v0R = std::sqrt(v0.calcR2()); // gives distance from pvertex to origin? in centimeters (?) NOTE: unsure if this is to the primary vertex or to origin + + dPosACS = getAverageClusterSize(&dPosRecoTrk); + dNegACS = getAverageClusterSize(&dNegRecoTrk); + + tgV0HypoMass = calcV0HypoMass(v0, tgPos, tgNeg); + bgV0HypoMass = calcV0HypoMass(v0, bgPos, bgNeg); + calcAPVars(v0, &alphaArm, &pT); + + if (mUseMC) { // check whether queried V0 is the target V0 in MC, and fill the cut validation plots + V0PdgCode = 0; + isMCTarget = false; + dPosLab = mcLabels[v0Idx.getProngID(0)]; // extract MC labels for the prongs + dNegLab = mcLabels[v0Idx.getProngID(1)]; + if (!dPosLab.isValid() || !dNegLab.isValid()) { + LOGP(debug, "Daughter MCCompLabel not valid: {}(+) and {}(-). Skipping.", dPosLab.isValid(), dNegLab.isValid()); + nNotValid++; + } else { + dPosMCTrk = mKineReader->getTrack(dPosLab); + dNegMCTrk = mKineReader->getTrack(dNegLab); + if (dPosMCTrk == nullptr || dNegMCTrk == nullptr) { + LOGP(debug, "Nullptr found: {}(+) and {}(-). Skipping.", (void*)dPosMCTrk, (void*)dNegMCTrk); + nNullptrs++; + } else { + mPosTrkId = dPosMCTrk->getMotherTrackId(); + mNegTrkId = dNegMCTrk->getMotherTrackId(); + LOGP(debug, "Daughter PDG codes: {}(+) and {}(-)", dPosMCTrk->GetPdgCode(), dNegMCTrk->GetPdgCode()); + if (mPosTrkId != mNegTrkId || mPosTrkId == -1 || mNegTrkId == -1) { + LOGP(debug, "Mother track ID mismatch or default -1: {}(+) and {}(-). Skipping.", mPosTrkId, mNegTrkId); + nMotherIDMismatch++; + } else { + if (dNegLab.getEventID() != dPosLab.getEventID()) { + LOGP(debug, "Daughter EvID mismatch: {}(+) and {}(-). Skipping.", dPosLab.getEventID(), dNegLab.getEventID()); + nEvIDMismatch++; + } else { + V0mcTrk = mKineReader->getTrack(dNegLab.getEventID(), mPosTrkId); // assume daughter MCTracks are in same event as V0 MCTrack + V0PdgCode = V0mcTrk->GetPdgCode(); + if (V0PdgCode == targetPDGCode) { + isMCTarget = true; + nTargetV0++; + } else { + nNotTargetV0++; + } + } + } + } + } + if (isMCTarget) { + mMCCosPAisTg->Fill(cosPA); + mMCDauDCAisTg->Fill(dauDCA); + mMCRisTg->Fill(v0R); + mMCPosPVDCAisTg->Fill(dPospvDCA); + mMCNegPVDCAisTg->Fill(dNegpvDCA); + mMCPosACSisTg->Fill(dPosACS); + mMCNegACSisTg->Fill(dNegACS); + mMCV0PVDCAisTg->Fill(v0pvDCA); + mMCArmPodolisTg->Fill(alphaArm, pT); + } else { + mMCCosPAisBg->Fill(cosPA); + mMCDauDCAisBg->Fill(dauDCA); + mMCRisBg->Fill(v0R); + mMCPosPVDCAisBg->Fill(dPospvDCA); + mMCNegPVDCAisBg->Fill(dNegpvDCA); + mMCPosACSisBg->Fill(dPosACS); + mMCNegACSisBg->Fill(dNegACS); + mMCV0PVDCAisBg->Fill(v0pvDCA); + mMCArmPodolisBg->Fill(alphaArm, pT); + } + } + + mOutputNtupleAll->Fill(dPosACS, dNegACS, cosPA, v0R, eta, dauDCA, dPospvDCA, dNegpvDCA, v0pvDCA, alphaArm, pT, calcV0HypoMass(v0, PID::Pion, PID::Pion), calcV0HypoMass(v0, PID::Proton, PID::Pion), calcV0HypoMass(v0, PID::Pion, PID::Proton), (float)V0PdgCode); + if ((cosPA > params.cosPAmin || params.disableCosPA) && (v0R < params.Rmax || params.disableRmax) && (v0R > params.Rmin || params.disableRmin) && (dauDCA < params.prongDCAmax || params.disableProngDCAmax) && (dPospvDCA > params.dauPVDCAmin || params.disableDauPVDCAmin) && (dNegpvDCA > params.dauPVDCAmin || params.disableDauPVDCAmin) && (v0pvDCA < params.v0PVDCAmax || params.disableV0PVDCAmax) && (abs(bgV0HypoMass - backgroundV0.getMass()) > params.bgV0window || params.disableMassHypoth) && (abs(tgV0HypoMass - targetV0.getMass()) < params.tgV0window || params.disableMassHypoth)) { + mOutputNtupleCut->Fill(dPosACS, dNegACS, cosPA, v0R, eta, dauDCA, dPospvDCA, dNegpvDCA, v0pvDCA, alphaArm, pT, calcV0HypoMass(v0, PID::Pion, PID::Pion), calcV0HypoMass(v0, PID::Proton, PID::Pion), calcV0HypoMass(v0, PID::Pion, PID::Proton), (float)V0PdgCode); + if (eta > params.etaMin && eta < params.etaMax) { + fillEtaBin(eta, dPosACS, 0); + fillEtaBin(eta, dNegACS, 0); + } else { + nV0OutOfEtaRange++; + } + } + } + + if (mUseMC) { + LOGP(info, "MONTE CARLO OVERALL STATISTICS: {} total V0s, {} nonvalid daughter labels, {} nullptrs, {} motherID mismatches, {} evID mismatches, {} matching target, {} not matching target", V0s.size(), nNotValid, nNullptrs, nMotherIDMismatch, nEvIDMismatch, nTargetV0, nNotTargetV0); + int nPrimaryTargetV0 = 0; + for (auto& mcTrk : mMCTracks) { // search through all MC tracks to find primary target V0s, whether reconstructed or not + if (mcTrk.GetPdgCode() == targetPDGCode && mcTrk.isPrimary()) { + nPrimaryTargetV0++; + } + } + LOGP(info, "MONTE CARLO OVERALL STATISTICS: {} MC target V0s (isPrimary) found in MC tracks out of {} total MC tracks", nPrimaryTargetV0, mMCTracks.size()); + } + LOGP(info, "{} V0s out of eta range ({}, {})", nV0OutOfEtaRange, params.etaMin, params.etaMax); +} + +float AvgClusSizeStudy::getAverageClusterSize(TrackITS* daughter) +{ + int totalSize{0}; + auto firstClus = daughter->getFirstClusterEntry(); + auto ncl = daughter->getNumberOfClusters(); + for (int icl = 0; icl < ncl; icl++) { + totalSize += mClusterSizes[mInputITSidxs[firstClus + icl]]; + } + return (float)totalSize / (float)ncl; +} + +float AvgClusSizeStudy::calcV0HypoMass(const V0& v0, PID hypothPIDPos, PID hypothPIDNeg) +{ + // Mass hypothesis calculation; taken from o2::strangeness_tracking::StrangenessTracker::calcMotherMass() + std::array pPos, pNeg, pV0; + v0.getProng(0).getPxPyPzGlo(pPos); + v0.getProng(1).getPxPyPzGlo(pNeg); + v0.getPxPyPzGlo(pV0); + double m2Pos = PID::getMass2(hypothPIDPos); + double m2Neg = PID::getMass2(hypothPIDNeg); + double p2Pos = (pPos[0] * pPos[0]) + (pPos[1] * pPos[1]) + (pPos[2] * pPos[2]); + double p2Neg = (pNeg[0] * pNeg[0]) + (pNeg[1] * pNeg[1]) + (pNeg[2] * pNeg[2]); + double ePos = std::sqrt(p2Pos + m2Pos), eNeg = std::sqrt(p2Neg + m2Neg); + double e2V0 = (ePos + eNeg) * (ePos + eNeg); + double pxV0 = (pPos[0] + pNeg[0]); + double pyV0 = (pPos[1] + pNeg[1]); + double pzV0 = (pPos[2] + pNeg[2]); + double p2V0 = (pxV0 * pxV0) + (pyV0 * pyV0) + (pzV0 * pzV0); + return (float)std::sqrt(e2V0 - p2V0); +} + +void AvgClusSizeStudy::calcAPVars(const V0& v0, float* alphaArm, float* pT) +{ + // Calculation of the Armenteros-Podolanski variables + std::array pV0, pPos, pNeg; + v0.getProng(0).getPxPyPzGlo(pPos); + v0.getProng(1).getPxPyPzGlo(pNeg); + v0.getPxPyPzGlo(pV0); + double p2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1] + pV0[2] * pV0[2]; + double qNeg = pNeg[0] * pV0[0] + pNeg[1] * pV0[1] + pNeg[2] * pV0[2]; + double qPos = pPos[0] * pV0[0] + pPos[1] * pV0[1] + pPos[2] * pV0[2]; + *alphaArm = (float)(qPos - qNeg) / (qPos + qNeg); + double p2Pos = pPos[0] * pPos[0] + pPos[1] * pPos[1] + pPos[2] * pPos[2]; + *pT = (float)std::sqrt(p2Pos - ((qPos * qPos) / p2V0)); +}; + +void AvgClusSizeStudy::fillEtaBin(float eta, float clusSize, int i) +{ + if (eta < mEtaBinUL[i]) { + mAvgClusSizeCEtaVec[i]->Fill(clusSize); + } else { + fillEtaBin(eta, clusSize, i + 1); + } +} + +void AvgClusSizeStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void AvgClusSizeStudy::saveHistograms() +{ + mDBGOut.reset(); + TFile fout(mOutName.c_str(), "RECREATE"); + + fout.WriteTObject(mOutputNtupleAll.get()); + fout.WriteTObject(mOutputNtupleCut.get()); + fout.WriteTObject(mMCR.get()); + fout.WriteTObject(mMCCosPA.get()); + fout.WriteTObject(mMCPosACS.get()); + fout.WriteTObject(mMCNegACS.get()); + fout.WriteTObject(mMCDauDCA.get()); + fout.WriteTObject(mMCPosPVDCA.get()); + fout.WriteTObject(mMCNegPVDCA.get()); + fout.WriteTObject(mMCV0PVDCA.get()); + fout.WriteTObject(mMCArmPodolisTg.get()); + fout.WriteTObject(mMCArmPodolisBg.get()); + + fout.WriteTObject(mAvgClusSizeCEta.get()); + fout.Close(); + + LOGP(info, "Stored histograms into {}", mOutName.c_str()); +} + +void AvgClusSizeStudy::plotHistograms() +{ + if (mUseMC) { + TCanvas* cMCR = new TCanvas(); + mMCR->Draw("nostack"); + cMCR->BuildLegend(); + cMCR->Print("mcR.png"); + TCanvas* cMCCosPA = new TCanvas(); + mMCCosPA->Draw("nostack"); + cMCCosPA->BuildLegend(); + cMCCosPA->Print("mcCosPA.png"); + TCanvas* cMCPosACS = new TCanvas(); + mMCPosACS->Draw("nostack"); + cMCPosACS->BuildLegend(); + cMCPosACS->Print("mcPosACS.png"); + TCanvas* cMCNegACS = new TCanvas(); + mMCNegACS->Draw("nostack"); + cMCNegACS->BuildLegend(); + cMCNegACS->Print("mcNegACS.png"); + TCanvas* cMCDauDCA = new TCanvas(); + mMCDauDCA->Draw("nostack"); + cMCDauDCA->BuildLegend(); + cMCDauDCA->Print("mcDauDCA.png"); + TCanvas* cMCPosPVDCA = new TCanvas(); + mMCPosPVDCA->Draw("nostack"); + cMCPosPVDCA->BuildLegend(); + cMCPosPVDCA->Print("mcPosPVDCA.png"); + TCanvas* cMCNegPVDCA = new TCanvas(); + mMCNegPVDCA->Draw("nostack"); + cMCNegPVDCA->BuildLegend(); + cMCNegPVDCA->Print("mcNegPVDCA.png"); + TCanvas* cMCV0PVDCA = new TCanvas(); + mMCV0PVDCA->Draw("nostack"); + cMCV0PVDCA->BuildLegend(); + cMCV0PVDCA->Print("mcV0PVDCA.png"); + TCanvas* cMCArmPodolTg = new TCanvas(); + mMCArmPodolisTg->Draw("COLZ"); + cMCArmPodolTg->Print("mcArmPodolTg.png"); + TCanvas* cMCArmPodolBg = new TCanvas(); + mMCArmPodolisBg->Draw("COLZ"); + cMCArmPodolBg->Print("mcArmPodolBg.png"); + } + + TCanvas* c10 = new TCanvas(); + mAvgClusSizeCEta->Draw("P NOSTACK"); + c10->BuildLegend(0.6, 0.6, 0.8, 0.8); + c10->Print("clusSizeEta.png"); +} + +void AvgClusSizeStudy::endOfStream(EndOfStreamContext& ec) +{ + auto& params = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); + setStyle(); + saveHistograms(); + if (params.generatePlots) { + plotHistograms(); + } +} + +void AvgClusSizeStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + // o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->requestPrimaryVertices(useMC); // NOTE: may be necessary to use requestPrimaryVerticesTMP()... + // dataRequest->requestPrimaryVerticesTMP(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-study-AvgClusSize", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, kineReader)}, + Options{}}; +} +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx new file mode 100644 index 0000000000000..494603641cde5 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Efficiency.cxx @@ -0,0 +1,2863 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include "TGeoGlobalMagField.h" +#include "ITSStudies/Efficiency.h" +#include "ITSStudies/ITSStudiesConfigParam.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITStracking/IOUtils.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/TrackParametrization.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NLAYERS 3 + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; + +class EfficiencyStudy : public Task +{ + public: + EfficiencyStudy(std::shared_ptr dr, + mask_t src, + bool useMC, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mUseMC(useMC), mKineReader(kineReader), mGGCCDBRequest(gr){}; + + ~EfficiencyStudy() final = default; + void init(InitContext&) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void initialiseRun(o2::globaltracking::RecoContainer&); + void stileEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, bool bidimensional, const int markerStyle, const double markersize, const int markercolor, const int linercolor); + int getDCAClusterTrackMC(int countDuplicated); + void studyDCAcutsMC(); + void studyClusterSelectionMC(); + void countDuplicatedAfterCuts(); + void getEfficiency(bool isMC); + void process(o2::globaltracking::RecoContainer&); + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool mVerboseOutput = false; + bool mUseMC; + std::string mOutFileName; + double b; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + float mrangesPt[NLAYERS][2] = {{0., 0.5}, {0.5, 2.}, {2., 7.5}}; + + // Spans + gsl::span mTracksROFRecords; + gsl::span mClustersROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mClusPatterns; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont; + std::vector> mITSClustersArray; + + // Data + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + + // Utils + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mOutFile; + int mDuplicated_layer[NLAYERS] = {0}; + + //// Histos + + // DCA betweeen track and original cluster + std::unique_ptr mDCAxyOriginal[NLAYERS]; + std::unique_ptr mDCAzOriginal[NLAYERS]; + // DCA betweeen track and duplicated cluster + std::unique_ptr mDCAxyDuplicated; + std::unique_ptr mDCAzDuplicated; + + // DCA betweeen track and duplicated cluster per layer + std::unique_ptr mDCAxyDuplicated_layer[NLAYERS]; + std::unique_ptr mDCAzDuplicated_layer[NLAYERS]; + + // phi, eta, pt of the cluster + std::unique_ptr mPhiOriginal[NLAYERS]; + std::unique_ptr mEtaOriginal[NLAYERS]; + std::unique_ptr mPtOriginal[NLAYERS]; + std::unique_ptr mPtDuplicated[NLAYERS]; + std::unique_ptr mEtaDuplicated[NLAYERS]; + std::unique_ptr mPhiDuplicated[NLAYERS]; + std::unique_ptr mPhiOriginalIfDuplicated[NLAYERS]; + + std::unique_ptr mZvsPhiDUplicated[NLAYERS]; + + // position of the clusters + std::unique_ptr m3DClusterPositions; + std::unique_ptr m3DDuplicatedClusterPositions; + std::unique_ptr m2DClusterOriginalPositions; + std::unique_ptr m2DClusterDuplicatedPositions; + + // Efficiency histos + std::unique_ptr mEfficiencyGoodMatch; + std::unique_ptr mEfficiencyFakeMatch; + std::unique_ptr mEfficiencyTotal; + std::unique_ptr mEfficiencyGoodMatch_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatch_layer[NLAYERS]; + std::unique_ptr mEfficiencyTotal_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPt_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchPt_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchEta_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchEta_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPhi_layer[NLAYERS]; + std::unique_ptr mEfficiencyGoodMatchPhiOriginal_layer[NLAYERS]; + std::unique_ptr mEfficiencyFakeMatchPhi_layer[NLAYERS]; + + // std::unique_ptr mEfficiencyColEta[NLAYERS]; + std::unique_ptr mDenColEta[NLAYERS]; + std::unique_ptr mNumColEta[NLAYERS]; + std::unique_ptr mDenRowPhi[NLAYERS]; + std::unique_ptr mNumRowPhi[NLAYERS]; + std::unique_ptr mDenRowCol[NLAYERS]; + std::unique_ptr mNumRowCol[NLAYERS]; + + // phi, eta, pt of the duplicated cluster per layer + std::unique_ptr mPt_EtaDupl[NLAYERS]; + + // duplicated per layer and per cut + std::unique_ptr mDuplicatedEtaAllPt[NLAYERS]; + std::unique_ptr mDuplicatedEta[NLAYERS][3]; + std::unique_ptr mDuplicatedPhiAllPt[NLAYERS]; + std::unique_ptr mDuplicatedPhi[NLAYERS][3]; + std::unique_ptr mDuplicatedPt[NLAYERS]; + std::unique_ptr mDuplicatedRow[NLAYERS]; + std::unique_ptr mDuplicatedCol[NLAYERS]; + std::unique_ptr mDuplicatedZ[NLAYERS]; + std::unique_ptr mDuplicatedPtEta[NLAYERS]; + std::unique_ptr mDuplicatedPtPhi[NLAYERS]; + std::unique_ptr mDuplicatedEtaPhi[NLAYERS]; + + // matches per layer and per cut + std::unique_ptr mNGoodMatchesEtaAllPt[NLAYERS]; + std::unique_ptr mNGoodMatchesEta[NLAYERS][3]; + std::unique_ptr mNGoodMatchesPhiAllPt[NLAYERS]; + std::unique_ptr mNGoodMatchesPhi[NLAYERS][3]; + + std::unique_ptr mNFakeMatchesEtaAllPt[NLAYERS]; + std::unique_ptr mNFakeMatchesEta[NLAYERS][3]; + std::unique_ptr mNFakeMatchesPhiAllPt[NLAYERS]; + std::unique_ptr mNFakeMatchesPhi[NLAYERS][3]; + + std::unique_ptr mNGoodMatchesPt[NLAYERS]; + std::unique_ptr mNFakeMatchesPt[NLAYERS]; + + std::unique_ptr mNGoodMatchesRow[NLAYERS]; + std::unique_ptr mNFakeMatchesRow[NLAYERS]; + + std::unique_ptr mNGoodMatchesCol[NLAYERS]; + std::unique_ptr mNFakeMatchesCol[NLAYERS]; + + std::unique_ptr mNGoodMatchesZ[NLAYERS]; + std::unique_ptr mNFakeMatchesZ[NLAYERS]; + + std::unique_ptr mNGoodMatchesPtEta[NLAYERS]; + std::unique_ptr mNFakeMatchesPtEta[NLAYERS]; + + std::unique_ptr mNGoodMatchesPtPhi[NLAYERS]; + std::unique_ptr mNFakeMatchesPtPhi[NLAYERS]; + + std::unique_ptr mNGoodMatchesEtaPhi[NLAYERS]; + std::unique_ptr mNFakeMatchesEtaPhi[NLAYERS]; + + // calculating the efficiency with TEfficiency class + std::unique_ptr mEffPtGood[NLAYERS]; + std::unique_ptr mEffPtFake[NLAYERS]; + std::unique_ptr mEffRowGood[NLAYERS]; + std::unique_ptr mEffRowFake[NLAYERS]; + std::unique_ptr mEffColGood[NLAYERS]; + std::unique_ptr mEffColFake[NLAYERS]; + std::unique_ptr mEffZGood[NLAYERS]; + std::unique_ptr mEffZFake[NLAYERS]; + std::unique_ptr mEffPtEtaGood[NLAYERS]; + std::unique_ptr mEffPtEtaFake[NLAYERS]; + std::unique_ptr mEffPtPhiGood[NLAYERS]; + std::unique_ptr mEffPtPhiFake[NLAYERS]; + std::unique_ptr mEffEtaPhiGood[NLAYERS]; + std::unique_ptr mEffEtaPhiFake[NLAYERS]; + + std::unique_ptr mEffEtaGoodAllPt[NLAYERS]; + std::unique_ptr mEffEtaGood[NLAYERS][3]; + std::unique_ptr mEffEtaFakeAllPt[NLAYERS]; + std::unique_ptr mEffEtaFake[NLAYERS][3]; + + std::unique_ptr mEffPhiGoodAllPt[NLAYERS]; + std::unique_ptr mEffPhiGood[NLAYERS][3]; + std::unique_ptr mEffPhiFakeAllPt[NLAYERS]; + std::unique_ptr mEffPhiFake[NLAYERS][3]; + + std::unique_ptr mnGoodMatchesPt_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesPt_layer[NLAYERS]; + + std::unique_ptr mnGoodMatchesEta_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesEta_layer[NLAYERS]; + + std::unique_ptr mnGoodMatchesPhi_layer[NLAYERS]; + std::unique_ptr mnGoodMatchesPhiOriginal_layer[NLAYERS]; + std::unique_ptr mnFakeMatchesPhi_layer[NLAYERS]; + + std::unique_ptr DCAxyData[NLAYERS]; + std::unique_ptr DCAzData[NLAYERS]; + + std::unique_ptr DCAxyRejected[NLAYERS]; + std::unique_ptr DCAzRejected[NLAYERS]; + + std::unique_ptr denPt[NLAYERS]; + std::unique_ptr numPt[NLAYERS]; + std::unique_ptr numPtGood[NLAYERS]; + std::unique_ptr numPtFake[NLAYERS]; + + std::unique_ptr denPhi[NLAYERS]; + std::unique_ptr numPhi[NLAYERS]; + std::unique_ptr numPhiGood[NLAYERS]; + std::unique_ptr numPhiFake[NLAYERS]; + + std::unique_ptr denEta[NLAYERS]; + std::unique_ptr numEta[NLAYERS]; + std::unique_ptr numEtaGood[NLAYERS]; + std::unique_ptr numEtaFake[NLAYERS]; + + std::unique_ptr denRow[NLAYERS]; + std::unique_ptr numRow[NLAYERS]; + std::unique_ptr numRowGood[NLAYERS]; + std::unique_ptr numRowFake[NLAYERS]; + + std::unique_ptr denCol[NLAYERS]; + std::unique_ptr numCol[NLAYERS]; + std::unique_ptr numColGood[NLAYERS]; + std::unique_ptr numColFake[NLAYERS]; + std::unique_ptr denZ[NLAYERS]; + std::unique_ptr numZ[NLAYERS]; + std::unique_ptr numZGood[NLAYERS]; + std::unique_ptr numZFake[NLAYERS]; + + std::unique_ptr numLayers; + std::unique_ptr denLayers; + std::unique_ptr numGoodLayers; + std::unique_ptr numFakeLayers; + + int nDuplicatedClusters[NLAYERS] = {0}; + int nTracksSelected[NLAYERS] = {0}; // denominator fot the efficiency calculation + + std::unique_ptr IPOriginalxy[NLAYERS]; + std::unique_ptr IPOriginalz[NLAYERS]; + + std::unique_ptr chipRowDuplicated[NLAYERS]; + std::unique_ptr chipRowOriginalIfDuplicated[NLAYERS]; + + std::unique_ptr chi2trackAccepted; + + /// checking where the duplicated not found are (histograms filled with the orifinal cluster variables) + std::unique_ptr phiFound[NLAYERS]; + std::unique_ptr rowFound[NLAYERS]; + std::unique_ptr phiNotFound[NLAYERS]; + std::unique_ptr rowNotFound[NLAYERS]; + std::unique_ptr zFound[NLAYERS]; + std::unique_ptr zNotFound[NLAYERS]; + std::unique_ptr colFoundOriginalVsDuplicated[NLAYERS]; + std::unique_ptr colFoundOriginal[NLAYERS]; + std::unique_ptr colNotFound[NLAYERS]; + std::unique_ptr radiusFound[NLAYERS]; + std::unique_ptr radiusNotFound[NLAYERS]; + std::unique_ptr m2DClusterFoundPositions; + std::unique_ptr m2DClusterNotFoundPositions; + std::unique_ptr mChipNotFound; + std::unique_ptr mChipFound; + std::unique_ptr l0_00; + std::unique_ptr l1_15; + std::unique_ptr l2_19; + std::unique_ptr chipOrigVsOverlap; + std::unique_ptr chipmap; +}; + +void EfficiencyStudy::init(InitContext& ic) +{ + LOGP(info, "init"); + + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + + auto& pars = o2::its::study::ITSEfficiencyParamConfig::Instance(); + mOutFileName = pars.outFileName; + b = pars.b; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + mOutFile = std::make_unique(mOutFileName.c_str(), "recreate"); + + mDCAxyDuplicated = std::make_unique("dcaXYDuplicated", "Distance between track and duplicated cluster ;DCA xy (cm); ", 200, -0.01, 0.01); + mDCAzDuplicated = std::make_unique("dcaZDuplicated", "Distance between track and duplicated cluster ;DCA z (cm); ", 200, -0.01, 0.01); + + m3DClusterPositions = std::make_unique("3DClusterPositions", ";x (cm);y (cm);z (cm)", 200, -10, 10, 200, -10, 10, 400, -20, 20); + m3DDuplicatedClusterPositions = std::make_unique("3DDuplicatedClusterPositions", ";x (cm);y (cm);z (cm)", 200, -10, 10, 200, -10, 10, 500, -30, 30); + m2DClusterOriginalPositions = std::make_unique("m2DClusterOriginalPositions", ";x (cm);y (cm)", 400, -10, 10, 400, -6, 6); + m2DClusterDuplicatedPositions = std::make_unique("m2DClusterDuplicatedPositions", ";x (cm);y (cm)", 400, -10, 10, 400, -6, 6); + + mEfficiencyGoodMatch = std::make_unique("mEfficiencyGoodMatch", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyFakeMatch = std::make_unique("mEfficiencyFakeMatch", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyTotal = std::make_unique("mEfficiencyTotal", ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + + chi2trackAccepted = std::make_unique("chi2trackAccepted", "; $#chi^{2}", 500, 0, 100); + + m2DClusterFoundPositions = std::make_unique("m2DClusterFoundPositions", ";x (cm);y (cm)", 250, -5, 5, 250, -5, 5); + m2DClusterNotFoundPositions = std::make_unique("m2DClusterNotFoundPositions", ";x (cm);y (cm)", 250, -5, 5, 250, -5, 5); + mChipNotFound = std::make_unique("mChipNotFound", ";chipID", 432, 0, 432); + mChipFound = std::make_unique("mChipFound", ";chipID", 432, 0, 432); + l0_00 = std::make_unique("l0_00", ";col; row", 2304, -0.5, 9215.5, 128, -0.5, 511.5); + l1_15 = std::make_unique("l1_15", ";col; row", 2304, -0.5, 9215.5, 512, -0.5, 511.5); + l2_19 = std::make_unique("l2_19", ";col; row", 2304, -0.5, 9215.5, 512, -0.5, 511.5); + chipOrigVsOverlap = std::make_unique("chipOrigVsOverlap", ";chipID Overlap;chipID Original", 9, 0, 9, 9, 0, 9); + chipmap = std::make_unique("chipmap", ";Column;Row", 1024, 0, 1023, 512, -0.5, 511.5); + + numLayers = std::make_unique("numLayers", "numLayers; ; Efficiency", 3, -0.5, 2.5); + numGoodLayers = std::make_unique("numGoodLayers", "numGoodLayers; ; Efficiency", 3, -0.5, 2.5); + numFakeLayers = std::make_unique("numFakeLayers", "numFakeLayers; ; Efficiency", 3, -0.5, 2.5); + denLayers = std::make_unique("denLayers", "denLayers; ; Efficiency", 3, -0.5, 2.5); + + for (int i = 0; i < NLAYERS; i++) { + + chipRowDuplicated[i] = std::make_unique(Form("chipPosDuplicated_L%d", i), Form("L%d; row", i), 512, -0.5, 511.5); + chipRowOriginalIfDuplicated[i] = std::make_unique(Form("chipPosOriginalIfDuplicated%d", i), Form("L%d; row", i), 512, -0.5, 511.5); + + DCAxyData[i] = std::make_unique(Form("dcaXYData_L%d", i), "Distance between track and original cluster ;DCA xy (cm); ", 4000, -0.2, 0.2); + DCAzData[i] = std::make_unique(Form("dcaZData_L%d", i), "Distance between track and original cluster ;DCA z (cm); ", 4000, -0.2, 0.2); + DCAxyRejected[i] = std::make_unique(Form("DCAxyRejected%d", i), "Distance between track and original cluster (rejected) ;DCA xy (cm); ", 30000, -30, 30); + DCAzRejected[i] = std::make_unique(Form("DCAzRejected%d", i), "Distance between track and original cluster (rejected) ;DCA z (cm); ", 30000, -30, 30); + + mDCAxyOriginal[i] = std::make_unique(Form("dcaXYOriginal_L%d", i), "Distance between track and original cluster ;DCA xy (cm); ", 200, -0.01, 0.01); + mDCAzOriginal[i] = std::make_unique(Form("dcaZOriginal_L%d", i), "Distance between track and original cluster ;DCA z (cm); ", 200, -0.01, 0.01); + + mPhiOriginal[i] = std::make_unique(Form("phiOriginal_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mEtaOriginal[i] = std::make_unique(Form("etaOriginal_L%d", i), ";eta (rad); ", 100, -2, 2); + mPtOriginal[i] = std::make_unique(Form("ptOriginal_L%d", i), ";pt (GeV/c); ", 100, 0, 10); + + mZvsPhiDUplicated[i] = std::make_unique(Form("zvsphiDuplicated_L%d", i), ";z (cm);phi (rad)", 400, -20, 20, 90, -3.2, 3.2); + + mPtDuplicated[i] = std::make_unique(Form("ptDuplicated_L%d", i), ";pt (GeV/c); ", nbPt, 0, 7.5); // xbins); + mEtaDuplicated[i] = std::make_unique(Form("etaDuplicated_L%d", i), ";eta; ", 40, -2, 2); + mPhiDuplicated[i] = std::make_unique(Form("phiDuplicated_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mPhiOriginalIfDuplicated[i] = std::make_unique(Form("phiOriginalIfDuplicated_L%d", i), ";phi (rad); ", 90, -3.2, 3.2); + mDCAxyDuplicated_layer[i] = std::make_unique(Form("dcaXYDuplicated_layer_L%d", i), "Distance between track and duplicated cluster ;DCA xy (cm); ", 100, -0.01, 0.01); + mDCAzDuplicated_layer[i] = std::make_unique(Form("dcaZDuplicated_layer_L%d", i), "Distance between track and duplicated cluster ;DCA z (cm); ", 100, -0.01, 0.01); + + mEfficiencyGoodMatch_layer[i] = std::make_unique(Form("mEfficiencyGoodMatch_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyFakeMatch_layer[i] = std::make_unique(Form("mEfficiencyFakeMatch_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + mEfficiencyTotal_layer[i] = std::make_unique(Form("mEfficiencyTotal_layer_L%d", i), ";#sigma(DCA) cut;Efficiency;", 20, 0.5, 20.5); + + mEfficiencyGoodMatchPt_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPt_layer_L%d", i), ";#it{p}_{T} (GeV/c);#sigma(DCA) cut;Efficiency;", nbPt, 0, 7.5, /* xbins*/ 20, 0.5, 20.5); + mEfficiencyFakeMatchPt_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchPt_layer_L%d", i), ";#it{p}_{T} (GeV/c);#sigma(DCA) cut;Efficiency;", nbPt, 0, 7.5, /* xbins*/ 20, 0.5, 20.5); + + mEfficiencyGoodMatchEta_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchEta_layer_L%d", i), ";#eta;#sigma(DCA) cut;Efficiency;", 40, -2, 2, 20, 0.5, 20.5); + mEfficiencyFakeMatchEta_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchEta_layer_L%d", i), ";#eta;#sigma(DCA) cut;Efficiency;", 40, -2, 2, 20, 0.5, 20.5); + + mEfficiencyGoodMatchPhi_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPhi_layer_L%d", i), ";#phi;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + mEfficiencyGoodMatchPhiOriginal_layer[i] = std::make_unique(Form("mEfficiencyGoodMatchPhiOriginal_layer_L%d", i), ";#phi Original;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + mEfficiencyFakeMatchPhi_layer[i] = std::make_unique(Form("mEfficiencyFakeMatchPhi_layer_L%d", i), ";#phi;#sigma(DCA) cut;Efficiency;", 90, -3.2, 3.2, 20, 0.5, 20.5); + + mPt_EtaDupl[i] = std::make_unique(Form("mPt_EtaDupl_L%d", i), ";#it{p}_{T} (GeV/c);#eta; ", 100, 0, 10, 100, -2, 2); + + mDuplicatedPt[i] = std::make_unique(Form("mDuplicatedPt_log_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/); + mDuplicatedPt[i]->Sumw2(); + mNGoodMatchesPt[i] = std::make_unique(Form("mNGoodMatchesPt_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/); + mNGoodMatchesPt[i]->Sumw2(); + mNFakeMatchesPt[i] = std::make_unique(Form("mNFakeMatchesPt_L%d", i), Form("; #it{p}_{T} (GeV/c); Number of fake matches L%d", i), nbPt, 0, 7.5 /* xbins*/); + mNFakeMatchesPt[i]->Sumw2(); + + mDuplicatedRow[i] = std::make_unique(Form("mDuplicatedRow_L%d", i), Form("; Row; Number of duplciated clusters L%d", i), 128, -0.5, 511.5); + mDuplicatedRow[i]->Sumw2(); + mNGoodMatchesRow[i] = std::make_unique(Form("mNGoodMatchesRow_L%d", i), Form("; Row; Number of good matches L%d", i), 128, -0.5, 511.5); + mNGoodMatchesRow[i]->Sumw2(); + mNFakeMatchesRow[i] = std::make_unique(Form("mNFakeMatchesRow_L%d", i), Form(";Row; Number of fake matches L%d", i), 128, -0.5, 511.5); + mNFakeMatchesRow[i]->Sumw2(); + + mDuplicatedCol[i] = std::make_unique(Form("mDuplicatedCol_L%d", i), Form("; Col; Number of duplciated clusters L%d", i), 128, -0.5, 1023.5); + mDuplicatedCol[i]->Sumw2(); + mNGoodMatchesCol[i] = std::make_unique(Form("mNGoodMatchesCol_L%d", i), Form("; Col; Number of good matches L%d", i), 128, -0.5, 1023.5); + mNGoodMatchesCol[i]->Sumw2(); + mNFakeMatchesCol[i] = std::make_unique(Form("mNFakeMatchesCol_L%d", i), Form(";Col; Number of fake matches L%d", i), 128, -0.5, 1023.5); + mNFakeMatchesCol[i]->Sumw2(); + + mDuplicatedZ[i] = std::make_unique(Form("mDuplicatedZ_L%d", i), Form("; Z (cm); Number of duplciated clusters L%d", i), 100, -15, 15); + mDuplicatedZ[i]->Sumw2(); + mNGoodMatchesZ[i] = std::make_unique(Form("mNGoodMatchesZ_L%d", i), Form("; Z (cm); Number of good matches L%d", i), 100, -15, 15); + mNGoodMatchesZ[i]->Sumw2(); + mNFakeMatchesZ[i] = std::make_unique(Form("mNFakeMatchesZ_L%d", i), Form(";Z (cm); Number of fake matches L%d", i), 100, -15, 15); + mNFakeMatchesZ[i]->Sumw2(); + + mDuplicatedPtEta[i] = std::make_unique(Form("mDuplicatedPtEta_log_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mDuplicatedPtEta[i]->Sumw2(); + mNGoodMatchesPtEta[i] = std::make_unique(Form("mNGoodMatchesPtEta_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mNGoodMatchesPtEta[i]->Sumw2(); + mNFakeMatchesPtEta[i] = std::make_unique(Form("mNFakeMatchesPtEta_L%d", i), Form("; #it{p}_{T} (GeV/c);#eta; Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 40, -2, 2); + mNFakeMatchesPtEta[i]->Sumw2(); + + mDuplicatedPtPhi[i] = std::make_unique(Form("mDuplicatedPtPhi_log_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of duplciated clusters L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mDuplicatedPtPhi[i]->Sumw2(); + mNGoodMatchesPtPhi[i] = std::make_unique(Form("mNGoodMatchesPtPhi_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mNGoodMatchesPtPhi[i]->Sumw2(); + mNFakeMatchesPtPhi[i] = std::make_unique(Form("mNFakeMatchesPtPhi_L%d", i), Form("; #it{p}_{T} (GeV/c);#phi (rad); Number of good matches L%d", i), nbPt, 0, 7.5 /* xbins*/, 90, -3.2, 3.2); + mNFakeMatchesPtPhi[i]->Sumw2(); + + mDuplicatedEtaPhi[i] = std::make_unique(Form("mDuplicatedEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of duplciated clusters L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mDuplicatedEtaPhi[i]->Sumw2(); + mNGoodMatchesEtaPhi[i] = std::make_unique(Form("mNGoodMatchesEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of good matches L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mNGoodMatchesEtaPhi[i]->Sumw2(); + mNFakeMatchesEtaPhi[i] = std::make_unique(Form("mNFakeMatchesEtaPhi_L%d", i), Form("; #eta;#phi (rad); Number of good matches L%d", i), 40, -2, 2, 90, -3.2, 3.2); + mNFakeMatchesEtaPhi[i]->Sumw2(); + + mDuplicatedEtaAllPt[i] = std::make_unique(Form("mDuplicatedEtaAllPt_L%d", i), Form("; #eta; Number of duplicated clusters L%d", i), 40, -2, 2); + mNGoodMatchesEtaAllPt[i] = std::make_unique(Form("mNGoodMatchesEtaAllPt_L%d", i), Form("; #eta; Number of good matches L%d", i), 40, -2, 2); + mNFakeMatchesEtaAllPt[i] = std::make_unique(Form("mNFakeMatchesEtaAllPt_L%d", i), Form("; #eta; Number of fake matches L%d", i), 40, -2, 2); + + mDuplicatedPhiAllPt[i] = std::make_unique(Form("mDuplicatedPhiAllPt_L%d", i), Form("; #phi (rad); Number of duplicated clusters L%d", i), 90, -3.2, 3.2); + mNGoodMatchesPhiAllPt[i] = std::make_unique(Form("mNGoodMatchesPhiAllPt_L%d", i), Form("; #phi (rad); Number of good matches L%d", i), 90, -3.2, 3.2); + mNFakeMatchesPhiAllPt[i] = std::make_unique(Form("mNFakeMatchesPhiAllPt_L%d", i), Form("; #phi (rad); Number of fake matches L%d", i), 90, -3.2, 3.2); + + mnGoodMatchesPt_layer[i] = std::make_unique(Form("mnGoodMatchesPt_layer_L%d", i), ";pt; nGoodMatches", nbPt, 0, 7.5 /* xbins*/, 20, 0.5, 20.5); + mnFakeMatchesPt_layer[i] = std::make_unique(Form("mnFakeMatchesPt_layer_L%d", i), ";pt; nFakeMatches", nbPt, 0, 7.5 /* xbins*/, 20, 0.5, 20.5); + mnGoodMatchesEta_layer[i] = std::make_unique(Form("mnGoodMatchesEta_layer_L%d", i), ";#eta; nGoodMatches", 40, -2, 2, 20, 0.5, 20.5); + mnFakeMatchesEta_layer[i] = std::make_unique(Form("mnFakeMatchesEta_layer_L%d", i), ";#eta; nFakeMatches", 40, -2, 2, 20, 0.5, 20.5); + mnGoodMatchesPhi_layer[i] = std::make_unique(Form("mnGoodMatchesPhi_layer_L%d", i), ";#Phi; nGoodMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + mnGoodMatchesPhiOriginal_layer[i] = std::make_unique(Form("mnGoodMatchesPhiOriginal_layer_L%d", i), ";#Phi of the original Cluster; nGoodMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + mnFakeMatchesPhi_layer[i] = std::make_unique(Form("mnFakeMatchesPhi_layer_L%d", i), ";#Phi; nFakeMatches", 90, -3.2, 3.2, 20, 0.5, 20.5); + + denPt[i] = std::make_unique(Form("denPt_L%d", i), Form("denPt_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPt[i] = std::make_unique(Form("numPt_L%d", i), Form("numPt_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPtGood[i] = std::make_unique(Form("numPtGood_L%d", i), Form("numPtGood_L%d", i), nbPt, 0, 7.5 /* xbins*/); + numPtFake[i] = std::make_unique(Form("numPtFake_L%d", i), Form("numPtFake_L%d", i), nbPt, 0, 7.5 /* xbins*/); + + denPhi[i] = std::make_unique(Form("denPhi_L%d", i), Form("denPhi_L%d", i), 90, -3.2, 3.2); + numPhi[i] = std::make_unique(Form("numPhi_L%d", i), Form("numPhi_L%d", i), 90, -3.2, 3.2); + numPhiGood[i] = std::make_unique(Form("numPhiGood_L%d", i), Form("numPhiGood_L%d", i), 90, -3.2, 3.2); + numPhiFake[i] = std::make_unique(Form("numPhiFake_L%d", i), Form("numPhiFake_L%d", i), 90, -3.2, 3.2); + + denEta[i] = std::make_unique(Form("denEta_L%d", i), Form("denEta_L%d", i), 200, -2, 2); + numEta[i] = std::make_unique(Form("numEta_L%d", i), Form("numEta_L%d", i), 200, -2, 2); + numEtaGood[i] = std::make_unique(Form("numEtaGood_L%d", i), Form("numEtaGood_L%d", i), 200, -2, 2); + numEtaFake[i] = std::make_unique(Form("numEtaFake_L%d", i), Form("numEtaFake_L%d", i), 200, -2, 2); + + denRow[i] = std::make_unique(Form("denRow_L%d", i), Form("denRow_L%d", i), 128, -0.5, 511.5); + numRow[i] = std::make_unique(Form("numRow_L%d", i), Form("numRow_L%d", i), 128, -0.5, 511.5); + numRowGood[i] = std::make_unique(Form("numRowGood_L%d", i), Form("numRowGood_L%d", i), 128, -0.5, 511.5); + numRowFake[i] = std::make_unique(Form("numRowFake_L%d", i), Form("numRowFake_L%d", i), 128, -0.5, 511.5); + + denCol[i] = std::make_unique(Form("denCol_L%d", i), Form("denCol_L%d", i), 128, -0.5, 1023.5); + numCol[i] = std::make_unique(Form("numCol_L%d", i), Form("numCol_L%d", i), 128, -0.5, 1023.5); + numColGood[i] = std::make_unique(Form("numColGood_L%d", i), Form("numColGood_L%d", i), 128, -0.5, 1023.5); + numColFake[i] = std::make_unique(Form("numColFake_L%d", i), Form("numColFake_L%d", i), 128, -0.5, 1023.5); + + denZ[i] = std::make_unique(Form("denZ_L%d", i), Form("denZ_L%d", i), 100, -15, 15); + numZ[i] = std::make_unique(Form("numZ_L%d", i), Form("numZ_L%d", i), 100, -15, 15); + numZGood[i] = std::make_unique(Form("numZGood_L%d", i), Form("numZGood_L%d", i), 100, -15, 15); + numZFake[i] = std::make_unique(Form("numZFake_L%d", i), Form("numZFake_L%d", i), 100, -15, 15); + + mDenColEta[i] = std::make_unique(Form("mDenColEta_L%d", i), Form("mDenColEta_L%d", i), 128, -0.5, 1023.5, 50, -1, 1); + mNumColEta[i] = std::make_unique(Form("mNumColEta_L%d", i), Form("mNumColEta_L%d", i), 128, -0.5, 1023.5, 50, -1, 1); + + mDenRowPhi[i] = std::make_unique(Form("mDenRowPhi_L%d", i), Form("mDenRowPhi_L%d", i), 128, -0.5, 511.5, 90, -3.2, 3.2); + mNumRowPhi[i] = std::make_unique(Form("mNumRowPhi_L%d", i), Form("mNumRowPhi_L%d", i), 128, -0.5, 511.5, 90, -3.2, 3.2); + + mDenRowCol[i] = std::make_unique(Form("mDenRowCol_L%d", i), Form("mDenRowCol_L%d", i), 128, -0.5, 511.5, 128, -0.5, 1023.5); + mNumRowCol[i] = std::make_unique(Form("mNumRowCol_L%d", i), Form("mNumRowCol_L%d", i), 128, -0.5, 511.5, 128, -0.5, 1023.5); + + IPOriginalxy[i] = std::make_unique(Form("IPOriginalxy_L%d", i), Form("IPOriginalxy_L%d", i), 500, -0.002, 0.002); + IPOriginalz[i] = std::make_unique(Form("IPOriginalz_L%d", i), Form("IPOriginalz_L%d", i), 200, -10, 10); + + phiFound[i] = std::make_unique(Form("phiFound_L%d", i), Form("phiFound_L%d", i), 190, -3.2, 3.2); + rowFound[i] = std::make_unique(Form("rowFound_L%d", i), Form("rowFound_L%d", i), 128, -0.5, 511.5); + phiNotFound[i] = std::make_unique(Form("phiNotFound_L%d", i), Form("phiNotFound_L%d", i), 90, -3.2, 3.2); + rowNotFound[i] = std::make_unique(Form("rowNotFound_L%d", i), Form("rowNotFound_L%d", i), 128, -0.5, 511.5); + zFound[i] = std::make_unique(Form("zFound_L%d", i), Form("zFound_L%d", i), 100, -15, 15); + zNotFound[i] = std::make_unique(Form("zNotFound%d", i), Form("zNotFound%d", i), 100, -15, 15); + colFoundOriginalVsDuplicated[i] = std::make_unique(Form("colFoundOriginalVsDuplicated_L%d", i), Form("colFoundOriginalVsDuplicated_L%d; Col Original cluster; Col Overlap cluster", i), 9216, -0.5, 9215.5, 9216, -0.5, 9215.5); + colFoundOriginal[i] = std::make_unique(Form("colFoundOriginal_L%d", i), Form("colFoundOriginal_L%d; Col Original cluster;", i), 9216, -0.5, 9215.5); + colNotFound[i] = std::make_unique(Form("colNotFound_L%d", i), Form("colNotFound_L%d", i), 9216, -0.5, 9215.5); + radiusFound[i] = std::make_unique(Form("radiusFound_L%d", i), Form("radiusFound_L%d", i), 80, 0, 6); + radiusNotFound[i] = std::make_unique(Form("radiusNotFound_L%d", i), Form("radiusNotFound_L%d", i), 80, 0, 4); + + for (int j = 0; j < 3; j++) { + mDuplicatedEta[i][j] = std::make_unique(Form("mDuplicatedEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of duplicated clusters L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + mNGoodMatchesEta[i][j] = std::make_unique(Form("mNGoodMatchesEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of good matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + mNFakeMatchesEta[i][j] = std::make_unique(Form("mNFakeMatchesEta_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #eta; Number of fake matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 40, -2, 2); + + mDuplicatedPhi[i][j] = std::make_unique(Form("mDuplicatedPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of duplicated clusters L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + mNGoodMatchesPhi[i][j] = std::make_unique(Form("mNGoodMatchesPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of good matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + mNFakeMatchesPhi[i][j] = std::make_unique(Form("mNFakeMatchesPhi_L%d_pt%d", i, j), Form("%f < #it{p}_{T} < %f GeV/c; #phi; Number of fake matches L%d", mrangesPt[j][0], mrangesPt[j][1], i), 90, -3.2, 3.2); + } + } + gStyle->SetPalette(55); +} + +void EfficiencyStudy::run(ProcessingContext& pc) +{ + LOGP(info, "--------------- run"); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + initialiseRun(recoData); + process(recoData); +} + +void EfficiencyStudy::initialiseRun(o2::globaltracking::RecoContainer& recoData) +{ + LOGP(info, "--------------- initialiseRun"); + if (mUseMC) { + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + } + mITSClustersArray.clear(); + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mClusters = recoData.getITSClusters(); + mClustersROFRecords = recoData.getITSClustersROFRecords(); + mClusPatterns = recoData.getITSClustersPatterns(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + mITSClustersArray.reserve(mClusters.size()); + auto pattIt = mClusPatterns.begin(); + o2::its::ioutils::convertCompactClusters(mClusters, pattIt, mITSClustersArray, mDict); // clusters converted to 3D spacepoints +} + +void EfficiencyStudy::stileEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, bool bidimensional = false, const int markerStyle = kFullCircle, const double markersize = 1, const int markercolor = kBlack, const int linecolor = kBlack) +{ + eff->SetName(name); + eff->SetTitle(title); + if (!bidimensional) { + eff->SetMarkerStyle(markerStyle); + eff->SetMarkerSize(markersize); + eff->SetMarkerColor(markercolor); + eff->SetLineColor(linecolor); + } +} + +int EfficiencyStudy::getDCAClusterTrackMC(int countDuplicated = 0) +{ + // get the DCA between the clusters and the track from MC and fill histograms: distance between original and duplicated cluster, DCA, phi, clusters + // used to study the DCA cut to be applied + LOGP(info, "--------------- getDCAClusterTrackMC"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + auto bz = o2::base::Propagator::Instance()->getNominalBz(); + LOG(info) << ">>>>>>>>>>>> Magnetic field: " << bz; + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + int duplicated = 0; + + std::unordered_map> label_vecClus[mClustersROFRecords.size()][NLAYERS]; // array of maps nRofs x Nlayers -> {label, vec(iClus)} where vec(iClus) are the clusters that share the same label + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + float ip[2]; // IP from 0,0,0 and the track should be the deplacement of the primary vertex + track.getImpactParams(0, 0, 0, 0, ip); + + // if (abs(ip[0])>0.001 ) continue; ///pv not in (0,0,0) + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + // if (pt < mPtCuts[0] || pt > mPtCuts[1]) { + // continue; + // } + // if (eta < mEtaCuts[0] || eta > mEtaCuts[1]) { + // continue; + // } + + float phioriginal = 0; + float phiduplicated = 0; + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + UShort_t rowOriginal = clusOriginal.getRow(); + UShort_t colOriginal = clusOriginal.getCol(); + + auto layer = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layer >= NLAYERS) { + continue; // checking only selected layers + } + auto labsTrack = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + phioriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + mPhiOriginal[layer]->Fill(phioriginal); + mPtOriginal[layer]->Fill(pt); + mEtaOriginal[layer]->Fill(eta); + m3DClusterPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y(), clusOriginalPointGlob.z()); + m2DClusterOriginalPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + + for (auto& labT : labsTrack) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + nLabels++; + if (labT.isValid()) { + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + auto layerClus = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerClus != layer) { + continue; + } + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + // phiduplicated = std::atan2(clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.x()) * 180 / M_PI + 180; + phiduplicated = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + auto labsClus = mClustersMCLCont->getLabels(iClus); // ideally I can have more than one label per cluster + for (auto labC : labsClus) { + if (labC == labT) { + label_vecClus[iROF][layerClus][labT].push_back(iClus); // same cluster: label from the track = label from the cluster + // if a duplicate cluster is found, propagate the track to the duplicate cluster and compute the distance from the original cluster + // if (clusOriginalPointGlob != clusDuplicatedPointGlob) { /// check that the duplicated cluster is not the original one just counted twice + // if (clusDuplicated.getSensorID() != clusOriginal.getSensorID()) { /// check that the duplicated cluster is not the original one just counted twice + + // applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerClus) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + duplicated++; + + if (countDuplicated == 0) { + UShort_t rowDuplicated = clusDuplicated.getRow(); + UShort_t colDuplicated = clusDuplicated.getCol(); + + chipRowDuplicated[layerDuplicated]->Fill(rowDuplicated); + chipRowOriginalIfDuplicated[layerDuplicated]->Fill(rowOriginal); + + mDuplicated_layer[layerDuplicated]++; // This has to be incremented at the first call + mPtDuplicated[layerClus]->Fill(pt); + mEtaDuplicated[layerClus]->Fill(eta); + mPhiDuplicated[layerClus]->Fill(phiduplicated); + mZvsPhiDUplicated[layerClus]->Fill(clusDuplicatedPointGlob.Z(), phiduplicated); + mPhiOriginalIfDuplicated[layerClus]->Fill(phioriginal); + } + + if (countDuplicated == 1) { + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mDuplicatedEta[layerDuplicated][ipt]->Fill(eta); + mDuplicatedPhi[layerDuplicated][ipt]->Fill(phiduplicated); + } + } + UShort_t rowDuplicated = clusDuplicated.getRow(); + mDuplicatedRow[layerDuplicated]->Fill(rowOriginal); + mDuplicatedCol[layerDuplicated]->Fill(clusOriginal.getCol()); + mDuplicatedZ[layerDuplicated]->Fill(clusOriginalPointGlob.Z()); + mDuplicatedPt[layerDuplicated]->Fill(pt); + mDuplicatedPtEta[layerDuplicated]->Fill(pt, eta); + mDuplicatedPtPhi[layerDuplicated]->Fill(pt, phiduplicated); + mDuplicatedEtaPhi[layerDuplicated]->Fill(eta, phiduplicated); + + mDuplicatedEtaAllPt[layerDuplicated]->Fill(eta); + mDuplicatedPhiAllPt[layerDuplicated]->Fill(phiduplicated); + mPt_EtaDupl[layerClus]->Fill(pt, eta); + } + + m3DClusterPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.z()); + m2DClusterDuplicatedPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y()); + + /// Compute the DCA between the cluster location and the track + + /// first propagate to the original cluster + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusOriginal.getSensorID())); + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusOriginal.getSensorID())); + if (propagator->propagateToDCA(clusOriginalPointGlob, trackParCov, b, 2.f, matCorr, &clusOriginalDCA)) { + mDCAxyOriginal[layerClus]->Fill(clusOriginalDCA[0]); + mDCAzOriginal[layerClus]->Fill(clusOriginalDCA[1]); + } + /// then propagate to the duplicated cluster + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { + mDCAxyDuplicated->Fill(clusDuplicatedDCA[0]); + mDCAzDuplicated->Fill(clusDuplicatedDCA[1]); + mDCAxyDuplicated_layer[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + mDCAzDuplicated_layer[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + } + /////////////////////////////////////////////////////// + } + } + } + } + } + } // end loop on clusters + totClus += NLAYERS; // summing only the number of clusters in the considered layers. Since the imposition of 7-clusters tracks, if the track is valid should release as clusters as the number of considered layers + } // end loop on tracks per ROF + } // end loop on ROFRecords array + LOGP(info, "Total number of clusters: {} ", totClus); + LOGP(info, "total nLabels: {}", nLabels); + LOGP(info, "Number of duplicated clusters: {}", duplicated); + + if (mVerboseOutput && mUseMC) { + // printing the duplicates + for (unsigned int iROF = 0; iROF < mClustersROFRecords.size(); iROF++) { + LOGP(info, "°°°°°°°°°°°°°°°°°°°°°°°° ROF {} °°°°°°°°°°°°°°°°°°°°°°°°", iROF); + for (unsigned int lay = 0; lay < NLAYERS; lay++) { + LOGP(info, "°°°°°°°°°°°°°°°°°°°°°°°° LAYER {} °°°°°°°°°°°°°°°°°°°°°°°°", lay); + for (auto& it : label_vecClus[iROF][lay]) { + if (it.second.size() <= 1) { + continue; // printing only duplicates + } + std::cout << " \n++++++++++++ Label: "; + auto label = it.first; + it.first.print(); + for (auto iClus : it.second) { + auto name = mGeometry->getSymbolicName(mClusters[iClus].getSensorID()); + auto chipid = mClusters[iClus].getChipID(); + auto clus = mClusters[iClus]; + auto clusPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusPointTrack = {clusPoint.getX(), clusPoint.getY(), clusPoint.getZ()}; + o2::math_utils::Point3D clusPointGlob = mGeometry->getMatrixT2G(clus.getSensorID()) * clusPointTrack; + std::cout << "ROF: " << iROF << ", iClus: " << iClus << " -> chip: " << chipid << " = " << name << std::endl; + LOGP(info, "LOCtrack: {} {} {}", clusPointTrack.x(), clusPointTrack.y(), clusPointTrack.z()); + LOGP(info, "LOCglob {} {} {}", clusPointGlob.x(), clusPointGlob.y(), clusPointGlob.z()); + } + } + } + } + } + return duplicated; +} + +void EfficiencyStudy::countDuplicatedAfterCuts() +{ + // count the effective number of duplicated cluster good matches after applying the pt eta and phi cuts on the track + // to check the applied cuts + LOGP(info, "--------------- countDuplicatedAfterCuts"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + int duplicated[3] = {0}; + int possibleduplicated[3] = {0}; + + std::cout << "Track candidates: " << std::endl; + + std::unordered_map> label_vecClus[mClustersROFRecords.size()][NLAYERS]; // array of maps nRofs x Nlayers -> {label, vec(iClus)} where vec(iClus) are the clusters that share the same label + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + std::cout << "ROF number: " << iROF << std::endl; + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto eta = trackParCov.getEta(); + + // applying the cuts on the track - only eta + + if (eta < mEtaCuts[0] || eta >= mEtaCuts[1]) { + continue; + } + + float phi = -999.; + float phiOriginal = -999.; + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + auto layer = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layer >= NLAYERS) { + continue; // checking only selected layers + } + auto labsTrack = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + if (abs(clusOriginalPointGlob.y()) < 0.5) { ///// excluding gap between bottom and top barrels + continue; + } + + if (abs(clusOriginalPointGlob.z()) >= 10) { /// excluding external z + continue; + } + + if (clusOriginal.getRow() < 2 || (clusOriginal.getRow() > 15 && clusOriginal.getRow() < 496) || clusOriginal.getRow() > 509) { //// cutting on the row + continue; + } + + if (clusOriginal.getCol() < 160 || clusOriginal.getCol() > 870) { /// excluding the gap between two chips in the same stave (comment to obtain the plot efficiency col vs eta) + continue; + } + + for (auto& labT : labsTrack) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + + if (labT.isValid()) { + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + auto layerClus = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerClus != layer) { + continue; + } + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + auto labsClus = mClustersMCLCont->getLabels(iClus); // ideally I can have more than one label per cluster + for (auto labC : labsClus) { + if (labC == labT) { + label_vecClus[iROF][layerClus][labT].push_back(iClus); // same cluster: label from the track = label from the cluster + // if a duplicate cluster is found, propagate the track to the duplicate cluster and compute the distance from the original cluster + // if (clusOriginalPointGlob != clusDuplicatedPointGlob) { /// check that the duplicated cluster is not the original one just counted twice + // if (clusDuplicated.getSensorID() != clusOriginal.getSensorID()) { /// check that the duplicated cluster is not the original one just counted twice + + // applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerClus) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + duplicated[layer]++; + std::cout << "Taken L" << layer << " # " << duplicated[layer] << " : eta, phi = " << eta << " , " << phiOriginal << " Label: " << std::endl; + labC.print(); + } + } + } + } + } + } // end loop on clusters + totClus += ncl; + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + LOGP(info, "Total number of possible cluster duplicated in L0: {} ", possibleduplicated[0]); + LOGP(info, "Total number of possible cluster duplicated in L1: {} ", possibleduplicated[1]); + LOGP(info, "Total number of possible cluster duplicated in L2: {} ", possibleduplicated[2]); + + LOGP(info, "Total number of cluster duplicated in L0: {} ", duplicated[0]); + LOGP(info, "Total number of cluster duplicated in L1: {} ", duplicated[1]); + LOGP(info, "Total number of cluster duplicated in L2: {} ", duplicated[2]); +} + +void EfficiencyStudy::studyDCAcutsMC() +{ + //// Study the DCA cuts to be applied + + LOGP(info, "--------------- studyDCAcutsMC"); + + int duplicated = getDCAClusterTrackMC(0); + + double meanDCAxyDuplicated[NLAYERS] = {0}; + double meanDCAzDuplicated[NLAYERS] = {0}; + double sigmaDCAxyDuplicated[NLAYERS] = {0}; + double sigmaDCAzDuplicated[NLAYERS] = {0}; + + std::ofstream ofs("dcaValues.csv", std::ofstream::out); + ofs << "layer,dcaXY,dcaZ,sigmaDcaXY,sigmaDcaZ" << std::endl; + + for (int l = 0; l < NLAYERS; l++) { + meanDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetMean(); + meanDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetMean(); + sigmaDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetRMS(); + sigmaDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetRMS(); + ofs << l << "," << meanDCAxyDuplicated[l] << "," << meanDCAzDuplicated[l] << "," << sigmaDCAxyDuplicated[l] << "," << sigmaDCAzDuplicated[l] << std::endl; + } + + for (int l = 0; l < NLAYERS; l++) { + LOGP(info, "meanDCAxyDuplicated L{}: {}, meanDCAzDuplicated: {}, sigmaDCAxyDuplicated: {}, sigmaDCAzDuplicated: {}", l, meanDCAxyDuplicated[l], meanDCAzDuplicated[l], sigmaDCAxyDuplicated[l], sigmaDCAzDuplicated[l]); + } + // now we have the DCA distribution: + // ->iterate again over tracks and over duplicated clusters and find the matching ones basing on DCA cuts (1 sigma, 2 sigma,...) + // then control if the matching ones according to the DCA matches have the same label + // if yes, then we have a good match -> increase the good match counter + // if not, keep it as a fake match -> increase the fake match counter + // the efficiency of each one will be match counter / total of the duplicated clusters + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + unsigned int nDCAMatches[20] = {0}; + unsigned int nGoodMatches[20] = {0}; + unsigned int nFakeMatches[20] = {0}; + + unsigned int nGoodMatches_layer[NLAYERS][20] = {0}; + unsigned int nFakeMatches_layer[NLAYERS][20] = {0}; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + float phi = -999.; + float phiOriginal = -999.; + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + if (mVerboseOutput) { + LOGP(info, "--------- track Label: "); + tracklab.print(); + } + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; // cluster spacepoint in the tracking system + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layerOriginal >= NLAYERS) { + continue; + } + auto labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + for (auto& labT : labsOriginal) { // for each valid label iterate over ALL the clusters in the ROF to see if there are duplicates + if (labT != tracklab) { + continue; + } + if (!labT.isValid()) { + continue; + } + + /// for each oroginal cluster iterate over all the possible "adjacent" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then compare the cluster label with the track label to see if it is a true or fake match + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + auto labsDuplicated = mClustersMCLCont->getLabels(iClus); + + /// if the cheks are passed, then calculate the DCA + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + /// checking the DCA for 20 different sigma ranges + for (int i = 0; i < 20; i++) { + if (abs(dcaXY[layerDuplicated] - clusDuplicatedDCA[0]) < (i + 1) * sigmaDcaXY[layerDuplicated] && abs(dcaZ[layerDuplicated] - clusDuplicatedDCA[1]) < (i + 1) * sigmaDcaZ[layerDuplicated]) { // check if the DCA is within the cut i*sigma + + if (mVerboseOutput) { + LOGP(info, "Check DCA ok: {} < {}; {} < {}", abs(meanDCAxyDuplicated[layerDuplicated] - clusDuplicatedDCA[0]), (i + 1) * sigmaDCAxyDuplicated[layerDuplicated], abs(meanDCAzDuplicated[layerDuplicated] - clusDuplicatedDCA[1]), (i + 1) * sigmaDCAzDuplicated[layerDuplicated]); + } + nDCAMatches[i]++; + bool isGoodMatch = false; + + for (auto labD : labsDuplicated) { // at this point the track has been matched with a duplicated cluster based on the DCA cut. Now we check if the matching is good ore not based on the label + if (mVerboseOutput) { + LOGP(info, "tracklab, labD:"); + tracklab.print(); + labD.print(); + } + if (labD == tracklab) { //// check if the label of the origial cluster is equal to the label of the duplicated cluster among all the labels for a cluster + isGoodMatch = true; + continue; + } + } + + if (isGoodMatch) { + nGoodMatches[i]++; + nGoodMatches_layer[layerDuplicated][i]++; + mnGoodMatchesPt_layer[layerDuplicated]->Fill(pt, i); + mnGoodMatchesEta_layer[layerDuplicated]->Fill(eta, i); + mnGoodMatchesPhi_layer[layerDuplicated]->Fill(phi, i); + mnGoodMatchesPhiOriginal_layer[layerDuplicated]->Fill(phiOriginal, i); + } else { + + nFakeMatches[i]++; + nFakeMatches_layer[layerDuplicated][i]++; + mnFakeMatchesPt_layer[layerDuplicated]->Fill(pt, i); + mnFakeMatchesEta_layer[layerDuplicated]->Fill(eta, i); + mnFakeMatchesPhi_layer[layerDuplicated]->Fill(phi, i); + } + } else if (mVerboseOutput) { + LOGP(info, "Check DCA failed"); + } + } + } else if (mVerboseOutput) { + LOGP(info, "Propagation failed"); + } + } // end loop on all the clusters in the rof + } + } // end loop on clusters associated to the track + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + for (int i = 0; i < 20; i++) { + LOGP(info, "Cut: {} sigma -> number of duplicated clusters: {} nDCAMatches: {} nGoodMatches: {} nFakeMatches: {}", i + 1, duplicated, nDCAMatches[i], nGoodMatches[i], nFakeMatches[i]); + mEfficiencyGoodMatch->SetBinContent(i + 1, nGoodMatches[i]); + mEfficiencyFakeMatch->SetBinContent(i + 1, nFakeMatches[i]); + mEfficiencyTotal->SetBinContent(i + 1, double(nGoodMatches[i] + nFakeMatches[i])); + + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatch_layer[l]->SetBinContent(i + 1, nGoodMatches_layer[l][i]); + mEfficiencyFakeMatch_layer[l]->SetBinContent(i + 1, nFakeMatches_layer[l][i]); + mEfficiencyTotal_layer[l]->SetBinContent(i + 1, double(nGoodMatches_layer[l][i] + nFakeMatches_layer[l][i])); + + for (int ipt = 0; ipt < mPtDuplicated[l]->GetNbinsX(); ipt++) { + if (mPtDuplicated[l]->GetBinContent(ipt + 1) != 0) { + mEfficiencyGoodMatchPt_layer[l]->SetBinContent(ipt + 1, i + 1, mnGoodMatchesPt_layer[l]->GetBinContent(ipt + 1, i + 1) / mPtDuplicated[l]->GetBinContent(ipt + 1)); + } + mEfficiencyFakeMatchPt_layer[l]->SetBinContent(ipt + 1, i + 1, mnFakeMatchesPt_layer[l]->GetBinContent(ipt + 1, i + 1) / mPtDuplicated[l]->GetBinContent(ipt + 1)); + } + + for (int ieta = 0; ieta < mEtaDuplicated[l]->GetNbinsX(); ieta++) { + if (mEtaDuplicated[l]->GetBinContent(ieta + 1) != 0) { + mEfficiencyGoodMatchEta_layer[l]->SetBinContent(ieta + 1, i + 1, mnGoodMatchesEta_layer[l]->GetBinContent(ieta + 1, i + 1) / mEtaDuplicated[l]->GetBinContent(ieta + 1)); + } + mEfficiencyFakeMatchEta_layer[l]->SetBinContent(ieta + 1, i + 1, mnFakeMatchesEta_layer[l]->GetBinContent(ieta + 1, i + 1) / mEtaDuplicated[l]->GetBinContent(ieta + 1)); + } + + for (int iphi = 0; iphi < mPhiDuplicated[l]->GetNbinsX(); iphi++) { + if (mPhiDuplicated[l]->GetBinContent(iphi + 1) != 0) { + mEfficiencyGoodMatchPhi_layer[l]->SetBinContent(iphi + 1, i + 1, mnGoodMatchesPhi_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiDuplicated[l]->GetBinContent(iphi + 1)); + } + mEfficiencyFakeMatchPhi_layer[l]->SetBinContent(iphi + 1, i + 1, mnFakeMatchesPhi_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiDuplicated[l]->GetBinContent(iphi + 1)); + } + + for (int iphi = 0; iphi < mPhiOriginalIfDuplicated[l]->GetNbinsX(); iphi++) { + if (mPhiOriginalIfDuplicated[l]->GetBinContent(iphi + 1) != 0) { + mEfficiencyGoodMatchPhiOriginal_layer[l]->SetBinContent(iphi + 1, i + 1, mnGoodMatchesPhiOriginal_layer[l]->GetBinContent(iphi + 1, i + 1) / mPhiOriginalIfDuplicated[l]->GetBinContent(iphi + 1)); + } + } + } + } + for (int i = 0; i < NLAYERS; i++) { + std::cout << "+++++++++ Duplicated in layer L" << i << ": " << mDuplicated_layer[i] << std::endl; + } + + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatch_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + mEfficiencyFakeMatch_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + mEfficiencyTotal_layer[l]->Scale(1. / double(mDuplicated_layer[l]), "b"); + } + + mEfficiencyGoodMatch->Scale(1. / double(duplicated), "b"); + mEfficiencyFakeMatch->Scale(1. / double(duplicated), "b"); + mEfficiencyTotal->Scale(1. / double(duplicated), "b"); + + mOutFile->mkdir("EffVsDCA2D/"); + mOutFile->cd("EffVsDCA2D/"); + for (int l = 0; l < NLAYERS; l++) { + mEfficiencyGoodMatchPt_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPt_layer[l]->Write(); + mEfficiencyGoodMatchEta_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchEta_layer[l]->Write(); + mEfficiencyGoodMatchPhi_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPhi_layer[l]->Write(); + mEfficiencyGoodMatchPhiOriginal_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyGoodMatchPhiOriginal_layer[l]->Write(); + mEfficiencyFakeMatchPt_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchPt_layer[l]->Write(); + mEfficiencyFakeMatchEta_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchEta_layer[l]->Write(); + mEfficiencyFakeMatchPhi_layer[l]->GetZaxis()->SetRangeUser(0, 1); + mEfficiencyFakeMatchPhi_layer[l]->Write(); + } + + mOutFile->mkdir("Efficiency/"); + mOutFile->cd("Efficiency/"); + mEfficiencyGoodMatch->Write(); + mEfficiencyFakeMatch->Write(); + mEfficiencyTotal->Write(); + for (int l = 0; l < NLAYERS; l++) { + + mEfficiencyGoodMatch_layer[l]->Write(); + mEfficiencyFakeMatch_layer[l]->Write(); + mEfficiencyTotal_layer[l]->Write(); + + mEfficiencyGoodMatch_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyFakeMatch_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyTotal_layer[l]->GetYaxis()->SetRangeUser(-0.1, 1.1); + } + + mEfficiencyGoodMatch->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyFakeMatch->GetYaxis()->SetRangeUser(-0.1, 1.1); + mEfficiencyTotal->GetYaxis()->SetRangeUser(-0.1, 1.1); + + TCanvas c; + c.SetName("EffVsDCA_allLayers"); + auto leg = std::make_unique(0.75, 0.45, 0.89, 0.65); + leg->AddEntry(mEfficiencyGoodMatch.get(), "#frac{# good matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyFakeMatch.get(), "#frac{# fake matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyTotal.get(), "#frac{# tot matches}{# tot duplicated clusters}", "p"); + + mEfficiencyGoodMatch->Draw("P l E1_NOSTAT PLC PMC "); + mEfficiencyFakeMatch->Draw("same P l E1_NOSTAT PLC PMC"); + mEfficiencyTotal->Draw("same P l E1_NOSTAT PLC PMC"); + leg->Draw("same"); + c.Write(); + + TCanvas cc[NLAYERS]; + for (int l = 0; l < NLAYERS; l++) { + cc[l].cd(); + cc[l].SetName(Form("EffVsDCA_layer_L%d", l)); + + auto leg = std::make_unique(0.75, 0.45, 0.89, 0.65); + leg->AddEntry(mEfficiencyGoodMatch_layer[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyFakeMatch_layer[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "p"); + leg->AddEntry(mEfficiencyTotal_layer[l].get(), "#frac{# tot matches}{# tot duplicated clusters}", "p"); + + mEfficiencyGoodMatch_layer[l]->SetLineColor(kBlue + 3); + mEfficiencyGoodMatch_layer[l]->SetMarkerColor(kBlue + 3); + mEfficiencyGoodMatch_layer[l]->Draw("P l E1_NOSTAT"); + mEfficiencyFakeMatch_layer[l]->SetLineColor(kAzure + 7); + mEfficiencyFakeMatch_layer[l]->SetMarkerColor(kAzure + 7); + mEfficiencyFakeMatch_layer[l]->Draw("same P l E1_NOSTAT"); + mEfficiencyTotal_layer[l]->SetLineColor(kGreen + 1); + mEfficiencyTotal_layer[l]->SetMarkerColor(kGreen + 1); + mEfficiencyTotal_layer[l]->Draw("same P l E1_NOSTAT"); + leg->Draw("same"); + cc[l].Write(); + } +} + +void EfficiencyStudy::studyClusterSelectionMC() +{ + //// to be used only with MC + // study to find a good selection method for the duplicated cluster, to be used for non-MC data + // iterate over tracks an associated clusters, and find the closer cluster that is not the original one applying cuts on staveID and chipID + // fix the DCA < 10 sigma, then compute the efficiency for each bin of pt, eta and phi and also in the rows + + LOGP(info, "--------------- studyClusterSelection"); + + int duplicated = getDCAClusterTrackMC(1); + + std::cout << "duplicated: " << duplicated << std::endl; + + double meanDCAxyDuplicated[NLAYERS] = {0}; + double meanDCAzDuplicated[NLAYERS] = {0}; + double sigmaDCAxyDuplicated[NLAYERS] = {0}; + double sigmaDCAzDuplicated[NLAYERS] = {0}; + + for (int l = 0; l < NLAYERS; l++) { + meanDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetMean(); + meanDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetMean(); + sigmaDCAxyDuplicated[l] = mDCAxyDuplicated_layer[l]->GetRMS(); + sigmaDCAzDuplicated[l] = mDCAzDuplicated_layer[l]->GetRMS(); + } + + for (int l = 0; l < NLAYERS; l++) { + LOGP(info, "meanDCAxyDuplicated L{}: {}, meanDCAzDuplicated: {}, sigmaDCAxyDuplicated: {}, sigmaDCAzDuplicated: {}", l, meanDCAxyDuplicated[l], meanDCAzDuplicated[l], sigmaDCAxyDuplicated[l], sigmaDCAzDuplicated[l]); + } + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + int nLabels = 0; + unsigned int totClus = 0; + + unsigned int nDCAMatches[15] = {0}; + unsigned int nGoodMatches[15] = {0}; + unsigned int nFakeMatches[15] = {0}; + + std::map, bool> clusterMatchesPtEta[100][100] = {}; + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + //////calculcating efficiency vs pt, eta, phi + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + + /// cut on primary vertex position (?) + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + if (ncl < 7) { + continue; + } + + auto& tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + + auto pt = trackParCov.getPt(); + auto eta = trackParCov.getEta(); + + float phi = -999.; + float phiOriginal = -999.; + float phiDuplicated = -999.; + UShort_t row = -999; + + if (mVerboseOutput) { + LOGP(info, "--------- track Label: "); + tracklab.print(); + } + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + if (layerOriginal >= NLAYERS) { + continue; + } + + IPOriginalxy[layerOriginal]->Fill(ip[0]); + IPOriginalz[layerOriginal]->Fill(ip[1]); + + UShort_t rowOriginal = clusOriginal.getRow(); + + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + + auto phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + auto labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + std::tuple> clusID_rDCA_label = {0, 999., gsl::span()}; // inizializing tuple with dummy values + + bool adjacentFound = 0; + /// for each oroginal cluster iterate over all the possible "adjacten" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then choose the closest one. + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + auto labsDuplicated = mClustersMCLCont->getLabels(iClus); + + /// if the cheks are passed, then calculate the DCA + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + + auto phiDuplicated = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (!propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + continue; + } + + // Imposing that the distance between the original cluster and the duplicated one is less than x sigma + if (!(clusDuplicatedDCA[0] > mDCACutsXY[layerDuplicated][0] && clusDuplicatedDCA[0] < mDCACutsXY[layerDuplicated][1] && clusDuplicatedDCA[1] > mDCACutsZ[layerDuplicated][0] && clusDuplicatedDCA[1] < mDCACutsZ[layerDuplicated][1])) { + continue; + } + + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + double rDCA = std::hypot(clusDuplicatedDCA[0], clusDuplicatedDCA[1]); + + // taking the closest cluster within x sigma + if (rDCA < std::get<1>(clusID_rDCA_label)) { // updating the closest cluster + clusID_rDCA_label = {iClus, rDCA, labsDuplicated}; + phi = phiDuplicated; + row = rowOriginal; + } + adjacentFound = 1; + + } // end loop on all the clusters in the rof + + // here clusID_rDCA_label is updated with the closest cluster to the track other than the original one + // checking if it is a good or fake match looking at the labels + + if (!adjacentFound) { + continue; + } + + bool isGood = false; + for (auto lab : std::get<2>(clusID_rDCA_label)) { + if (lab == tracklab) { + isGood = true; + + mNGoodMatchesPt[layerOriginal]->Fill(pt); + mNGoodMatchesRow[layerOriginal]->Fill(row); + mNGoodMatchesCol[layerOriginal]->Fill(clusOriginal.getCol()); + mNGoodMatchesZ[layerOriginal]->Fill(clusOriginalPointGlob.Z()); + mNGoodMatchesPtEta[layerOriginal]->Fill(pt, eta); + mNGoodMatchesPtPhi[layerOriginal]->Fill(pt, phi); + mNGoodMatchesEtaPhi[layerOriginal]->Fill(eta, phi); + + mNGoodMatchesEtaAllPt[layerOriginal]->Fill(eta); + mNGoodMatchesPhiAllPt[layerOriginal]->Fill(phi); + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mNGoodMatchesEta[layerOriginal][ipt]->Fill(eta); + mNGoodMatchesPhi[layerOriginal][ipt]->Fill(phi); + } + } + + break; + } + } + if (!isGood) { + + mNFakeMatchesPt[layerOriginal]->Fill(pt); + mNFakeMatchesRow[layerOriginal]->Fill(row); + mNFakeMatchesCol[layerOriginal]->Fill(clusOriginal.getCol()); + mNFakeMatchesZ[layerOriginal]->Fill(clusOriginalPointGlob.Z()); + mNFakeMatchesPtEta[layerOriginal]->Fill(pt, eta); + mNFakeMatchesPtPhi[layerOriginal]->Fill(pt, phi); + mNFakeMatchesEtaPhi[layerOriginal]->Fill(eta, phi); + mNFakeMatchesEtaAllPt[layerOriginal]->Fill(eta); + mNFakeMatchesPhiAllPt[layerOriginal]->Fill(phi); + + for (int ipt = 0; ipt < 3; ipt++) { + if (pt >= mrangesPt[ipt][0] && pt < mrangesPt[ipt][1]) { + mNFakeMatchesEta[layerOriginal][ipt]->Fill(eta); + mNFakeMatchesPhi[layerOriginal][ipt]->Fill(phi); + } + } + } + } // end loop on clusters associated to the track + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + mOutFile->mkdir("EfficiencyCuts/"); + mOutFile->cd("EfficiencyCuts/"); + + std::cout << "Calculating efficiency..." << std::endl; + std::unique_ptr axpt = std::make_unique("axpt", "", 1, 0.05, 7.5); + std::unique_ptr axRow = std::make_unique("axRow", "", 1, -0.5, 511.5); + std::unique_ptr axCol = std::make_unique("axRow", "", 1, -0.5, 1023.5); + std::unique_ptr axZ = std::make_unique("axZ", "", 1, -15, 15); + std::unique_ptr axptetaGood = std::make_unique("axptetaGood", "", 1, 0.05, 7.5, 1, -2, 2); + std::unique_ptr axptetaFake = std::make_unique("axptetaFake", "", 1, 0.05, 7.5, 1, -2, 2); + std::unique_ptr axptphiGood = std::make_unique("axptphiGood", "", 1, 0.05, 7.5, 1, -3.2, 3.2); + std::unique_ptr axptphiFake = std::make_unique("axptphiFake", "", 1, 0.05, 7.5, 1, -3.2, 3.2); + std::unique_ptr axetaphiGood = std::make_unique("axetaphiGood", "", 1, -2, 2, 1, -3.2, 3.2); + std::unique_ptr axetaphiFake = std::make_unique("axetaphiFake", "", 1, -2, 2, 1, -3.2, 3.2); + std::unique_ptr axetaAllPt = std::make_unique("axetaAllPt", "", 1, -2, 2); + std::unique_ptr axeta[NLAYERS]; + std::unique_ptr axphi[NLAYERS]; + for (int ipt = 0; ipt < 3; ipt++) { + axeta[ipt] = std::make_unique(Form("axeta%d", ipt), Form("axeta%d", ipt), 1, -2, 2); + axphi[ipt] = std::make_unique(Form("axphi%d", ipt), Form("axphi%d", ipt), 1, -3.2, 3.2); + } + std::unique_ptr axphiAllPt = std::make_unique("axphi", "", 1, -3.2, 3.2); + + std::unique_ptr effPt[NLAYERS]; + std::unique_ptr effRow[NLAYERS]; + std::unique_ptr effCol[NLAYERS]; + std::unique_ptr effZ[NLAYERS]; + std::unique_ptr effPtEta[NLAYERS][2]; + std::unique_ptr effPtPhi[NLAYERS][2]; + std::unique_ptr effEtaPhi[NLAYERS][2]; + std::unique_ptr effEtaAllPt[NLAYERS]; + std::unique_ptr effEta[NLAYERS][3]; + std::unique_ptr effPhiAllPt[NLAYERS]; + std::unique_ptr effPhi[NLAYERS][3]; + + ///////////////// plotting results + for (int l = 0; l < 3; l++) { + if (mVerboseOutput) { + std::cout << "Pt L" << l << "\n\n"; + } + + // Pt + effPt[l] = std::make_unique(Form("effPt_L%d", l)); + + mEffPtGood[l] = std::make_unique(*mNGoodMatchesPt[l], *mDuplicatedPt[l]); + stileEfficiencyGraph(mEffPtGood[l], Form("mEffPtGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPt[l]->GetBinContent(ibin) > mDuplicatedPt[l]->GetBinContent(ibin)) { + std::cout << "--- Pt: Npass = " << mNFakeMatchesPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesPt[l]->SetBinContent(ibin, mDuplicatedPt[l]->GetBinContent(ibin)); + } + } + mEffPtFake[l] = std::make_unique(*mNFakeMatchesPt[l], *mDuplicatedPt[l]); + stileEfficiencyGraph(mEffPtFake[l], Form("mEffPtFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axpt->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});Efficiency", l)); + axpt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axpt->GetXaxis()->SetRangeUser(0.05, 7.5); + axpt->Draw(); + mEffPtGood[l]->Draw("same p"); + mEffPtFake[l]->Draw("same p"); + + auto legpt = std::make_unique(0.70, 0.15, 0.89, 0.35); + legpt->AddEntry(mEffPtGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legpt->AddEntry(mEffPtFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legpt->Draw("same"); + effPt[l]->Write(); + + // PtEtaGood + effPtEta[l][0] = std::make_unique(Form("effPtEtaGood_L%d", l)); + + mEffPtEtaGood[l] = std::make_unique(*mNGoodMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaGood[l], Form("mEffPtEtaGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + + axptetaGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaGood->GetYaxis()->SetRangeUser(-2., 2.); + axptetaGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaGood->Draw(); + mEffPtEtaGood[l]->Draw("same colz"); + effPtEta[l][0]->Update(); + effPtEta[l][0]->Write(); + + if (mVerboseOutput) { + std::cout << "Underflow (bin 0,0): " << mNFakeMatchesPtEta[l]->GetBinContent(0, 0) << " " << mDuplicatedPtEta[l]->GetBinContent(0, 0) << std::endl; + std::cout << "Overflow (bin nbinsx,nbinsy): " << mNFakeMatchesPtEta[l]->GetNbinsX() << " " << mNFakeMatchesPtEta[l]->GetNbinsY() << " -> " << mNFakeMatchesPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << " " << mDuplicatedPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << std::endl; + } + + for (int ibin = 1; ibin <= mNFakeMatchesPtEta[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtEta[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) > mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- PtEta fakematches : Npass = " << mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtEta[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtEta[l]->SetBinContent(ibin, jbin, mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)); + } + } + } + + // Row + effRow[l] = std::make_unique(Form("effRow_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesRow[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Row: Npass = " << mNGoodMatchesRow[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedRow[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffRowGood[l] = std::make_unique(*mNGoodMatchesRow[l], *mDuplicatedRow[l]); + stileEfficiencyGraph(mEffRowGood[l], Form("mEffRowGood_L%d", l), Form("L%d;Row;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesRow[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesRow[l]->GetBinContent(ibin) > mDuplicatedRow[l]->GetBinContent(ibin)) { + std::cout << "--- Row: Npass = " << mNFakeMatchesRow[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedRow[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesRow[l]->SetBinContent(ibin, mDuplicatedRow[l]->GetBinContent(ibin)); + } + } + mEffRowFake[l] = std::make_unique(*mNFakeMatchesRow[l], *mDuplicatedRow[l]); + stileEfficiencyGraph(mEffRowFake[l], Form("mEffRowFake_L%d", l), Form("L%d;Row;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axRow->SetTitle(Form("L%d;Row;Efficiency", l)); + axRow->GetYaxis()->SetRangeUser(-0.1, 1.1); + axRow->GetXaxis()->SetRangeUser(0, 512); + axRow->Draw(); + mEffRowGood[l]->Draw("same p"); + mEffRowFake[l]->Draw("same p"); + + auto legRow = std::make_unique(0.70, 0.15, 0.89, 0.35); + legRow->AddEntry(mEffRowGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legRow->AddEntry(mEffRowFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legRow->Draw("same"); + effRow[l]->Write(); + + // Col + effCol[l] = std::make_unique(Form("effCol_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesCol[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Col: Npass = " << mNGoodMatchesCol[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedCol[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffColGood[l] = std::make_unique(*mNGoodMatchesCol[l], *mDuplicatedCol[l]); + stileEfficiencyGraph(mEffColGood[l], Form("mEffColGood_L%d", l), Form("L%d;Col;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesCol[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesCol[l]->GetBinContent(ibin) > mDuplicatedCol[l]->GetBinContent(ibin)) { + std::cout << "--- Col: Npass = " << mNFakeMatchesCol[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedCol[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesCol[l]->SetBinContent(ibin, mDuplicatedCol[l]->GetBinContent(ibin)); + } + } + mEffColFake[l] = std::make_unique(*mNFakeMatchesCol[l], *mDuplicatedCol[l]); + stileEfficiencyGraph(mEffColFake[l], Form("mEffColFake_L%d", l), Form("L%d;Col;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axCol->SetTitle(Form("L%d;Col;Efficiency", l)); + axCol->GetYaxis()->SetRangeUser(-0.1, 1.1); + axCol->GetXaxis()->SetRangeUser(0, 1024); + axCol->Draw(); + mEffColGood[l]->Draw("same p"); + mEffColFake[l]->Draw("same p"); + + auto legCol = std::make_unique(0.70, 0.15, 0.89, 0.35); + legCol->AddEntry(mEffColGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legCol->AddEntry(mEffColFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legCol->Draw("same"); + effCol[l]->Write(); + + // Z + effZ[l] = std::make_unique(Form("effZ_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesZ[l]->GetNbinsX(); ibin++) { + std::cout << "--- Good Z: Npass = " << mNGoodMatchesZ[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedZ[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + + mEffZGood[l] = std::make_unique(*mNGoodMatchesZ[l], *mDuplicatedZ[l]); + stileEfficiencyGraph(mEffZGood[l], Form("mEffZGood_L%d", l), Form("L%d;Z;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesZ[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesZ[l]->GetBinContent(ibin) > mDuplicatedZ[l]->GetBinContent(ibin)) { + std::cout << "--- Z: Npass = " << mNFakeMatchesZ[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedZ[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + mNFakeMatchesZ[l]->SetBinContent(ibin, mDuplicatedZ[l]->GetBinContent(ibin)); + } + } + mEffZFake[l] = std::make_unique(*mNFakeMatchesZ[l], *mDuplicatedZ[l]); + stileEfficiencyGraph(mEffZFake[l], Form("mEffZFake_L%d", l), Form("L%d;Z;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axZ->SetTitle(Form("L%d;Z;Efficiency", l)); + axZ->GetYaxis()->SetRangeUser(-0.1, 1.1); + axZ->GetXaxis()->SetRangeUser(0, 512); + axZ->Draw(); + mEffZGood[l]->Draw("same p"); + mEffZFake[l]->Draw("same p"); + + auto legZ = std::make_unique(0.70, 0.15, 0.89, 0.35); + legZ->AddEntry(mEffZGood[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legZ->AddEntry(mEffZFake[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legZ->Draw("same"); + effZ[l]->Write(); + + // PtEtaGood + effPtEta[l][0] = std::make_unique(Form("effPtEtaGood_L%d", l)); + + mEffPtEtaGood[l] = std::make_unique(*mNGoodMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaGood[l], Form("mEffPtEtaGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + + axptetaGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaGood->GetYaxis()->SetRangeUser(-2., 2.); + axptetaGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaGood->Draw(); + mEffPtEtaGood[l]->Draw("same colz"); + effPtEta[l][0]->Update(); + effPtEta[l][0]->Write(); + + if (mVerboseOutput) { + std::cout << "Underflow (bin 0,0): " << mNFakeMatchesPtEta[l]->GetBinContent(0, 0) << " " << mDuplicatedPtEta[l]->GetBinContent(0, 0) << std::endl; + std::cout << "Overflow (bin nbinsx,nbinsy): " << mNFakeMatchesPtEta[l]->GetNbinsX() << " " << mNFakeMatchesPtEta[l]->GetNbinsY() << " -> " << mNFakeMatchesPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << " " << mDuplicatedPtEta[l]->GetBinContent(mNFakeMatchesPtEta[l]->GetNbinsX(), mNFakeMatchesPtEta[l]->GetNbinsY()) << std::endl; + } + + for (int ibin = 1; ibin <= mNFakeMatchesPtEta[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtEta[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) > mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- PtEta fakematches : Npass = " << mNFakeMatchesPtEta[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtEta[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtEta[l]->SetBinContent(ibin, jbin, mDuplicatedPtEta[l]->GetBinContent(ibin, jbin)); + } + } + } + + // PtEtaFake + effPtEta[l][1] = std::make_unique(Form("effPtEtaFake_L%d", l)); + + mEffPtEtaFake[l] = std::make_unique(*mNFakeMatchesPtEta[l], *mDuplicatedPtEta[l]); + stileEfficiencyGraph(mEffPtEtaFake[l], Form("mEffPtEtaFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l), true); + axptetaFake->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#eta;Efficiency", l)); + axptetaFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptetaFake->GetYaxis()->SetRangeUser(-2., 2.); + axptetaFake->GetXaxis()->SetRangeUser(0.05, 7.5); + axptetaFake->Draw(); + mEffPtEtaFake[l]->Draw("same colz"); + effPtEta[l][1]->Update(); + effPtEta[l][1]->Write(); + + // PtPhiGood + effPtPhi[l][0] = std::make_unique(Form("effPtPhiGood_L%d", l)); + + mEffPtPhiGood[l] = std::make_unique(*mNGoodMatchesPtPhi[l], *mDuplicatedPtPhi[l]); + stileEfficiencyGraph(mEffPtPhiGood[l], Form("mEffPtPhiGood_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l), true); + + axptphiGood->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l)); + axptphiGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptphiGood->GetYaxis()->SetRangeUser(-3.2, 3.2); + axptphiGood->GetXaxis()->SetRangeUser(0.05, 7.5); + axptphiGood->Draw(); + mEffPtPhiGood[l]->Draw("same colz"); + effPtPhi[l][0]->Update(); + effPtPhi[l][0]->Write(); + + for (int ibin = 1; ibin <= mNFakeMatchesPtPhi[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesPtPhi[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesPtPhi[l]->GetBinContent(ibin, jbin) > mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- Pt: Npass = " << mNFakeMatchesPtPhi[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesPtPhi[l]->SetBinContent(ibin, jbin, mDuplicatedPtPhi[l]->GetBinContent(ibin, jbin)); + } + } + } + + // PtPhiFake + effPtPhi[l][1] = std::make_unique(Form("effPtPhiFake_L%d", l)); + + mEffPtPhiFake[l] = std::make_unique(*mNFakeMatchesPtPhi[l], *mDuplicatedPtPhi[l]); + stileEfficiencyGraph(mEffPtPhiFake[l], Form("mEffPtPhiFake_L%d", l), Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l), true); + axptphiFake->SetTitle(Form("L%d;#it{p}_{T} (GeV/#it{c});#phi (rad);Efficiency", l)); + axptphiFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axptphiFake->GetYaxis()->SetRangeUser(-3.2, 3.2); + axptphiFake->GetXaxis()->SetRangeUser(0.05, 7.5); + axptphiFake->Draw(); + mEffPtPhiFake[l]->Draw("same colz"); + effPtPhi[l][1]->Update(); + effPtPhi[l][1]->Write(); + + // EtaPhiGood + effEtaPhi[l][0] = std::make_unique(Form("effEtaPhiGood_L%d", l)); + + mEffEtaPhiGood[l] = std::make_unique(*mNGoodMatchesEtaPhi[l], *mDuplicatedEtaPhi[l]); + stileEfficiencyGraph(mEffEtaPhiGood[l], Form("mEffEtaPhiGood_L%d", l), Form("L%d;#eta;#phi (rad);Efficiency", l), true); + + axetaphiGood->SetTitle(Form("L%d;#eta;#phi (rad);Efficiency", l)); + axetaphiGood->GetZaxis()->SetRangeUser(-0.1, 1.1); + axetaphiGood->GetYaxis()->SetRangeUser(-3.2, 3.2); + axetaphiGood->GetXaxis()->SetRangeUser(-2, 2); + axetaphiGood->Draw(); + mEffEtaPhiGood[l]->Draw("same colz"); + effEtaPhi[l][0]->Update(); + effEtaPhi[l][0]->Write(); + + for (int ibin = 1; ibin <= mNFakeMatchesEtaPhi[l]->GetNbinsX(); ibin++) { + for (int jbin = 1; jbin <= mNFakeMatchesEtaPhi[l]->GetNbinsY(); jbin++) { + if (mNFakeMatchesEtaPhi[l]->GetBinContent(ibin, jbin) > mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin)) { + if (mVerboseOutput) { + std::cout << "--- Eta: Npass = " << mNFakeMatchesEtaPhi[l]->GetBinContent(ibin, jbin) << ", Nall = " << mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin) << " for ibin = " << ibin << ", jbin = " << jbin << std::endl; + } + mNFakeMatchesEtaPhi[l]->SetBinContent(ibin, jbin, mDuplicatedEtaPhi[l]->GetBinContent(ibin, jbin)); + } + } + } + + // EtaPhiFake + effEtaPhi[l][1] = std::make_unique(Form("effEtaPhiFake_L%d", l)); + + mEffEtaPhiFake[l] = std::make_unique(*mNFakeMatchesEtaPhi[l], *mDuplicatedEtaPhi[l]); + stileEfficiencyGraph(mEffEtaPhiFake[l], Form("mEffEtaPhiFake_L%d", l), Form("L%d;#eta;#phi (rad);Efficiency", l), true); + axetaphiFake->SetTitle(Form("L%d;#eta;#phi (rad);Efficiency", l)); + axetaphiFake->GetZaxis()->SetRangeUser(-0.1, 1.1); + axetaphiFake->GetYaxis()->SetRangeUser(-3.2, 3.2); + axetaphiFake->GetXaxis()->SetRangeUser(-2, 2); + axetaphiFake->Draw(); + mEffEtaPhiFake[l]->Draw("same colz"); + effEtaPhi[l][1]->Update(); + effEtaPhi[l][1]->Write(); + + // EtaAllPt + if (mVerboseOutput) { + std::cout << "Eta L" << l << "\n\n"; + } + + effEtaAllPt[l] = std::make_unique(Form("effEtaAllPt_L%d", l)); + + mEffEtaGoodAllPt[l] = std::make_unique(*mNGoodMatchesEtaAllPt[l], *mDuplicatedEtaAllPt[l]); + stileEfficiencyGraph(mEffEtaGoodAllPt[l], Form("mEffEtaGoodAllPt_L%d", l), Form("L%d;#eta;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesEtaAllPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesEtaAllPt[l]->GetBinContent(ibin) > mDuplicatedEtaAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- EtaAllPt: Npass = " << mNFakeMatchesEtaAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedEtaAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesEtaAllPt[l]->SetBinContent(ibin, mDuplicatedEtaAllPt[l]->GetBinContent(ibin)); + } + } + mEffEtaFakeAllPt[l] = std::make_unique(*mNFakeMatchesEtaAllPt[l], *mDuplicatedEtaAllPt[l]); + stileEfficiencyGraph(mEffEtaFakeAllPt[l], Form("mEffEtaFakeAllPt_L%d", l), Form("L%d;#eta;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axetaAllPt->SetTitle(Form("L%d;#eta;Efficiency", l)); + axetaAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axetaAllPt->Draw(); + mEffEtaGoodAllPt[l]->Draw("same p"); + mEffEtaFakeAllPt[l]->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGoodAllPt[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFakeAllPt[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEtaAllPt[l]->Write(); + + /// eta and phi in different pt ranges + for (int ipt = 0; ipt < 3; ipt++) { + // eta + effEta[l][ipt] = std::make_unique(Form("effEta_L%d_pt%d", l, ipt)); + + mEffEtaGood[l][ipt] = std::make_unique(*mNGoodMatchesEta[l][ipt], *mDuplicatedEta[l][ipt]); + stileEfficiencyGraph(mEffEtaGood[l][ipt], Form("mEffEtaGood_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesEta[l][ipt]->GetNbinsX(); ibin++) { + if (mNFakeMatchesEta[l][ipt]->GetBinContent(ibin) > mDuplicatedEta[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Eta : Npass = " << mNFakeMatchesEta[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedEta[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesEta[l][ipt]->SetBinContent(ibin, mDuplicatedEta[l][ipt]->GetBinContent(ibin)); + } + } + + mEffEtaFake[l][ipt] = std::make_unique(*mNFakeMatchesEta[l][ipt], *mDuplicatedEta[l][ipt]); + stileEfficiencyGraph(mEffEtaFake[l][ipt], Form("mEffEtaFake_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axeta[ipt]->SetTitle(Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#eta;Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1])); + axeta[ipt]->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axeta[ipt]->Draw(); + mEffEtaGood[l][ipt]->Draw("same p"); + mEffEtaFake[l][ipt]->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGood[l][ipt].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFake[l][ipt].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEta[l][ipt]->Write(); + + // phi + effPhi[l][ipt] = std::make_unique(Form("effPhi_L%d_pt%d", l, ipt)); + + for (int ibin = 1; ibin <= mNGoodMatchesPhi[l][ipt]->GetNbinsX(); ibin++) { + if (mNGoodMatchesPhi[l][ipt]->GetBinContent(ibin) > mDuplicatedPhi[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Phi L: Npass = " << mNGoodMatchesPhi[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhi[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNGoodMatchesPhi[l][ipt]->SetBinContent(ibin, 0); + } + } + + mEffPhiGood[l][ipt] = std::make_unique(*mNGoodMatchesPhi[l][ipt], *mDuplicatedPhi[l][ipt]); + stileEfficiencyGraph(mEffPhiGood[l][ipt], Form("mEffPhiGood_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPhi[l][ipt]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPhi[l][ipt]->GetBinContent(ibin) > mDuplicatedPhi[l][ipt]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- Phi L: Npass = " << mNFakeMatchesPhi[l][ipt]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhi[l][ipt]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesPhi[l][ipt]->SetBinContent(ibin, mDuplicatedPhi[l][ipt]->GetBinContent(ibin)); + } + } + + mEffPhiFake[l][ipt] = std::make_unique(*mNFakeMatchesPhi[l][ipt], *mDuplicatedPhi[l][ipt]); + stileEfficiencyGraph(mEffPhiFake[l][ipt], Form("mEffPhiFake_L%d_pt%d", l, ipt), Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1]), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axphi[ipt]->SetTitle(Form("L%d %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#phi (rad);Efficiency", l, mrangesPt[ipt][0], mrangesPt[ipt][1])); + axphi[ipt]->GetYaxis()->SetRangeUser(-0.1, 1.1); + + axphi[ipt]->Draw(); + mEffPhiGood[l][ipt]->Draw("same p"); + mEffPhiFake[l][ipt]->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGood[l][ipt].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFake[l][ipt].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhi[l][ipt]->Write(); + } + + // PhiAllPt + if (mVerboseOutput) { + std::cout << "Phi L" << l << "\n\n"; + } + + effPhiAllPt[l] = std::make_unique(Form("effPhiAllPt_L%d", l)); + + for (int ibin = 1; ibin <= mNGoodMatchesPhiAllPt[l]->GetNbinsX(); ibin++) { + if (mNGoodMatchesPhiAllPt[l]->GetBinContent(ibin) > mDuplicatedPhiAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- phi all good Npass = " << mNGoodMatchesPhiAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhiAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNGoodMatchesPhiAllPt[l]->SetBinContent(ibin, 0); + } + } + + mEffPhiGoodAllPt[l] = std::make_unique(*mNGoodMatchesPhiAllPt[l], *mDuplicatedPhiAllPt[l]); + stileEfficiencyGraph(mEffPhiGoodAllPt[l], Form("mEffPhiGoodAllPt_L%d", l), Form("L%d;#phi;Efficiency", l), false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + + for (int ibin = 1; ibin <= mNFakeMatchesPhiAllPt[l]->GetNbinsX(); ibin++) { + if (mNFakeMatchesPhiAllPt[l]->GetBinContent(ibin) > mDuplicatedPhiAllPt[l]->GetBinContent(ibin)) { + if (mVerboseOutput) { + std::cout << "--- phi all fake Npass = " << mNFakeMatchesPhiAllPt[l]->GetBinContent(ibin) << ", Nall = " << mDuplicatedPhiAllPt[l]->GetBinContent(ibin) << " for ibin = " << ibin << std::endl; + } + mNFakeMatchesPhiAllPt[l]->SetBinContent(ibin, mDuplicatedPhiAllPt[l]->GetBinContent(ibin)); + } + } + mEffPhiFakeAllPt[l] = std::make_unique(*mNFakeMatchesPhiAllPt[l], *mDuplicatedPhiAllPt[l]); + stileEfficiencyGraph(mEffPhiFakeAllPt[l], Form("mEffPhiFakeAllPt_L%d", l), Form("L%d;#phi;Efficiency", l), false, kFullDiamond, 1, kRed + 1, kRed + 1); + + axphiAllPt->SetTitle(Form("L%d;#phi;Efficiency", l)); + axphiAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axphiAllPt->Draw(); + mEffPhiGoodAllPt[l]->Draw("same p"); + mEffPhiFakeAllPt[l]->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGoodAllPt[l].get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFakeAllPt[l].get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhiAllPt[l]->Write(); + } + + /// all Row + std::unique_ptr effRowAll = std::make_unique("effRowAll"); + auto numRowGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesRow[0]->Clone("numRowGoodAll")); + numRowGoodAll->Add(mNGoodMatchesRow[1].get()); + numRowGoodAll->Add(mNGoodMatchesRow[2].get()); + numRowGoodAll->Write(); + auto numRowFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesRow[0]->Clone("numRowFakeAll")); + numRowFakeAll->Add(mNFakeMatchesRow[1].get()); + numRowFakeAll->Add(mNFakeMatchesRow[2].get()); + numRowFakeAll->Write(); + auto denRowAll = std::unique_ptr((TH1D*)mDuplicatedRow[0]->Clone("denRowAll")); + denRowAll->Add(mDuplicatedRow[1].get()); + denRowAll->Add(mDuplicatedRow[2].get()); + denRowAll->Write(); + + std::unique_ptr mEffRowGoodAll = std::make_unique(*numRowGoodAll, *denRowAll); + stileEfficiencyGraph(mEffRowGoodAll, "mEffRowGoodAll", "L0 + L1 + L2;Row;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffRowFakeAll = std::make_unique(*numRowFakeAll, *denRowAll); + stileEfficiencyGraph(mEffRowFakeAll, "mEffRowFakeAll", "L0 + L1 + L2;Row;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axRow->SetTitle("L0 + L1 + L2;Row;Efficiency"); + axRow->GetYaxis()->SetRangeUser(-0.1, 1.1); + axRow->Draw(); + mEffRowGoodAll->Draw("same p"); + mEffRowFakeAll->Draw("same p"); + + auto legRow = std::make_unique(0.70, 0.15, 0.89, 0.35); + legRow->AddEntry(mEffRowGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legRow->AddEntry(mEffRowFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legRow->Draw("same"); + effRowAll->Write(); + + /// all Col + std::unique_ptr effColAll = std::make_unique("effColAll"); + auto numColGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesCol[0]->Clone("numColGoodAll")); + numColGoodAll->Add(mNGoodMatchesCol[1].get()); + numColGoodAll->Add(mNGoodMatchesCol[2].get()); + numColGoodAll->Write(); + auto numColFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesCol[0]->Clone("numColFakeAll")); + numColFakeAll->Add(mNFakeMatchesCol[1].get()); + numColFakeAll->Add(mNFakeMatchesCol[2].get()); + numColFakeAll->Write(); + auto denColAll = std::unique_ptr((TH1D*)mDuplicatedCol[0]->Clone("denColAll")); + denColAll->Add(mDuplicatedCol[1].get()); + denColAll->Add(mDuplicatedCol[2].get()); + denColAll->Write(); + + std::unique_ptr mEffColGoodAll = std::make_unique(*numColGoodAll, *denColAll); + stileEfficiencyGraph(mEffColGoodAll, "mEffColGoodAll", "L0 + L1 + L2;Column;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffColFakeAll = std::make_unique(*numColFakeAll, *denColAll); + stileEfficiencyGraph(mEffColFakeAll, "mEffColFakeAll", "L0 + L1 + L2;Column;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axCol->SetTitle("L0 + L1 + L2;Col;Efficiency"); + axCol->GetYaxis()->SetRangeUser(-0.1, 1.1); + axCol->Draw(); + mEffColGoodAll->Draw("same p"); + mEffColFakeAll->Draw("same p"); + + auto legCol = std::make_unique(0.70, 0.15, 0.89, 0.35); + legCol->AddEntry(mEffColGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legCol->AddEntry(mEffColFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legCol->Draw("same"); + effColAll->Write(); + + /// all Z + std::unique_ptr effZAll = std::make_unique("effZAll"); + auto numZGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesZ[0]->Clone("numZGoodAll")); + numZGoodAll->Add(mNGoodMatchesZ[1].get()); + numZGoodAll->Add(mNGoodMatchesZ[2].get()); + numZGoodAll->Write(); + auto numZFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesZ[0]->Clone("numZFakeAll")); + numZFakeAll->Add(mNFakeMatchesZ[1].get()); + numZFakeAll->Add(mNFakeMatchesZ[2].get()); + numZFakeAll->Write(); + auto denZAll = std::unique_ptr((TH1D*)mDuplicatedZ[0]->Clone("denZAll")); + denZAll->Add(mDuplicatedZ[1].get()); + denZAll->Add(mDuplicatedZ[2].get()); + denZAll->Write(); + + std::unique_ptr mEffZGoodAll = std::make_unique(*numZGoodAll, *denZAll); + stileEfficiencyGraph(mEffZGoodAll, "mEffZGoodAll", "L0 + L1 + L2;Z;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffZFakeAll = std::make_unique(*numZFakeAll, *denZAll); + stileEfficiencyGraph(mEffZFakeAll, "mEffZFakeAll", "L0 + L1 + L2;Z;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axZ->SetTitle("L0 + L1 + L2;Z;Efficiency"); + axZ->GetYaxis()->SetRangeUser(-0.1, 1.1); + axZ->Draw(); + mEffZGoodAll->Draw("same p"); + mEffZFakeAll->Draw("same p"); + + auto legZ = std::make_unique(0.70, 0.15, 0.89, 0.35); + legZ->AddEntry(mEffZGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legZ->AddEntry(mEffZFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legZ->Draw("same"); + effZAll->Write(); + + /// all Eta + std::unique_ptr effEtaAll = std::make_unique("effEtaAll"); + auto numEtaGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesEtaAllPt[0]->Clone("numEtaGoodAll")); + numEtaGoodAll->Add(mNGoodMatchesEtaAllPt[1].get()); + numEtaGoodAll->Add(mNGoodMatchesEtaAllPt[2].get()); + numEtaGoodAll->Write(); + auto numEtaFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesEtaAllPt[0]->Clone("numEtaFakeAll")); + numEtaFakeAll->Add(mNFakeMatchesEtaAllPt[1].get()); + numEtaFakeAll->Add(mNFakeMatchesEtaAllPt[2].get()); + numEtaFakeAll->Write(); + auto denEtaAll = std::unique_ptr((TH1D*)mDuplicatedEtaAllPt[0]->Clone("denEtaAll")); + denEtaAll->Add(mDuplicatedEtaAllPt[1].get()); + denEtaAll->Add(mDuplicatedEtaAllPt[2].get()); + denEtaAll->Write(); + + std::unique_ptr mEffEtaGoodAll = std::make_unique(*numEtaGoodAll, *denEtaAll); + stileEfficiencyGraph(mEffEtaGoodAll, "mEffEtaGoodAll", "L0 + L1 + L2;#Eta;Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffEtaFakeAll = std::make_unique(*numEtaFakeAll, *denEtaAll); + stileEfficiencyGraph(mEffEtaFakeAll, "mEffEtaFakeAll", "L0 + L1 + L2;#Eta;Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axetaAllPt->SetTitle("L0 + L1 + L2;Eta;Efficiency"); + axetaAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axetaAllPt->Draw(); + mEffEtaGoodAll->Draw("same p"); + mEffEtaFakeAll->Draw("same p"); + + auto legEta = std::make_unique(0.70, 0.15, 0.89, 0.35); + legEta->AddEntry(mEffEtaGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legEta->AddEntry(mEffEtaFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legEta->Draw("same"); + effEtaAll->Write(); + + /// all Phi + std::unique_ptr effPhiAll = std::make_unique("effPhiAll"); + auto numPhiGoodAll = std::unique_ptr((TH1D*)mNGoodMatchesPhiAllPt[0]->Clone("numPhiGoodAll")); + numPhiGoodAll->Add(mNGoodMatchesPhiAllPt[1].get()); + numPhiGoodAll->Add(mNGoodMatchesPhiAllPt[2].get()); + numPhiGoodAll->Write(); + auto numPhiFakeAll = std::unique_ptr((TH1D*)mNFakeMatchesPhiAllPt[0]->Clone("numPhiFakeAll")); + numPhiFakeAll->Add(mNFakeMatchesPhiAllPt[1].get()); + numPhiFakeAll->Add(mNFakeMatchesPhiAllPt[2].get()); + numPhiFakeAll->Write(); + auto denPhiAll = std::unique_ptr((TH1D*)mDuplicatedPhiAllPt[0]->Clone("denPhiAll")); + denPhiAll->Add(mDuplicatedPhiAllPt[1].get()); + denPhiAll->Add(mDuplicatedPhiAllPt[2].get()); + denPhiAll->Write(); + + std::unique_ptr mEffPhiGoodAll = std::make_unique(*numPhiGoodAll, *denPhiAll); + stileEfficiencyGraph(mEffPhiGoodAll, "mEffPhiGoodAll", "L0 + L1 + L2;#Phi (rad);Efficiency", false, kFullDiamond, 1, kGreen + 3, kGreen + 3); + std::unique_ptr mEffPhiFakeAll = std::make_unique(*numPhiFakeAll, *denPhiAll); + stileEfficiencyGraph(mEffPhiFakeAll, "mEffPhiFakeAll", "L0 + L1 + L2;#Phi (rad);Efficiency", false, kFullDiamond, 1, kRed + 1, kRed + 1); + axphiAllPt->SetTitle("L0 + L1 + L2;Phi;Efficiency"); + axphiAllPt->GetYaxis()->SetRangeUser(-0.1, 1.1); + axphiAllPt->Draw(); + mEffPhiGoodAll->Draw("same p"); + mEffPhiFakeAll->Draw("same p"); + + auto legPhi = std::make_unique(0.70, 0.15, 0.89, 0.35); + legPhi->AddEntry(mEffPhiGoodAll.get(), "#frac{# good matches}{# tot duplicated clusters}", "pl"); + legPhi->AddEntry(mEffPhiFakeAll.get(), "#frac{# fake matches}{# tot duplicated clusters}", "pl"); + legPhi->Draw("same"); + effPhiAll->Write(); +} + +void EfficiencyStudy::getEfficiency(bool isMC) +{ + // Extract the efficiency for the IB, exploiting the staves overlaps and the duplicated clusters for the tracks passing through the overlaps + // The denominator for the efficiency calculation will be the number of tracks per layer fulfilling some cuts (eta, z, row, col) + // The numerator will be the number of duplicated clusters for the tracks passing through the overlaps + + LOGP(info, "getEfficiency()"); + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::array clusOriginalDCA, clusDuplicatedDCA; + auto propagator = o2::base::Propagator::Instance(); + + unsigned int rofIndexTrack = 0; + unsigned int rofNEntriesTrack = 0; + unsigned int rofIndexClus = 0; + unsigned int rofNEntriesClus = 0; + + int nbPt = 75; + double xbins[nbPt + 1], ptcutl = 0.05, ptcuth = 7.5; + double a = std::log(ptcuth / ptcutl) / nbPt; + for (int i = 0; i <= nbPt; i++) { + xbins[i] = ptcutl * std::exp(i * a); + } + + int totNClusters; + int nDuplClusters; + + for (unsigned int iROF = 0; iROF < mTracksROFRecords.size(); iROF++) { // loop on ROFRecords array + + rofIndexTrack = mTracksROFRecords[iROF].getFirstEntry(); + rofNEntriesTrack = mTracksROFRecords[iROF].getNEntries(); + + rofIndexClus = mClustersROFRecords[iROF].getFirstEntry(); + rofNEntriesClus = mClustersROFRecords[iROF].getNEntries(); + + ////// imposing cuts on the tracks = collecting tracks for the denominator + for (unsigned int iTrack = rofIndexTrack; iTrack < rofIndexTrack + rofNEntriesTrack; iTrack++) { // loop on tracks per ROF + auto track = mTracks[iTrack]; + o2::track::TrackParCov trackParCov = mTracks[iTrack]; + + auto pt = trackParCov.getPt(); // Always 0.6 GeV/c for B = 0 T + auto eta = trackParCov.getEta(); + float phi = -999.; + float phiOriginal = -999.; + + float chi2 = track.getChi2(); + + float ip[2]; + track.getImpactParams(0, 0, 0, 0, ip); + + // float phiTrack = trackParCov.getPhi(); // * 180 / M_PI; + + // applying the cuts on the track - only eta + if (eta < mEtaCuts[0] || eta >= mEtaCuts[1]) { + continue; + } + + int firstClus = track.getFirstClusterEntry(); // get the first cluster of the track + int ncl = track.getNumberOfClusters(); // get the number of clusters of the track + + //// keeping only 7 clusters track to reduce fakes + if (ncl < 7) { + continue; + } + + o2::MCCompLabel tracklab; + if (isMC) { + tracklab = mTracksMCLabels[iTrack]; + if (tracklab.isFake()) { + continue; + } + } + + if (mVerboseOutput && isMC) { + LOGP(info, "track Label: "); + tracklab.print(); + } + + for (int iclTrack = firstClus; iclTrack < firstClus + ncl; iclTrack++) { // loop on clusters associated to the track to extract layer, stave and chip to restrict the possible matches to be searched with the DCA cut + auto& clusOriginal = mClusters[mInputITSidxs[iclTrack]]; + auto clusOriginalPoint = mITSClustersArray[mInputITSidxs[iclTrack]]; + auto layerOriginal = mGeometry->getLayer(clusOriginal.getSensorID()); + + UShort_t rowOriginal = clusOriginal.getRow(); + UShort_t colOriginal = clusOriginal.getCol(); + + /// filling some chip maps + if (clusOriginal.getChipID() >= 0 && clusOriginal.getChipID() <= 8) { + l0_00->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + if (clusOriginal.getChipID() >= 252 && clusOriginal.getChipID() <= 260) { + l1_15->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + if (clusOriginal.getChipID() >= 423 && clusOriginal.getChipID() <= 431) { + l2_19->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusOriginal.getRow()); + } + + //// only IB + if (layerOriginal >= NLAYERS) { + continue; + } + + chipmap->Fill(clusOriginal.getCol(), clusOriginal.getRow()); + + IPOriginalxy[layerOriginal]->Fill(ip[0]); + IPOriginalz[layerOriginal]->Fill(ip[1]); + + ///// cluster point and conversion from track local coordinates to global coordinates + o2::math_utils::Point3D clusOriginalPointTrack = {clusOriginalPoint.getX(), clusOriginalPoint.getY(), clusOriginalPoint.getZ()}; + o2::math_utils::Point3D clusOriginalPointGlob = mGeometry->getMatrixT2G(clusOriginal.getSensorID()) * clusOriginalPointTrack; + phiOriginal = clusOriginalPointGlob.phi(); // * 180 / M_PI; + + if (abs(clusOriginalPointGlob.y()) < 0.5) { ///// excluding gap between bottom and top barrels + continue; + } + + if (abs(clusOriginalPointGlob.z()) >= 10) { /// excluding external z + continue; + } + + if (rowOriginal < 2 || (rowOriginal > 15 && rowOriginal < 496) || rowOriginal > 509) { //// cutting on the row + continue; + } + + if (mUseMC) { //// excluding known bad chips in MC which are not bad in data --- to be checked based on the anchored run + if (std::find(mExcludedChipMC.begin(), mExcludedChipMC.end(), clusOriginal.getChipID()) != mExcludedChipMC.end()) { + continue; + } + } + + if (clusOriginal.getCol() < 160 || clusOriginal.getCol() > 870) { /// excluding the gap between two chips in the same stave (comment to obtain the plot efficiency col vs eta) + continue; + } + + /// if the track passes the cuts, fill the den and go ahead + m2DClusterOriginalPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + m3DClusterPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y(), clusOriginalPointGlob.z()); + chi2trackAccepted->Fill(chi2); + denPt[layerOriginal]->Fill(pt); + denPhi[layerOriginal]->Fill(phiOriginal); + denEta[layerOriginal]->Fill(eta); + denRow[layerOriginal]->Fill(rowOriginal); + denCol[layerOriginal]->Fill(clusOriginal.getCol()); + denZ[layerOriginal]->Fill(clusOriginalPointGlob.z()); + nTracksSelected[layerOriginal]++; + mDenColEta[layerOriginal]->Fill(clusOriginal.getCol(), eta); + mDenRowPhi[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginalPointGlob.z()); + mDenRowCol[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginal.getCol()); + denLayers->Fill(layerOriginal); + + /// if the cuts up to here are passed, then search for the duplicated cluster, otherwise go to the next cluster + gsl::span labsOriginal = {}; + if (isMC) { + labsOriginal = mClustersMCLCont->getLabels(mInputITSidxs[iclTrack]); // get labels of the cluster associated to the track (original) + } + + auto staveOriginal = mGeometry->getStave(clusOriginal.getSensorID()); + auto chipOriginal = mGeometry->getChipIdInStave(clusOriginal.getSensorID()); + + std::tuple> clusID_rDCA_label = {0, 999., gsl::span()}; // inizializing tuple with dummy values (if data, ignore the third value) + + bool adjacentFound = 0; + float phiDuplicated = -999.; + float ptDuplicated = -999.; + float etaDuplicated = -999.; + float clusZ = -999.; + + o2::itsmft::CompClusterExt clusDuplicatedSelected = o2::itsmft::CompClusterExt(); + + /// for each original cluster iterate over all the possible duplicated clusters to select the "adjacent" clusters (stave +-1, chip =,+-1) and calculate the DCA with the track. Then choose the closest one. + for (unsigned int iClus = rofIndexClus; iClus < rofIndexClus + rofNEntriesClus; iClus++) { // iteration over ALL the clusters in the ROF + auto clusDuplicated = mClusters[iClus]; + auto clusDuplicatedPoint = mITSClustersArray[iClus]; + + o2::math_utils::Point3D clusDuplicatedPointTrack = {clusDuplicatedPoint.getX(), clusDuplicatedPoint.getY(), clusDuplicatedPoint.getZ()}; + o2::math_utils::Point3D clusDuplicatedPointGlob = mGeometry->getMatrixT2G(clusDuplicated.getSensorID()) * clusDuplicatedPointTrack; + phi = clusDuplicatedPointGlob.phi(); // * 180 / M_PI; + + //// applying constraints: the cluster should be on the same layer, should be on an adjacent stave and on the same or adjacent chip position + if (clusDuplicated.getSensorID() == clusOriginal.getSensorID()) { + continue; + } + auto layerDuplicated = mGeometry->getLayer(clusDuplicated.getSensorID()); + if (layerDuplicated != layerOriginal) { + continue; + } + auto staveDuplicated = mGeometry->getStave(clusDuplicated.getSensorID()); + if (abs(staveDuplicated - staveOriginal) != 1) { + continue; + } + auto chipDuplicated = mGeometry->getChipIdInStave(clusDuplicated.getSensorID()); + if (abs(chipDuplicated - chipOriginal) > 1) { + continue; + } + + gsl::span labsDuplicated = {}; + if (isMC) { + labsDuplicated = mClustersMCLCont->getLabels(iClus); + } + + /// if the cheks are passed, then calculate the DCA + /// Compute the DCA between the duplicated cluster location and the track + trackParCov.rotate(mGeometry->getSensorRefAlpha(clusDuplicated.getSensorID())); + if (!propagator->propagateToDCA(clusDuplicatedPointGlob, trackParCov, b, 2.f, matCorr, &clusDuplicatedDCA)) { // check if the propagation fails + continue; + } + + DCAxyData[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + DCAzData[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + + // Imposing that the distance between the duplicated cluster and the track is less than x sigma + if (!(clusDuplicatedDCA[0] > mDCACutsXY[layerDuplicated][0] && clusDuplicatedDCA[0] < mDCACutsXY[layerDuplicated][1] && clusDuplicatedDCA[1] > mDCACutsZ[layerDuplicated][0] && clusDuplicatedDCA[1] < mDCACutsZ[layerDuplicated][1])) { + DCAxyRejected[layerDuplicated]->Fill(clusDuplicatedDCA[0]); + DCAzRejected[layerDuplicated]->Fill(clusDuplicatedDCA[1]); + continue; + } + + m2DClusterDuplicatedPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y()); + m3DDuplicatedClusterPositions->Fill(clusDuplicatedPointGlob.x(), clusDuplicatedPointGlob.y(), clusDuplicatedPointGlob.z()); + + if (mVerboseOutput) { + LOGP(info, "Propagation ok"); + } + double rDCA = std::hypot(clusDuplicatedDCA[0], clusDuplicatedDCA[1]); + + // taking the closest cluster within x sigma + if (rDCA < std::get<1>(clusID_rDCA_label)) { // updating the closest cluster + if (isMC) { + clusID_rDCA_label = {iClus, rDCA, labsDuplicated}; + } else { + clusID_rDCA_label = {iClus, rDCA, gsl::span()}; + } + phiDuplicated = phiOriginal; + ptDuplicated = pt; + etaDuplicated = eta; + clusZ = clusOriginalPointGlob.z(); + clusDuplicatedSelected = clusDuplicated; + } + adjacentFound = 1; + } // end loop on all the clusters in the rof -> at this point we have the information on the closest cluster (if there is one) + + // here clusID_rDCA_label is updated with the closest cluster to the track other than the original one + + if (!adjacentFound) { + radiusNotFound[layerOriginal]->Fill(sqrt(clusOriginalPointGlob.x() * clusOriginalPointGlob.x() + clusOriginalPointGlob.y() * clusOriginalPointGlob.y())); + colNotFound[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9))); + rowNotFound[layerOriginal]->Fill(rowOriginal); + zNotFound[layerOriginal]->Fill(clusOriginalPointGlob.z()); + phiNotFound[layerOriginal]->Fill(phiOriginal); + continue; + } + + chipOrigVsOverlap->Fill(clusOriginal.getChipID() % 9, clusDuplicatedSelected.getChipID() % 9); + mChipFound->Fill(clusOriginal.getChipID()); + zFound[layerOriginal]->Fill(clusOriginalPointGlob.z()); + radiusFound[layerOriginal]->Fill(sqrt(clusOriginalPointGlob.x() * clusOriginalPointGlob.x() + clusOriginalPointGlob.y() * clusOriginalPointGlob.y())); + colFoundOriginalVsDuplicated[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9)), clusDuplicatedSelected.getCol() + (1024 * (clusDuplicatedSelected.getChipID() % 9))); + colFoundOriginal[layerOriginal]->Fill(clusOriginal.getCol() + (1024 * (clusOriginal.getChipID() % 9))); + m2DClusterFoundPositions->Fill(clusOriginalPointGlob.x(), clusOriginalPointGlob.y()); + phiFound[layerOriginal]->Fill(phiOriginal); + rowFound[layerOriginal]->Fill(rowOriginal); + nDuplClusters++; + nDuplicatedClusters[layerOriginal]++; + numPt[layerOriginal]->Fill(pt); + numPhi[layerOriginal]->Fill(phiDuplicated); + numEta[layerOriginal]->Fill(etaDuplicated); + numRow[layerOriginal]->Fill(rowOriginal); + numCol[layerOriginal]->Fill(clusOriginal.getCol()); + numZ[layerOriginal]->Fill(clusOriginalPointGlob.z()); + mZvsPhiDUplicated[layerOriginal]->Fill(clusZ, phiDuplicated); + mNumColEta[layerOriginal]->Fill(clusOriginal.getCol(), eta); + mNumRowPhi[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginalPointGlob.z()); + mNumRowCol[layerOriginal]->Fill(clusOriginal.getRow(), clusOriginal.getCol()); + numLayers->Fill(layerOriginal); + + // checking if it is a good or fake match looking at the labels (only if isMC) + if (isMC) { + bool isGood = false; + for (auto lab : std::get<2>(clusID_rDCA_label)) { + if (lab == tracklab) { + isGood = true; + numPtGood[layerOriginal]->Fill(ptDuplicated); + numPhiGood[layerOriginal]->Fill(phiDuplicated); + numEtaGood[layerOriginal]->Fill(etaDuplicated); + numRowGood[layerOriginal]->Fill(rowOriginal); + numColGood[layerOriginal]->Fill(clusOriginal.getCol()); + numZGood[layerOriginal]->Fill(clusOriginalPointGlob.z()); + numGoodLayers->Fill(layerOriginal); + continue; + } + } + if (!isGood) { + numPtFake[layerOriginal]->Fill(ptDuplicated); + numPhiFake[layerOriginal]->Fill(phiDuplicated); + numEtaFake[layerOriginal]->Fill(etaDuplicated); + numRowFake[layerOriginal]->Fill(rowOriginal); + numColFake[layerOriginal]->Fill(clusOriginal.getCol()); + numZFake[layerOriginal]->Fill(clusOriginalPointGlob.z()); + numFakeLayers->Fill(layerOriginal); + } + } + } // end loop on clusters associated to the track + totNClusters += NLAYERS; + } // end loop on tracks per ROF + } // end loop on ROFRecords array + + std::cout << " Num of duplicated clusters L0: " << nDuplicatedClusters[0] << " N tracks selected: " << nTracksSelected[0] << std::endl; + std::cout << " Num of duplicated clusters L1: " << nDuplicatedClusters[1] << " N tracks selected: " << nTracksSelected[1] << std::endl; + std::cout << " Num of duplicated clusters L2: " << nDuplicatedClusters[2] << " N tracks selected: " << nTracksSelected[2] << std::endl; + + std::cout << " --------- N total clusters: " << totNClusters << std::endl; + std::cout << " --------- N duplicated clusters: " << nDuplClusters << std::endl; +} + +void EfficiencyStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + LOGP(info, "--------------- process"); + + o2::base::GRPGeomHelper::instance().getGRPMagField()->print(); + + if (mUseMC) { + // getDCAClusterTrackMC(); + studyDCAcutsMC(); + // studyClusterSelectionMC(); + // countDuplicatedAfterCuts(); + getEfficiency(mUseMC); + } else { + getEfficiency(mUseMC); + } + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters\n\t", + mClustersROFRecords.size(), mClusters.size()); + + if (mUseMC) { + LOGP(info, "mClusters size: {}, mClustersROFRecords size: {}, mClustersMCLCont size: {}, mClustersconverted size: {} ", mClusters.size(), mClustersROFRecords.size(), mClustersMCLCont->getNElements(), mITSClustersArray.size()); + LOGP(info, "mTracks size: {}, mTracksROFRecords size: {}, mTracksMCLabels size: {}", mTracks.size(), mTracksROFRecords.size(), mTracksMCLabels.size()); + } else { + LOGP(info, "mClusters size: {}, mClustersROFRecords size: {}, mClustersconverted size: {} ", mClusters.size(), mClustersROFRecords.size(), mITSClustersArray.size()); + LOGP(info, "mTracks size: {}, mTracksROFRecords size: {}", mTracks.size(), mTracksROFRecords.size()); + } +} + +void EfficiencyStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G, o2::math_utils::TransformType::L2G)); + } +} + +void EfficiencyStudy::endOfStream(EndOfStreamContext& ec) +{ + LOGP(info, "--------------- endOfStream"); + + mOutFile->mkdir("EfficiencyFinal/"); + mOutFile->mkdir("DCAFinal/"); + mOutFile->mkdir("NotFoundChecks/"); + + mOutFile->mkdir("DCA/"); + mOutFile->mkdir("Pt_Eta_Phi/"); + + if (mUseMC) { + + mOutFile->cd("DCA"); + mDCAxyDuplicated->Write(); + mDCAzDuplicated->Write(); + for (int i = 0; i < NLAYERS; i++) { + mDCAxyDuplicated_layer[i]->Write(); + mDCAzDuplicated_layer[i]->Write(); + + mDCAxyOriginal[i]->Write(); + mDCAzOriginal[i]->Write(); + } + + mOutFile->cd("Pt_Eta_Phi/"); + for (int i = 0; i < NLAYERS; i++) { + mDuplicatedPhiAllPt[i]->Write(); + mPtDuplicated[i]->Write(); + mEtaDuplicated[i]->Write(); + mPhiDuplicated[i]->Write(); + mPhiOriginalIfDuplicated[i]->Write(); + mDuplicatedPt[i]->Write(); + mDuplicatedPtEta[i]->Write(); + mDuplicatedPtPhi[i]->Write(); + mDuplicatedEtaPhi[i]->Write(); + mDuplicatedEtaAllPt[i]->Write(); + mDuplicatedRow[i]->Write(); + mDuplicatedCol[i]->Write(); + mDuplicatedZ[i]->Write(); + + for (int p = 0; p < 3; p++) { + mDuplicatedEta[i][p]->Write(); + mDuplicatedPhi[i][p]->Write(); + } + mPt_EtaDupl[i]->Write(); + } + } + + mOutFile->cd("Pt_Eta_Phi/"); + for (int i = 0; i < NLAYERS; i++) { + IPOriginalxy[i]->Write(); + IPOriginalz[i]->Write(); + mPhiOriginal[i]->Write(); + mPtOriginal[i]->Write(); + mEtaOriginal[i]->Write(); + mZvsPhiDUplicated[i]->Write(); + chipRowDuplicated[i]->Write(); + chipRowOriginalIfDuplicated[i]->Write(); + } + + mOutFile->mkdir("chi2"); + mOutFile->cd("chi2/"); + + chi2trackAccepted->Write(); + + mOutFile->cd("EfficiencyFinal/"); + TList listNum; + TList listDen; + auto numPhiAll = std::unique_ptr((TH1D*)numPhi[0]->Clone("numPhiAll")); + auto denPhiAll = std::unique_ptr((TH1D*)denPhi[0]->Clone("denPhiAll")); + + TList listNumColEta; + TList listDenColEta; + auto numColEtaAll = std::unique_ptr((TH1D*)mNumColEta[0]->Clone("numColEtaAll")); + auto denColEtaAll = std::unique_ptr((TH1D*)mDenColEta[0]->Clone("denColEtaAll")); + + TList listNumRowPhi; + TList listDenRowPhi; + auto numRowPhiAll = std::unique_ptr((TH1D*)mNumRowPhi[0]->Clone("numRowPhiAll")); + auto denRowPhiAll = std::unique_ptr((TH1D*)mDenRowPhi[0]->Clone("denRowPhiAll")); + + TList listNumRowCol; + TList listDenRowCol; + auto numRowColAll = std::unique_ptr((TH1D*)mNumRowCol[0]->Clone("numRowColAll")); + auto denRowColAll = std::unique_ptr((TH1D*)mDenRowCol[0]->Clone("denRowColAll")); + + std::unique_ptr effLayers = std::make_unique(*numLayers, *denLayers); + effLayers->SetName("effLayers"); + effLayers->SetTitle("; ;Efficiency"); + std::unique_ptr effLayersGood = std::make_unique(*numGoodLayers, *denLayers); + effLayersGood->SetName("effLayersGood"); + effLayersGood->SetTitle("; ;Efficiency Good Matches"); + std::unique_ptr effLayersFake = std::make_unique(*numFakeLayers, *denLayers); + effLayersFake->SetName("effLayersFake"); + effLayersFake->SetTitle("; ;Efficiency Fake Matches"); + effLayers->Write(); + effLayersGood->Write(); + effLayersFake->Write(); + denLayers->Write(); + numLayers->Write(); + numGoodLayers->Write(); + numFakeLayers->Write(); + + for (int l = 0; l < NLAYERS; l++) { + + std::unique_ptr effPt = std::make_unique(*numPt[l], *denPt[l]); + effPt->SetName(Form("effPt_layer%d", l)); + effPt->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency", l)); + std::unique_ptr effPtGood = std::make_unique(*numPtGood[l], *denPt[l]); + effPtGood->SetName(Form("effPtGood_layer%d", l)); + effPtGood->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency Good Matches", l)); + std::unique_ptr effPtFake = std::make_unique(*numPtFake[l], *denPt[l]); + effPtFake->SetName(Form("effPtFake_layer%d", l)); + effPtFake->SetTitle(Form("L%d;p_{T} (GeV/c);Efficiency Fake Matches", l)); + effPt->Write(); + effPtGood->Write(); + effPtFake->Write(); + + std::unique_ptr effPhi = std::make_unique(*numPhi[l], *denPhi[l]); + effPhi->SetName(Form("effPhi_layer%d", l)); + effPhi->SetTitle(Form("L%d;#phi;Efficiency", l)); + std::unique_ptr effPhiGood = std::make_unique(*numPhiGood[l], *denPhi[l]); + effPhiGood->SetName(Form("effPhiGood_layer%d", l)); + effPhiGood->SetTitle(Form("L%d;#phi;Efficiency Good Matches", l)); + std::unique_ptr effPhiFake = std::make_unique(*numPhiFake[l], *denPhi[l]); + effPhiFake->SetName(Form("effPhiFake_layer%d", l)); + effPhiFake->SetTitle(Form("L%d;#phi;Efficiency Fake Matches", l)); + effPhi->Write(); + effPhiGood->Write(); + effPhiFake->Write(); + listNum.Add(numPhi[l].get()); + listDen.Add(denPhi[l].get()); + + std::unique_ptr effEta = std::make_unique(*numEta[l], *denEta[l]); + effEta->SetName(Form("effEta_layer%d", l)); + effEta->SetTitle(Form("L%d;#eta;Efficiency", l)); + std::unique_ptr effEtaGood = std::make_unique(*numEtaGood[l], *denEta[l]); + effEtaGood->SetName(Form("effEtaGood_layer%d", l)); + effEtaGood->SetTitle(Form("L%d;#eta;Efficiency Good Matches", l)); + std::unique_ptr effEtaFake = std::make_unique(*numEtaFake[l], *denEta[l]); + effEtaFake->SetName(Form("effEtaFake_layer%d", l)); + effEtaFake->SetTitle(Form("L%d;#eta;Efficiency Fake Matches", l)); + effEta->Write(); + effEtaGood->Write(); + effEtaFake->Write(); + + std::unique_ptr effRow = std::make_unique(*numRow[l], *denRow[l]); + effRow->SetName(Form("effRow_layer%d", l)); + effRow->SetTitle(Form("L%d;#Row;Efficiency", l)); + std::unique_ptr effRowGood = std::make_unique(*numRowGood[l], *denRow[l]); + effRowGood->SetName(Form("effRowGood_layer%d", l)); + effRowGood->SetTitle(Form("L%d;#Row;Efficiency Good Matches", l)); + std::unique_ptr effRowFake = std::make_unique(*numRowFake[l], *denRow[l]); + effRowFake->SetName(Form("effRowFake_layer%d", l)); + effRowFake->SetTitle(Form("L%d;#Row;Efficiency Fake Matches", l)); + effRow->Write(); + effRowGood->Write(); + effRowFake->Write(); + + std::unique_ptr effCol = std::make_unique(*numCol[l], *denCol[l]); + effCol->SetName(Form("effCol_layer%d", l)); + effCol->SetTitle(Form("L%d;#Col;Efficiency", l)); + std::unique_ptr effColGood = std::make_unique(*numColGood[l], *denCol[l]); + effColGood->SetName(Form("effColGood_layer%d", l)); + effColGood->SetTitle(Form("L%d;#Col;Efficiency Good Matches", l)); + std::unique_ptr effColFake = std::make_unique(*numColFake[l], *denCol[l]); + effColFake->SetName(Form("effColFake_layer%d", l)); + effColFake->SetTitle(Form("L%d;#Col;Efficiency Fake Matches", l)); + effCol->Write(); + effColGood->Write(); + effColFake->Write(); + + std::unique_ptr effZ = std::make_unique(*numZ[l], *denZ[l]); + effZ->SetName(Form("effZ_layer%d", l)); + effZ->SetTitle(Form("L%d;#Z (cm);Efficiency", l)); + std::unique_ptr effZGood = std::make_unique(*numZGood[l], *denZ[l]); + effZGood->SetName(Form("effZGood_layer%d", l)); + effZGood->SetTitle(Form("L%d;#Z (cm);Efficiency Good Matches", l)); + std::unique_ptr effZFake = std::make_unique(*numZFake[l], *denZ[l]); + effZFake->SetName(Form("effZFake_layer%d", l)); + effZFake->SetTitle(Form("L%d;#Z (cm);Efficiency Fake Matches", l)); + effZ->Write(); + effZGood->Write(); + effZFake->Write(); + + std::unique_ptr effColEta = std::make_unique(*mNumColEta[l], *mDenColEta[l]); + effColEta->SetName(Form("effColEta_layer%d", l)); + effColEta->SetTitle(Form("L%d;Column;#eta", l)); + effColEta->Write(); + + listNumColEta.Add(mNumColEta[l].get()); + listDenColEta.Add(mDenColEta[l].get()); + + std::unique_ptr effRowPhi = std::make_unique(*mNumRowPhi[l], *mDenRowPhi[l]); + effRowPhi->SetName(Form("effRowPhi_layer%d", l)); + effRowPhi->SetTitle(Form("L%d;Column;#eta", l)); + effRowPhi->Write(); + + listNumRowPhi.Add(mNumRowPhi[l].get()); + listDenRowPhi.Add(mDenRowPhi[l].get()); + + std::unique_ptr effRowCol = std::make_unique(*mNumRowCol[l], *mDenRowCol[l]); + effRowCol->SetName(Form("effRowCol_layer%d", l)); + effRowCol->SetTitle(Form("L%d;Column;#eta", l)); + effRowCol->Write(); + + listNumRowCol.Add(mNumRowCol[l].get()); + listDenRowCol.Add(mDenRowCol[l].get()); + + mNumRowCol[l]->Write(); + mDenRowCol[l]->Write(); + mNumRowPhi[l]->Write(); + mDenRowPhi[l]->Write(); + mNumColEta[l]->Write(); + mDenColEta[l]->Write(); + numPhi[l]->Write(); + denPhi[l]->Write(); + numPt[l]->Write(); + denPt[l]->Write(); + numEta[l]->Write(); + denEta[l]->Write(); + numRow[l]->Write(); + denRow[l]->Write(); + numCol[l]->Write(); + denCol[l]->Write(); + numZ[l]->Write(); + denZ[l]->Write(); + } + numPhiAll->Merge(&listNum); + denPhiAll->Merge(&listDen); + + numColEtaAll->Merge(&listNumColEta); + denColEtaAll->Merge(&listDenColEta); + + numRowPhiAll->Merge(&listNumRowPhi); + denRowPhiAll->Merge(&listDenRowPhi); + + numRowColAll->Merge(&listNumRowCol); + denRowColAll->Merge(&listDenRowCol); + + std::unique_ptr effPhiAll = std::make_unique(*numPhiAll, *denPhiAll); + effPhiAll->SetName("effPhi_AllLayers"); + effPhiAll->SetTitle("L0 + L1 + L2;#phi;Efficiency"); + effPhiAll->Write(); + numPhiAll->Write(); + denPhiAll->Write(); + + std::unique_ptr effColEtaAll = std::make_unique(*numColEtaAll, *denColEtaAll); + effColEtaAll->SetName("effColEta_AllLayers"); + effColEtaAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effColEtaAll->Write(); + numColEtaAll->Write(); + denColEtaAll->Write(); + + std::unique_ptr effRowPhiAll = std::make_unique(*numRowPhiAll, *denRowPhiAll); + effRowPhiAll->SetName("effRowPhi_AllLayers"); + effRowPhiAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effRowPhiAll->Write(); + numRowPhiAll->Write(); + denRowPhiAll->Write(); + + std::unique_ptr effRowColAll = std::make_unique(*numRowColAll, *denRowColAll); + effRowColAll->SetName("effRowCol_AllLayers"); + effRowColAll->SetTitle("L0 + L1 + L2;Column;#eta"); + effRowColAll->Write(); + numRowColAll->Write(); + denRowColAll->Write(); + + mOutFile->cd("DCAFinal/"); + + for (int l = 0; l < NLAYERS; l++) { + DCAxyData[l]->Write(); + DCAzData[l]->Write(); + DCAxyRejected[l]->Write(); + DCAzRejected[l]->Write(); + } + + mOutFile->cd("NotFoundChecks/"); + + for (int l = 0; l < NLAYERS; l++) { + phiFound[l]->Write(); + phiNotFound[l]->Write(); + rowFound[l]->Write(); + rowNotFound[l]->Write(); + zFound[l]->Write(); + zNotFound[l]->Write(); + radiusFound[l]->Write(); + radiusNotFound[l]->Write(); + colFoundOriginalVsDuplicated[l]->Write(); + colFoundOriginal[l]->Write(); + colNotFound[l]->Write(); + } + mChipFound->Write(); + mChipNotFound->Write(); + m2DClusterFoundPositions->Write(); + l0_00->Write(); + l1_15->Write(); + l2_19->Write(); + chipOrigVsOverlap->Write(); + chipmap->SetContour(100); + chipmap->Write(); + + mOutFile->Close(); +} + +void EfficiencyStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + std::cout << "-------- finaliseCCDB" << std::endl; + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +DataProcessorSpec getEfficiencyStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-efficiency-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, useMC, kineReader, ggRequest)}, + Options{}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx new file mode 100644 index 0000000000000..a5b3495047934 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx @@ -0,0 +1,224 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +// o2 includes +#include "ITSStudies/Helpers.h" +#include "TLegend.h" + +using namespace o2::its::study; + +//______________________________________________________________________________ +std::vector helpers::makeLogBinning(const int nbins, const double min, const double max) +{ + assert(min > 0); + assert(min < max); + + std::vector binLim(nbins + 1); + + const double expMax = std::log(max / min); + const double binWidth = expMax / nbins; + + binLim[0] = min; + binLim[nbins] = max; + + for (Int_t i = 1; i < nbins; ++i) { + binLim[i] = min * std::exp(i * binWidth); + } + + return binLim; +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1D(TH1F& histo, int color) +{ + // common and 1D case + histo.SetStats(0); + histo.SetMinimum(20); + histo.SetMaximum(300); + histo.SetLineColor(color); + histo.SetLineWidth(1); + histo.SetMarkerColor(color); + histo.SetMarkerSize(0.05); + histo.SetMarkerStyle(1); + histo.SetTitle(""); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1D(TH1F& histo, int color, TString title, TString titleYaxis, TString titleXaxis) +{ + // common and 1D case + helpers::setStyleHistogram1D(histo, color); + histo.GetXaxis()->SetTitle(titleXaxis.Data()); + histo.GetYaxis()->SetTitle(titleYaxis.Data()); + histo.GetXaxis()->SetTitleOffset(1.4); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram1DMeanValues(TH1F& histo, int color) +{ + // common and 1D case + helpers::setStyleHistogram1D(histo, color); + histo.SetMinimum(-15.); + histo.SetMaximum(15.); + histo.GetXaxis()->SetTitleOffset(1.4); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +void helpers::setStyleHistogram2D(TH2F& histo) +{ + // common and 1D case + histo.SetStats(0); + histo.GetYaxis()->SetRangeUser(-600, 600); + histo.GetXaxis()->SetTitleOffset(1.2); + histo.GetYaxis()->SetTitleOffset(1.2); +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TH1F& h2, int color2) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogy(); + c1->SetGridy(); + c1->SetLogx(); + c1->SetGridx(); + setStyleHistogram1D(h1, color1); + helpers::setStyleHistogram1D(h2, color2); + h1.Draw(); + h2.Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, bool logScale) +{ + TCanvas* c1 = new TCanvas(); + if (logScale) { + c1->SetLogy(); // c1->SetGridy(); + c1->SetLogx(); // c1->SetGridx(); + } + TString direction = ""; + TString histoName1 = h1.GetName(); + TString histoName2 = h2.GetName(); + if (histoName1.Contains("Xy")) { + direction = "XY"; + } + if (histoName1.Contains("Z")) { + direction = "Z"; + } + if ((histoName1.Contains("Xy")) && (histoName2.Contains("Z"))) { + direction = ""; + } + helpers::setStyleHistogram1D(h1, color1, "", Form("Pointing Resolution %s (#mum)", direction.Data()), h1.GetXaxis()->GetName()); + helpers::setStyleHistogram1D(h2, color2, "", Form("Pointing Resolution %s (#mum)", direction.Data()), h2.GetXaxis()->GetName()); + TLegend* leg = new TLegend(0.6, 0.3, 0.8, 0.5); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2Histograms(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2, TString intRate) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogy(); // c1->SetGridy(); + c1->SetLogx(); // c1->SetGridx(); + helpers::setStyleHistogram1D(h1, color1); + helpers::setStyleHistogram1D(h2, color2); + TLegend* leg = new TLegend(0.2, 0.3, 0.5, 0.5); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + TPaveText* paveText; + if (!intRate) { + helpers::paveTextITS(paveText, intRate); + } + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::prepareSimpleCanvas2DcaMeanValues(TH1F& h1, int color1, TString nameHisto1, TH1F& h2, int color2, TString nameHisto2) +{ + TCanvas* c1 = new TCanvas(); + c1->SetLogx(); + helpers::setStyleHistogram1DMeanValues(h1, color1); + helpers::setStyleHistogram1DMeanValues(h2, color2); + TLegend* leg = new TLegend(0.2, 0.15, 0.5, 0.35); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&h1, nameHisto1.Data(), "lp"); + leg->AddEntry(&h2, nameHisto2.Data(), "lp"); + h1.Draw(); + h2.Draw("same"); + leg->Draw("same"); + return c1; +} + +//______________________________________________________________________________ +TCanvas* helpers::plot2DwithMeanAndSigma(TH2F& h2D, TH1F& hMean, TH1F& hSigma, int color) +{ + TCanvas* c1 = new TCanvas(); + helpers::setStyleHistogram2D(h2D); + helpers::setStyleHistogram1D(hSigma, color); + h2D.Draw("colz"); + hMean.Draw("same"); + TGraphAsymmErrors* gSigma; + ConvertTH1ToTGraphAsymmError(hMean, hSigma, gSigma); + gSigma->SetLineColor(kRed); + gSigma->SetFillStyle(0); + gSigma->Draw("E2same"); + return c1; +} + +//______________________________________________________________________________ +void helpers::paveTextITS(TPaveText* pave, TString intRate) +{ + pave->SetFillStyle(0); + pave->SetBorderSize(0); + pave->SetFillColor(0); + pave->SetTextFont(53); + pave->SetTextSize(12); + pave->AddText("ALICE"); + pave->AddText("Run3 ITS Performaces"); + pave->AddText(Form("Interaction Rate = %s", intRate.Data())); +} + +//______________________________________________________________________________ +void helpers::ConvertTH1ToTGraphAsymmError(TH1F& hMean, TH1F& hSigma, TGraphAsymmErrors*& gr) +{ + const Int_t nbinsxx = hMean.GetNbinsX() + 1; + Double_t x[nbinsxx], y[nbinsxx], ex1[nbinsxx], ex2[nbinsxx], ey1[nbinsxx], ey2[nbinsxx]; + + for (int i = 0; i < nbinsxx; i++) { + x[i] = hMean.GetBinCenter(i); + y[i] = hMean.GetBinContent(i); + ex1[i] = hMean.GetBinCenter(i) - hMean.GetBinLowEdge(i); + ex2[i] = ex1[i]; + ey1[i] = hSigma.GetBinContent(i); + ey2[i] = hSigma.GetBinContent(i); + } + + gr = new TGraphAsymmErrors(nbinsxx, x, y, ex1, ex2, ey1, ey2); + return; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx new file mode 100644 index 0000000000000..c0b2d2863f3cc --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx @@ -0,0 +1,36 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/ITSStudiesConfigParam.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +static auto& sAvgClusSizeParamITS = o2::its::study::ITSAvgClusSizeParamConfig::Instance(); +static auto& sPIDStudyParamITS = o2::its::study::PIDStudyParamConfig::Instance(); +static auto& sCheckTracksParamsITS = o2::its::study::ITSCheckTracksParamConfig::Instance(); +static auto& sImpactParameterParamsITS = o2::its::study::ITSImpactParameterParamConfig::Instance(); +static auto& sAnomalyStudy = o2::its::study::AnomalyStudyParamConfig::Instance(); +static auto& sEfficiencyParamsITS = o2::its::study::ITSEfficiencyParamConfig::Instance(); + +O2ParamImpl(o2::its::study::ITSAvgClusSizeParamConfig); +O2ParamImpl(o2::its::study::PIDStudyParamConfig); +O2ParamImpl(o2::its::study::ITSCheckTracksParamConfig); +O2ParamImpl(o2::its::study::ITSImpactParameterParamConfig); +O2ParamImpl(o2::its::study::AnomalyStudyParamConfig); +O2ParamImpl(o2::its::study::ITSEfficiencyParamConfig); + +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h new file mode 100644 index 0000000000000..d56d718390b47 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::its::study::ITSAvgClusSizeParamConfig + ; +#pragma link C++ class o2::its::study::PIDStudyParamConfig + ; +#pragma link C++ class o2::its::study::ITSImpactParameterParamConfig + ; +#pragma link C++ class o2::its::study::AnomalyStudyParamConfig + ; +#pragma link C++ class o2::its::study::ITSEfficiencyParamConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSAvgClusSizeParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::PIDStudyParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSCheckTracksParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSImpactParameterParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::AnomalyStudyParamConfig> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::ITSEfficiencyParamConfig> + ; +#pragma link C++ function o2::its::studies::makeLogBinning + ; + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx new file mode 100644 index 0000000000000..c0aaabddaca1b --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx @@ -0,0 +1,561 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Skeleton derived from RS's code in ITSOffStudy + +#include +#include +#include +#include +#include "ITSStudies/ImpactParameter.h" +#include "ITSStudies/TrackCuts.h" +#include "ITSStudies/Helpers.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/Task.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsVertexing/PVertexer.h" +#include "DetectorsBase/Propagator.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DataFormatsParameters/GRPECSObject.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "Framework/DeviceSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigParamRegistry.h" +#include +#include +#include +#include +#include +#include +#include "TGeoGlobalMagField.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; +using namespace o2::its::study; + +using DetID = o2::detectors::DetID; +using PVertex = o2::dataformats::PrimaryVertex; +using GTrackID = o2::dataformats::GlobalTrackID; + +class ImpactParameterStudy : public Task +{ + public: + ImpactParameterStudy(std::shared_ptr dr, + std::shared_ptr gr, + mask_t src, + bool useMC) : mDataRequest(dr), + mGGCCDBRequest(gr), + mTracksSrc(src), + mUseMC(useMC) {} + ~ImpactParameterStudy() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void process(o2::globaltracking::RecoContainer&); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + void saveHistograms(); + void plotHistograms(); + std::shared_ptr mGGCCDBRequest; + GTrackID::mask_t mTracksSrc{}; + bool mUseMC{false}; ///< MC flag + o2::vertexing::PVertexer mVertexer; + float mITSROFrameLengthMUS = 0.; + float mITSROFBiasMUS = 0.; + std::unique_ptr mDBGOut; + // output histograms + std::unique_ptr mHistoContributorsPV{}; + std::unique_ptr mHistoTrackType; + std::unique_ptr mHistoTrackTypeRej; + std::unique_ptr mHistoTrackTypeAcc; + std::unique_ptr mHistoXPvVsRefitted{}; + std::unique_ptr mHistoYPvVsRefitted{}; + std::unique_ptr mHistoZPvVsRefitted{}; + std::unique_ptr mHistoXDeltaPVrefit{}; + std::unique_ptr mHistoYDeltaPVrefit{}; + std::unique_ptr mHistoZDeltaPVrefit{}; + std::unique_ptr mHistoImpParXy{}; + std::unique_ptr mHistoImpParZ{}; + std::unique_ptr mHistoImpParXyPhi{}; + std::unique_ptr mHistoImpParZPhi{}; + std::unique_ptr mHistoImpParXyTop{}; + std::unique_ptr mHistoImpParZTop{}; + std::unique_ptr mHistoImpParXyBottom{}; + std::unique_ptr mHistoImpParZBottom{}; + std::unique_ptr mHistoImpParXyPositiveCharge{}; + std::unique_ptr mHistoImpParZPositiveCharge{}; + std::unique_ptr mHistoImpParXyNegativeCharge{}; + std::unique_ptr mHistoImpParZNegativeCharge{}; + std::unique_ptr mHistoImpParXyMeanPhi{}; + std::unique_ptr mHistoImpParZMeanPhi{}; + std::unique_ptr mHistoImpParXySigmaPhi{}; + std::unique_ptr mHistoImpParZSigmaPhi{}; + std::unique_ptr mHistoImpParXySigma{}; + std::unique_ptr mHistoImpParZSigma{}; + std::unique_ptr mHistoImpParXySigmaTop{}; + std::unique_ptr mHistoImpParZSigmaTop{}; + std::unique_ptr mHistoImpParXySigmaBottom{}; + std::unique_ptr mHistoImpParZSigmaBottom{}; + std::unique_ptr mHistoImpParXyMeanTop{}; + std::unique_ptr mHistoImpParZMeanTop{}; + std::unique_ptr mHistoImpParXyMeanBottom{}; + std::unique_ptr mHistoImpParZMeanBottom{}; + std::unique_ptr mHistoImpParXySigmaPositiveCharge{}; + std::unique_ptr mHistoImpParZSigmaPositiveCharge{}; + std::unique_ptr mHistoImpParXySigmaNegativeCharge{}; + std::unique_ptr mHistoImpParZSigmaNegativeCharge{}; + + // output file + TString mOutName{}; + + // Data + std::shared_ptr mDataRequest; + gsl::span mPVertices; +}; + +void ImpactParameterStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + auto& params = ITSImpactParameterParamConfig::Instance(); + mOutName = params.outFileName; + mDBGOut = std::make_unique(mOutName.Data(), "recreate"); + + std::vector logPtBinning = helpers::makeLogBinning(100, 0.1, 10); + mHistoTrackType = std::make_unique("trackType", "# Track Type", 32, -0.5, 31.5); + mHistoTrackTypeRej = std::make_unique("trackTypeRej", "# Rejected Track Type", 32, -0.5, 31.5); + mHistoTrackTypeAcc = std::make_unique("trackTypeAcc", "# Filtered Track Type", 32, -0.5, 31.5); + mHistoContributorsPV = std::make_unique("nContribPVrefit", "# Contributors per PV", 100, 0, 100); + mHistoXPvVsRefitted = std::make_unique("histo2dXPvVsPVrefit", "#X PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoYPvVsRefitted = std::make_unique("histo2dYPvVsPVrefit", "#Y PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoZPvVsRefitted = std::make_unique("histo2dZPvVsPVrefit", "#Z PV vs PV_{-1}, #mum", 100, -10, 10, 100, -10, 10); + mHistoXDeltaPVrefit = std::make_unique("histoDeltaXPVrefit", "#DeltaX (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoYDeltaPVrefit = std::make_unique("histoDeltaYPVrefit", "#DeltaY (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoZDeltaPVrefit = std::make_unique("histoDeltaZPVrefit", "#DeltaZ (PV-PV_{-1}), #mum", 300, -15, 15); + mHistoImpParXyPhi = std::make_unique("histoImpParXyPhi", "#Phi; #phi; Impact Parameter XY (#mum);", 100, 0., 6.28, 100, -1000, 1000); + mHistoImpParZPhi = std::make_unique("histoImpParZPhi", "#Phi; #phi; Impact Parameter Z (#mum);", 100, 0., 6.28, 100, -1000, 1000); + mHistoImpParZ = std::make_unique("histoImpParZ", "Impact Parameter Z; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXy = std::make_unique("histoImpParXy", "Impact Parameter XY; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyTop = std::make_unique("histoImpParXyTop", "Impact Parameter XY, #phi(track)<#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyBottom = std::make_unique("histoImpParXyBottom", "Impact Parameter XY, #phi(track)>#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZTop = std::make_unique("histoImpParZTop", "Impact Parameter Z, #phi(track)<#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZBottom = std::make_unique("histoImpParZBottom", "Impact Parameter Z, #phi(track)>#pi; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyNegativeCharge = std::make_unique("histoImpParXyNegativeCharge", "Impact Parameter XY, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParXyPositiveCharge = std::make_unique("histoImpParXyPositiveCharge", "Impact Parameter XY, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZNegativeCharge = std::make_unique("histoImpParZNegativeCharge", "Impact Parameter Z, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + mHistoImpParZPositiveCharge = std::make_unique("histoImpParZPositiveCharge", "Impact Parameter Z, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data(), 100, -1000, 1000); + + mHistoImpParXySigma = std::make_unique("histoImpParXySigma", "Pointing Resolution XY; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigma = std::make_unique("histoImpParZSigma", "Pointing Resolution Z; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanPhi = std::make_unique("histoImpParXyMean", "Pointing Resolution XY; #phi; Mean #mum", 100, 0., 6.28); + mHistoImpParZMeanPhi = std::make_unique("histoImpParZMean", "Pointing Resolution Z; #phi; Mean #mum", 100, 0., 6.28); + mHistoImpParXySigmaPhi = std::make_unique("histoImpParXySigmaPhi", "Pointing Resolution XY; #phi; #sigma #mum", 100, 0., 6.28); + mHistoImpParZSigmaPhi = std::make_unique("histoImpParZSigmaPhi", "Pointing Resolution Z; #phi; #sigma #mum", 100, 0., 6.28); + mHistoImpParXySigmaTop = std::make_unique("histoImpParXySigmaTop", "Pointing Resolution XY, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaTop = std::make_unique("histoImpParZSigmaTop", "Pointing Resolution Z, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaBottom = std::make_unique("histoImpParXySigmaBottom", "Pointing Resolution XY, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaBottom = std::make_unique("histoImpParZSigmaBottom", "Pointing Resolution Z, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanTop = std::make_unique("histoImpParXyMeanTop", "Pointing Resolution XY, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZMeanTop = std::make_unique("histoImpParZMeanTop", "Pointing Resolution Z, Top; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXyMeanBottom = std::make_unique("histoImpParXyMeanBottom", "Mean Pointing Resolution XY, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZMeanBottom = std::make_unique("histoImpParZMeanBottom", "Mean Pointing Resolution Z, Bottom; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaPositiveCharge = std::make_unique("histoImpParXySigmaPositiveCharge", "Pointing Resolution XY, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaPositiveCharge = std::make_unique("histoImpParZSigmaPositiveCharge", "Pointing Resolution Z, sign>0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParXySigmaNegativeCharge = std::make_unique("histoImpParXySigmaNegativeCharge", "Pointing Resolution XY, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + mHistoImpParZSigmaNegativeCharge = std::make_unique("histoImpParZSigmaNegativeCharge", "Pointing Resolution Z, sign<0; #it{p}_{T} (GeV/#it{c}); #mum", logPtBinning.size() - 1, logPtBinning.data()); + + mHistoTrackType->SetDirectory(nullptr); + mHistoTrackTypeRej->SetDirectory(nullptr); + mHistoTrackTypeAcc->SetDirectory(nullptr); + mHistoContributorsPV->SetDirectory(nullptr); + mHistoXPvVsRefitted->SetDirectory(nullptr); + mHistoYPvVsRefitted->SetDirectory(nullptr); + mHistoZPvVsRefitted->SetDirectory(nullptr); + mHistoXDeltaPVrefit->SetDirectory(nullptr); + mHistoYDeltaPVrefit->SetDirectory(nullptr); + mHistoZDeltaPVrefit->SetDirectory(nullptr); + mHistoImpParZ->SetDirectory(nullptr); + mHistoImpParXy->SetDirectory(nullptr); + mHistoImpParXyPhi->SetDirectory(nullptr); + mHistoImpParZPhi->SetDirectory(nullptr); + mHistoImpParXyTop->SetDirectory(nullptr); + mHistoImpParXyBottom->SetDirectory(nullptr); + mHistoImpParZTop->SetDirectory(nullptr); + mHistoImpParZBottom->SetDirectory(nullptr); + mHistoImpParXyNegativeCharge->SetDirectory(nullptr); + mHistoImpParXyPositiveCharge->SetDirectory(nullptr); + mHistoImpParZNegativeCharge->SetDirectory(nullptr); + mHistoImpParZPositiveCharge->SetDirectory(nullptr); + mHistoImpParXyMeanPhi->SetDirectory(nullptr); + mHistoImpParZMeanPhi->SetDirectory(nullptr); + mHistoImpParXySigmaPhi->SetDirectory(nullptr); + mHistoImpParZSigmaPhi->SetDirectory(nullptr); + mHistoImpParXySigma->SetDirectory(nullptr); + mHistoImpParZSigma->SetDirectory(nullptr); + mHistoImpParXySigmaTop->SetDirectory(nullptr); + mHistoImpParZSigmaTop->SetDirectory(nullptr); + mHistoImpParXySigmaBottom->SetDirectory(nullptr); + mHistoImpParZSigmaBottom->SetDirectory(nullptr); + mHistoImpParXyMeanTop->SetDirectory(nullptr); + mHistoImpParZMeanTop->SetDirectory(nullptr); + mHistoImpParXyMeanBottom->SetDirectory(nullptr); + mHistoImpParZMeanBottom->SetDirectory(nullptr); + mHistoImpParXySigmaPositiveCharge->SetDirectory(nullptr); + mHistoImpParZSigmaPositiveCharge->SetDirectory(nullptr); + mHistoImpParXySigmaNegativeCharge->SetDirectory(nullptr); + mHistoImpParZSigmaNegativeCharge->SetDirectory(nullptr); +} + +void ImpactParameterStudy::run(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); + process(recoData); +} + +void ImpactParameterStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + auto& params = ITSImpactParameterParamConfig::Instance(); + + o2::base::GRPGeomHelper::instance().getGRPMagField()->print(); + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::vector vecPvContributorTrackParCov; + std::vector vec_globID_contr = {}; + std::vector trueVecPvContributorTrackParCov; + std::vector trueVec_globID_contr = {}; + float impParRPhi, impParZ; + constexpr float toMicrometers = 10000.f; // Conversion from [cm] to [mum] + auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto pvertices = recoData.getPrimaryVertices(); + auto trmTPCTracks = recoData.getTPCTracks(); + + TrackCuts cuts; // ITS and TPC(commented) cut implementation + + int nv = vtxRefs.size() - 1; // The last entry is for unassigned tracks, ignore them + for (int iv = 0; iv < nv; iv++) { // Loop over PVs + LOGP(info, "*** NEW VERTEX {}***", iv); + const auto& vtref = vtxRefs[iv]; + const o2::dataformats::VertexBase& pv = pvertices[iv]; + int it = vtref.getFirstEntry(), itLim = it + vtref.getEntries(); + int i = 0; + + for (; it < itLim; it++) { + auto tvid = trackIndex[it]; + // LOGP(info,"ORIGIN: {}", tvid.getSourceName(tvid.getSource())); + mHistoTrackType->Fill(tvid.getSource()); + if (!recoData.isTrackSourceLoaded(tvid.getSource())) { + // LOGP(info,"SOURCE Rej: {}", tvid.getSourceName(tvid.getSource())); + mHistoTrackTypeRej->Fill(tvid.getSource()); + continue; + } + // LOGP(info,"ORIGIN: {} INDEX: {}", tvid.getSourceName(tvid.getSource()), trackIndex[it]); + mHistoTrackTypeAcc->Fill(tvid.getSource()); + const o2::track::TrackParCov& trc = recoData.getTrackParam(tvid); // The actual track + + auto refs = recoData.getSingleDetectorRefs(tvid); + if (!refs[GTrackID::ITS].isIndexSet()) { // might be an afterburner track + // LOGP(info, " ** AFTERBURN **"); + continue; + } + // Apply track selections + if (params.applyTrackCuts) { + if (!cuts.isSelected(tvid, recoData)) { + // LOGP(info, "Fail"); + continue; + } + } + // Vectors for reconstructing the vertex + vec_globID_contr.push_back(trackIndex[it]); + vecPvContributorTrackParCov.push_back(trc); + // Store vector with index and tracks ITS only -- same order as the GLOBAL vectors + int itsTrackID = refs[GTrackID::ITS].getIndex(); + const o2::track::TrackParCov& trcITS = recoData.getTrackParam(itsTrackID); // The actual ITS track + trueVec_globID_contr.push_back(itsTrackID); + trueVecPvContributorTrackParCov.push_back(trcITS); + // LOGP(info, "SOURCE: {} indexGLOBAL: {} indexITS: {}", tvid.getSourceName(tvid.getSource()), trackIndex[it], trueVec_globID_contr[i]); + i++; + } // end loop tracks + LOGP(info, "************ SIZE INDEX GLOBAL: {} ", vec_globID_contr.size()); + LOGP(info, "************ SIZE INDEX ITS: {} ", trueVec_globID_contr.size()); + + it = vtref.getFirstEntry(); + // Preparation PVertexer refit + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); + bool PVrefit_doable = mVertexer.prepareVertexRefit(vecPvContributorTrackParCov, pv); + if (!PVrefit_doable) { + LOG(info) << "Not enough tracks accepted for the refit --> Skipping vertex"; + } else { + mHistoContributorsPV->Fill(vecPvContributorTrackParCov.size()); + // Neglect vertices with small number of contributors (configurable) + if (vecPvContributorTrackParCov.size() < params.minNumberOfContributors) { + continue; + } + for (it = 0; it < vec_globID_contr.size(); it++) { + // vector of booleans to keep track of the track to be skipped + std::vector vec_useTrk_PVrefit(vec_globID_contr.size(), true); + auto tvid = vec_globID_contr[it]; + auto trackIterator = std::find(vec_globID_contr.begin(), vec_globID_contr.end(), vec_globID_contr[it]); + if (trackIterator != vec_globID_contr.end()) { + // LOGP(info,"************ Trackiterator: {} : {} ", it+1, vec_globID_contr[it]); + o2::dataformats::VertexBase PVbase_recalculated; + /// this track contributed to the PV fit: let's do the refit without it + const int entry = std::distance(vec_globID_contr.begin(), trackIterator); + if (!params.useAllTracks) { + vec_useTrk_PVrefit[entry] = false; /// remove the track from the PV refitting + } + auto Pvtx_refitted = mVertexer.refitVertex(vec_useTrk_PVrefit, pv); // vertex refit + // enable the dca recalculation for the current PV contributor, after removing it from the PV refit + bool recalc_imppar = true; + if (Pvtx_refitted.getChi2() < 0) { + // LOG(info) << "---> Refitted vertex has bad chi2 = " << Pvtx_refitted.getChi2(); + recalc_imppar = false; + } + vec_useTrk_PVrefit[entry] = true; /// restore the track for the next PV refitting + if (recalc_imppar) { + const double DeltaX = pv.getX() - Pvtx_refitted.getX(); + const double DeltaY = pv.getY() - Pvtx_refitted.getY(); + const double DeltaZ = pv.getZ() - Pvtx_refitted.getZ(); + mHistoXPvVsRefitted->Fill(pv.getX(), Pvtx_refitted.getX()); + mHistoYPvVsRefitted->Fill(pv.getY(), Pvtx_refitted.getY()); + mHistoZPvVsRefitted->Fill(pv.getZ(), Pvtx_refitted.getZ()); + mHistoXDeltaPVrefit->Fill(DeltaX); + mHistoYDeltaPVrefit->Fill(DeltaY); + mHistoZDeltaPVrefit->Fill(DeltaZ); + + // fill the newly calculated PV + PVbase_recalculated.setX(Pvtx_refitted.getX()); + PVbase_recalculated.setY(Pvtx_refitted.getY()); + PVbase_recalculated.setZ(Pvtx_refitted.getZ()); + PVbase_recalculated.setCov(Pvtx_refitted.getSigmaX2(), Pvtx_refitted.getSigmaXY(), Pvtx_refitted.getSigmaY2(), Pvtx_refitted.getSigmaXZ(), Pvtx_refitted.getSigmaYZ(), Pvtx_refitted.getSigmaZ2()); + + auto trueID = trueVec_globID_contr[it]; + const o2::track::TrackParCov& trc = recoData.getTrackParam(trueID); + auto pt = trc.getPt(); + std::array dcaInfo{-999., -999.}; + // LOGP(info, " ---> Bz={}", o2::base::Propagator::Instance()->getNominalBz()); + o2::track::TrackPar trcTmp{trc}; + if (o2::base::Propagator::Instance()->propagateToDCABxByBz({Pvtx_refitted.getX(), Pvtx_refitted.getY(), Pvtx_refitted.getZ()}, trcTmp, 2.f, matCorr, &dcaInfo)) { + impParRPhi = dcaInfo[0] * toMicrometers; + impParZ = dcaInfo[1] * toMicrometers; + mHistoImpParXy->Fill(pt, impParRPhi); + mHistoImpParZ->Fill(pt, impParZ); + double phi = trcTmp.getPhi(); + mHistoImpParXyPhi->Fill(phi, impParRPhi); + mHistoImpParZPhi->Fill(phi, impParZ); + if (phi < TMath::Pi()) { + mHistoImpParXyTop->Fill(pt, impParRPhi); + mHistoImpParZTop->Fill(pt, impParZ); + } + if (phi > TMath::Pi()) { + mHistoImpParXyBottom->Fill(pt, impParRPhi); + mHistoImpParZBottom->Fill(pt, impParZ); + } + double sign = trcTmp.getSign(); + if (sign < 0) { + mHistoImpParXyNegativeCharge->Fill(pt, impParRPhi); + mHistoImpParZNegativeCharge->Fill(pt, impParZ); + } else { + mHistoImpParXyPositiveCharge->Fill(pt, impParRPhi); + mHistoImpParZPositiveCharge->Fill(pt, impParZ); + } + } + } // end recalc impact param + } + } // end loop tracks in pv refitted + } // else pv refit duable */ + vec_globID_contr.clear(); + vecPvContributorTrackParCov.clear(); + trueVec_globID_contr.clear(); + trueVecPvContributorTrackParCov.clear(); + + } // end loop pv + + mHistoImpParXy->FitSlicesY(); + mHistoImpParZ->FitSlicesY(); + mHistoImpParXyPhi->FitSlicesY(); + mHistoImpParZPhi->FitSlicesY(); + mHistoImpParXyTop->FitSlicesY(); + mHistoImpParZTop->FitSlicesY(); + mHistoImpParXyBottom->FitSlicesY(); + mHistoImpParZBottom->FitSlicesY(); + mHistoImpParXyNegativeCharge->FitSlicesY(); + mHistoImpParZNegativeCharge->FitSlicesY(); + mHistoImpParXyPositiveCharge->FitSlicesY(); + mHistoImpParZPositiveCharge->FitSlicesY(); + mHistoImpParXySigma = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXy_2"))->Clone())); + mHistoImpParZSigma = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZ_2"))->Clone())); + mHistoImpParXyMeanPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPhi_1"))->Clone())); + mHistoImpParZMeanPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPhi_1"))->Clone())); + mHistoImpParXySigmaPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPhi_2"))->Clone())); + mHistoImpParZSigmaPhi = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPhi_2"))->Clone())); + mHistoImpParXyMeanTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyTop_1"))->Clone())); + mHistoImpParZMeanTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZTop_1"))->Clone())); + mHistoImpParXyMeanBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyBottom_1"))->Clone())); + mHistoImpParZMeanBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZBottom_1"))->Clone())); + mHistoImpParXySigmaTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyTop_2"))->Clone())); + mHistoImpParZSigmaTop = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZTop_2"))->Clone())); + mHistoImpParXySigmaBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyBottom_2"))->Clone())); + mHistoImpParZSigmaBottom = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZBottom_2"))->Clone())); + mHistoImpParXySigmaNegativeCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyNegativeCharge_2"))->Clone())); + mHistoImpParZSigmaNegativeCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZNegativeCharge_2"))->Clone())); + mHistoImpParXySigmaPositiveCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParXyPositiveCharge_2"))->Clone())); + mHistoImpParZSigmaPositiveCharge = std::unique_ptr(static_cast((gROOT->FindObject("histoImpParZPositiveCharge_2"))->Clone())); +} +// end process + +void ImpactParameterStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + mVertexer.init(); + if (pc.services().get().inputTimesliceId == 0) { + } + } +} + +void ImpactParameterStudy::saveHistograms() +{ + mDBGOut.reset(); + TFile fout(mOutName.Data(), "update"); + fout.WriteTObject(mHistoContributorsPV.get()); + fout.WriteTObject(mHistoTrackType.get()); + fout.WriteTObject(mHistoTrackTypeRej.get()); + fout.WriteTObject(mHistoTrackTypeAcc.get()); + fout.WriteTObject(mHistoXPvVsRefitted.get()); + fout.WriteTObject(mHistoYPvVsRefitted.get()); + fout.WriteTObject(mHistoZPvVsRefitted.get()); + fout.WriteTObject(mHistoXDeltaPVrefit.get()); + fout.WriteTObject(mHistoYDeltaPVrefit.get()); + fout.WriteTObject(mHistoZDeltaPVrefit.get()); + fout.WriteTObject(mHistoImpParZ.get()); + fout.WriteTObject(mHistoImpParXy.get()); + fout.WriteTObject(mHistoImpParXyPhi.get()); + fout.WriteTObject(mHistoImpParZPhi.get()); + fout.WriteTObject(mHistoImpParXyTop.get()); + fout.WriteTObject(mHistoImpParZTop.get()); + fout.WriteTObject(mHistoImpParXyBottom.get()); + fout.WriteTObject(mHistoImpParZBottom.get()); + fout.WriteTObject(mHistoImpParXyPositiveCharge.get()); + fout.WriteTObject(mHistoImpParZPositiveCharge.get()); + fout.WriteTObject(mHistoImpParXyNegativeCharge.get()); + fout.WriteTObject(mHistoImpParZNegativeCharge.get()); + fout.WriteTObject(mHistoImpParZMeanPhi.get()); + fout.WriteTObject(mHistoImpParXyMeanPhi.get()); + fout.WriteTObject(mHistoImpParZSigmaPhi.get()); + fout.WriteTObject(mHistoImpParXySigmaPhi.get()); + fout.WriteTObject(mHistoImpParZSigma.get()); + fout.WriteTObject(mHistoImpParXySigma.get()); + fout.WriteTObject(mHistoImpParZMeanTop.get()); + fout.WriteTObject(mHistoImpParXyMeanTop.get()); + fout.WriteTObject(mHistoImpParZMeanBottom.get()); + fout.WriteTObject(mHistoImpParXyMeanBottom.get()); + fout.WriteTObject(mHistoImpParZSigmaTop.get()); + fout.WriteTObject(mHistoImpParXySigmaTop.get()); + fout.WriteTObject(mHistoImpParZSigmaBottom.get()); + fout.WriteTObject(mHistoImpParXySigmaBottom.get()); + fout.WriteTObject(mHistoImpParZSigmaPositiveCharge.get()); + fout.WriteTObject(mHistoImpParXySigmaPositiveCharge.get()); + fout.WriteTObject(mHistoImpParZSigmaNegativeCharge.get()); + fout.WriteTObject(mHistoImpParXySigmaNegativeCharge.get()); + LOGP(info, "Impact Parameters histograms stored in {}", mOutName.Data()); + fout.Close(); +} + +void ImpactParameterStudy::plotHistograms() +{ + TString directoryName = "./plotsImpactParameter"; + gSystem->mkdir(directoryName); + TCanvas* dcaXyVsdcaZ = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigma, kRed, "#sigma_{XY}", *mHistoImpParZSigma, kBlue, "#sigma_{Z}"); + TCanvas* dcaXyTopVsBottom = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigmaTop, kRed, "#sigma_{XY}^{TOP}", *mHistoImpParXySigmaBottom, kBlue, "#sigma_{XY}^{BOTTOM}"); + TCanvas* dcaZTopVsBottom = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParZSigmaTop, kRed, "#sigma_{Z}^{TOP}", *mHistoImpParZSigmaBottom, kBlue, "#sigma_{Z}^{BOTTOM}"); + TCanvas* dcaXyPosVsNeg = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParXySigmaPositiveCharge, kRed, "#sigma_{XY}^{positive}", *mHistoImpParXySigmaNegativeCharge, kBlue, "#sigma_{XY}^{negative}"); + TCanvas* dcaZPosVsNeg = helpers::prepareSimpleCanvas2Histograms(*mHistoImpParZSigmaPositiveCharge, kRed, "#sigma_{Z}^{positive}", *mHistoImpParZSigmaNegativeCharge, kBlue, "#sigma_{Z}^{negative}"); + TCanvas* dcaXYvsPhi = helpers::plot2DwithMeanAndSigma(*mHistoImpParXyPhi, *mHistoImpParXyMeanPhi, *mHistoImpParXySigmaPhi, kRed); + TCanvas* dcaZvsPhi = helpers::plot2DwithMeanAndSigma(*mHistoImpParZPhi, *mHistoImpParZMeanPhi, *mHistoImpParZSigmaPhi, kRed); + TCanvas* dcaXyPhiMeanTopVsBottom = helpers::prepareSimpleCanvas2DcaMeanValues(*mHistoImpParXyMeanTop, kRed, "mean_{XY}^{TOP}", *mHistoImpParXyMeanBottom, kBlue, "mean_{XY}^{BOTTOM}"); + TCanvas* dcaZPhiMeanTopVsBottom = helpers::prepareSimpleCanvas2DcaMeanValues(*mHistoImpParZMeanTop, kRed, "mean_{Z}^{TOP}", *mHistoImpParZMeanBottom, kBlue, "mean_{Z}^{BOTTOM}"); + + dcaXyVsdcaZ->SaveAs(Form("%s/ComparisonDCAXYvsZ.png", directoryName.Data())); + dcaXyTopVsBottom->SaveAs(Form("%s/ComparisonDCAXyTopVsBottom.png", directoryName.Data())); + dcaXyPosVsNeg->SaveAs(Form("%s/ComparisonDCAXyPosVsNeg.png", directoryName.Data())); + dcaZTopVsBottom->SaveAs(Form("%s/ComparisonDCAZTopVsBottom.png", directoryName.Data())); + dcaZPosVsNeg->SaveAs(Form("%s/ComparisonDCAZPosVsNeg.png", directoryName.Data())); + dcaXYvsPhi->SaveAs(Form("%s/dcaXYvsPhi.png", directoryName.Data())); + dcaZvsPhi->SaveAs(Form("%s/dcaZvsPhi.png", directoryName.Data())); + dcaXyPhiMeanTopVsBottom->SaveAs(Form("%s/dcaXyPhiMeanTopVsBottom.png", directoryName.Data())); + dcaZPhiMeanTopVsBottom->SaveAs(Form("%s/dcaZPhiMeanTopVsBottom.png", directoryName.Data())); +} + +void ImpactParameterStudy::endOfStream(EndOfStreamContext& ec) +{ + auto& params = ITSImpactParameterParamConfig::Instance(); + if (params.generatePlots) { + saveHistograms(); + plotHistograms(); + } +} + +void ImpactParameterStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getImpactParameterStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestPrimaryVertices(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-impactparameter", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracksMask, useMC)}, + Options{}}; +} + +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx new file mode 100644 index 0000000000000..9a7f6c218cd12 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx @@ -0,0 +1,351 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PIDStudy.cxx +/// \brief Study to evaluate the PID performance of the ITS +/// \author Francesco Mazzaschi, fmazzasc@cern.ch + +#include "ITSStudies/PIDStudy.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "Steer/MCKinematicsReader.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ITStracking/IOUtils.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsTPC/TrackTPC.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PID.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsTPC/PIDResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using PVertex = o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using Track = o2::track::TrackParCov; +using TrackITS = o2::its::TrackITS; +using TrackTPC = o2::tpc::TrackTPC; +using TrackITSTPC = o2::dataformats::TrackTPCITS; +using PIDResponse = o2::tpc::PIDResponse; +using DCA = o2::dataformats::DCA; +using PID = o2::track::PID; + +// structure for storing the output tree +struct particle { + // mc properties + int pdg = -1; + bool fakeMatch = 0; + // reco properties + int sign = -1; + float p, pt, pTPC, pITS, eta, phi, tgL, chi2ITS, chi2TPC, chi2ITSTPC; + int nClusTPC; + float dEdx, nSigmaDeu, nSigmaP, nSigmaK, nSigmaPi, nSigmaE; + int clSizeL0, clSizeL1, clSizeL2, clSizeL3, clSizeL4, clSizeL5, clSizeL6; + std::array clSizesITS; +}; + +class PIDStudy : public Task +{ + public: + PIDStudy(std::shared_ptr dr, + std::shared_ptr gr, + bool isMC, + std::shared_ptr kineReader) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC), mKineReader(kineReader){}; + ~PIDStudy() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + // Other functions + void process(o2::globaltracking::RecoContainer&); + void loadData(o2::globaltracking::RecoContainer&); + + // Helper functions + void saveOutput(); + void updateTimeDependentParams(ProcessingContext& pc); + void getClusterSizes(std::vector&, const gsl::span, gsl::span::iterator&, const o2::itsmft::TopologyDictionary*); + std::array getTrackClusterSizes(const TrackITS& track); + float computeNSigma(PID pid, TrackTPC& tpcTrack, float resolution); + + // Running options + bool mUseMC; + + PIDResponse mPIDresponse; + float mBBres; + // Data + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + std::vector mClusterSizes; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + std::unique_ptr mDBGOut; + std::string mOutName; + std::shared_ptr mKineReader; +}; + +void PIDStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + LOGP(info, "Starting average cluster size study..."); + + if (mUseMC) { // for counting the missed K0shorts + mKineReader = std::make_unique("collisioncontext.root"); + } + auto& params = o2::its::study::PIDStudyParamConfig::Instance(); + mOutName = params.outFileName; + mPIDresponse.setBetheBlochParams(params.mBBpars); + mBBres = params.mBBres; + LOGP(info, "PID size study initialized."); + + // prepare output tree + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); +} + +void PIDStudy::run(ProcessingContext& pc) +{ + // auto geom = o2::its::GeometryTGeo::Instance(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void PIDStudy::getClusterSizes(std::vector& clusSizeVec, const gsl::span ITSclus, gsl::span::iterator& pattIt, const o2::itsmft::TopologyDictionary* mdict) +{ + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + auto pattID = clus.getPatternID(); + int npix; + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + patt.acquirePattern(pattIt); + npix = patt.getNPixels(); + } else { + npix = mdict->getNpixels(pattID); + patt = mdict->getPattern(pattID); + } + clusSizeVec[iClus] = npix; + } +} + +void PIDStudy::loadData(o2::globaltracking::RecoContainer& recoData) +{ + mInputITSidxs = recoData.getITSTracksClusterRefs(); + mClusters = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + mClusterSizes.resize(mClusters.size()); + auto pattIt = clusPatt.begin(); + getClusterSizes(mClusterSizes, mClusters, pattIt, mDict); +} + +void PIDStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + loadData(recoData); + auto ITSTPCtracks = recoData.getTPCITSTracks(); + LOGP(debug, "Found {} ITSTPC tracks.", ITSTPCtracks.size()); + + gsl::span mcLabelsITS, mcLabelsTPC; + if (mUseMC) { + mcLabelsITS = recoData.getITSTracksMCLabels(); + mcLabelsTPC = recoData.getTPCTracksMCLabels(); + LOGP(debug, "Found {} ITS labels.", mcLabelsITS.size()); + LOGP(debug, "Found {} TPC labels.", mcLabelsTPC.size()); + } + + for (unsigned int iTrack{0}; iTrack < ITSTPCtracks.size(); ++iTrack) { + + auto& ITSTPCtrack = ITSTPCtracks[iTrack]; + if (ITSTPCtrack.getRefITS().getSource() == GTrackID::ITSAB) { // excluding Afterburned tracks + continue; + } + particle part; + auto ITStrack = recoData.getITSTrack(ITSTPCtrack.getRefITS()); + auto TPCtrack = recoData.getTPCTrack(ITSTPCtrack.getRefTPC()); + + if (mUseMC) { + // MC info + auto& mcLabelITS = mcLabelsITS[ITSTPCtrack.getRefITS().getIndex()]; + auto& mcLabelTPC = mcLabelsTPC[ITSTPCtrack.getRefTPC().getIndex()]; + if (mcLabelITS.getTrackID() != (int)mcLabelTPC.getTrackID()) { + part.fakeMatch = 1; + } + auto mctrk = mKineReader->getTrack(mcLabelITS); + part.pdg = mctrk->GetPdgCode(); + } + + part.sign = ITSTPCtrack.getSign(); + part.clSizesITS = getTrackClusterSizes(ITStrack); + part.p = ITSTPCtrack.getP(); + part.pt = ITSTPCtrack.getPt(); + part.pTPC = TPCtrack.getP(); + part.pITS = ITStrack.getP(); + part.eta = ITSTPCtrack.getEta(); + part.phi = ITSTPCtrack.getPhi(); + part.tgL = ITSTPCtrack.getTgl(); + part.chi2ITS = ITStrack.getChi2(); + part.chi2TPC = TPCtrack.getChi2(); + part.chi2ITSTPC = ITSTPCtrack.getChi2Match(); + + part.clSizeL0 = part.clSizesITS[0]; + part.clSizeL1 = part.clSizesITS[1]; + part.clSizeL2 = part.clSizesITS[2]; + part.clSizeL3 = part.clSizesITS[3]; + part.clSizeL4 = part.clSizesITS[4]; + part.clSizeL5 = part.clSizesITS[5]; + part.clSizeL6 = part.clSizesITS[6]; + + // PID info + part.dEdx = TPCtrack.getdEdx().dEdxTotTPC; + part.nClusTPC = TPCtrack.getNClusters(); + // 7% resolution for all particles + part.nSigmaDeu = computeNSigma(PID::Deuteron, TPCtrack, mBBres); + part.nSigmaP = computeNSigma(PID::Proton, TPCtrack, mBBres); + part.nSigmaK = computeNSigma(PID::Kaon, TPCtrack, mBBres); + part.nSigmaPi = computeNSigma(PID::Pion, TPCtrack, mBBres); + part.nSigmaE = computeNSigma(PID::Electron, TPCtrack, mBBres); + + if (mUseMC) { + (*mDBGOut) << "outTree" + << "pdg=" << part.pdg << "fakeMatch=" << part.fakeMatch; + } + (*mDBGOut) << "outTree" + << "sign=" << part.sign << "p=" << part.p << "pt=" << part.pt << "pTPC=" << part.pTPC << "pITS=" << part.pITS + << "eta=" << part.eta << "phi=" << part.phi << "tgL=" << part.tgL << "chi2ITS=" << part.chi2ITS << "chi2TPC=" + << part.chi2TPC << "chi2ITSTPC=" << part.chi2ITSTPC << "dEdx=" << part.dEdx << "nClusTPC=" << part.nClusTPC + << "nSigmaDeu=" << part.nSigmaDeu << "nSigmaP=" << part.nSigmaP << "nSigmaK=" << part.nSigmaK << "nSigmaPi=" + << part.nSigmaPi << "nSigmaE=" << part.nSigmaE << "clSizeL0=" << part.clSizeL0 << "clSizeL1=" << part.clSizeL1 + << "clSizeL2=" << part.clSizeL2 << "clSizeL3=" << part.clSizeL3 << "clSizeL4=" << part.clSizeL4 << "clSizeL5=" + << part.clSizeL5 << "clSizeL6=" << part.clSizeL6 << "\n"; + } +} + +std::array PIDStudy::getTrackClusterSizes(const TrackITS& track) +{ + auto geom = o2::its::GeometryTGeo::Instance(); + std::array clusSizes = {-1, -1, -1, -1, -1, -1, -1}; + auto firstClus = track.getFirstClusterEntry(); + auto ncl = track.getNumberOfClusters(); + for (int icl = 0; icl < ncl; icl++) { + auto& clus = mClusters[mInputITSidxs[firstClus + icl]]; + auto& clSize = mClusterSizes[mInputITSidxs[firstClus + icl]]; + auto layer = geom->getLayer(clus.getSensorID()); + clusSizes[layer] = clSize; + } + return clusSizes; +} + +void PIDStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this param need to be queried only once + initOnceDone = true; + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void PIDStudy::saveOutput() +{ + mDBGOut.reset(); + LOGP(info, "Stored histograms into {}", mOutName.c_str()); +} + +void PIDStudy::endOfStream(EndOfStreamContext& ec) +{ + // saveOutput(); +} + +void PIDStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + // o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +float PIDStudy::computeNSigma(PID pid, TrackTPC& tpcTrack, float resolution) +{ + float nSigma = -999; + float bb = mPIDresponse.getExpectedSignal(tpcTrack, pid); + if (tpcTrack.getdEdx().dEdxTotTPC > 0) { + nSigma = (tpcTrack.getdEdx().dEdxTotTPC - bb) / (resolution * bb); + } + return nSigma; +} + +DataProcessorSpec getPIDStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-pid-study", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, kineReader)}, + Options{}}; +} +} // namespace study +} // namespace its +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx new file mode 100644 index 0000000000000..bbe7a6ec5e9bb --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx @@ -0,0 +1,1380 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSStudies/TrackCheck.h" +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCTrack.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "Framework/Task.h" +#include "Steer/MCKinematicsReader.h" +#include "ITSBase/GeometryTGeo.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using o2::steer::MCKinematicsReader; +class TrackCheckStudy : public Task +{ + struct ParticleInfo { + int event; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float vx; + float vy; + float vz; + int unsigned short clusters = 0u; + unsigned char isReco = 0u; + unsigned char isFake = 0u; + bool isPrimary = false; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + const char* prodProcessName; + int prodProcess; + o2::its::TrackITS track; + }; + + public: + TrackCheckStudy(std::shared_ptr dr, + mask_t src, + bool useMC, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mKineReader(kineReader), mGGCCDBRequest(gr) + { + if (useMC) { + LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); + } + } + + ~TrackCheckStudy() final = default; + void init(InitContext&) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void initialiseRun(o2::globaltracking::RecoContainer&); + void process(); + void setEfficiencyGraph(std::unique_ptr&, const char*, const char*, const int, const double, const double, const int, const double); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + std::string mOutFileName = "TrackCheckStudy.root"; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry; + + // Spans + gsl::span mTracksROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont; + + // Data + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + std::vector>> mParticleInfo; // src/event/track + unsigned short mMask = 0x7f; + + // Utils + std::shared_ptr mGGCCDBRequest; + + // Histos + std::unique_ptr mGoodPt; + std::unique_ptr mGoodEta; + std::unique_ptr mGoodPtSec; + std::unique_ptr mGoodEtaSec; + std::unique_ptr mGoodChi2; + std::unique_ptr mFakePt; + std::unique_ptr mFakeEta; + std::unique_ptr mFakePtSec; + std::unique_ptr mFakeEtaSec; + std::unique_ptr mMultiFake; + std::unique_ptr mFakeChi2; + std::unique_ptr mClonePt; + std::unique_ptr mCloneEta; + + std::unique_ptr mDenominatorPt; + std::unique_ptr mDenominatorEta; + std::unique_ptr mDenominatorPtSec; + std::unique_ptr mDenominatorEtaSec; + + std::unique_ptr processvsZ; // TH2D with production process + std::unique_ptr processvsRad; + std::unique_ptr processvsRadOther; + std::unique_ptr processvsRadNotTracked; + std::unique_ptr processvsEtaNotTracked; + + std::unique_ptr mEffPt; // Eff vs Pt primary + std::unique_ptr mEffFakePt; + std::unique_ptr mEffClonesPt; + std::unique_ptr mEffEta; // Eff vs Eta primary + std::unique_ptr mEffFakeEta; + std::unique_ptr mEffClonesEta; + + std::unique_ptr mEffPtSec; // Eff vs Pt secondary + std::unique_ptr mEffFakePtSec; + std::unique_ptr mEffEtaSec; // Eff vs Eta secondary + std::unique_ptr mEffFakeEtaSec; + + std::unique_ptr mPtResolution; // Pt resolution for both primary and secondary + std::unique_ptr mPtResolution2D; + std::unique_ptr mPtResolutionSec; + std::unique_ptr mPtResolutionPrim; + std::unique_ptr g1; + + const char* ParticleName[7] = {"e^{-/+}", "#pi^{-/+}", "p", "^{2}H", "^{3}He", "_{#Lambda}^{3}H", "k^{+/-}"}; + const int PdgcodeClusterFake[7] = {11, 211, 2212, 1000010020, 100002030, 1010010030, 321}; + const char* name[3] = {"_{#Lambda}^{3}H", "#Lambda", "k^{0}_{s}"}; + const char* particleToanalize[4] = {"IperT", "Lambda", "k0s", "Tot"}; // [3]=Total of secondary particle + const int PDG[3] = {1010010030, 3122, 310}; + const char* ProcessName[50]; + int colorArr[4] = {kGreen, kRed, kBlue, kOrange}; + + std::vector> histLength, histLength1Fake, histLength2Fake, histLength3Fake, histLengthNoCl, histLength1FakeNoCl, histLength2FakeNoCl, histLength3FakeNoCl; // FakeCluster Study + std::vector stackLength, stackLength1Fake, stackLength2Fake, stackLength3Fake; + std::vector legends, legends1Fake, legends2Fake, legends3Fake; + std::vector> mClusterFake; + std::vector>> mGoodPts, mFakePts, mTotPts, mGoodEtas, mTotEtas, mFakeEtas; + std::vector>> mEffGoodPts, mEffFakePts, mEffGoodEtas, mEffFakeEtas; + std::vector> mGoodRad, mFakeRad, mTotRad, mGoodZ, mFakeZ, mTotZ; + std::vector> mEffGoodRad, mEffFakeRad, mEffGoodZ, mEffFakeZ; + // Canvas & decorations + std::unique_ptr mCanvasPt; + std::unique_ptr mCanvasPtSec; + std::unique_ptr mCanvasPt2; + std::unique_ptr mCanvasPt2fake; + std::unique_ptr mCanvasEta; + std::unique_ptr mCanvasEtaSec; + std::unique_ptr mCanvasRad; + std::unique_ptr mCanvasZ; + std::unique_ptr mCanvasRadD; + std::unique_ptr mCanvasZD; + std::unique_ptr mCanvasPtRes; + std::unique_ptr mCanvasPtRes2; + std::unique_ptr mCanvasPtRes3; + std::unique_ptr mCanvasPtRes4; + std::unique_ptr mLegendPt; + std::unique_ptr mLegendEta; + std::unique_ptr mLegendPtSec; + std::unique_ptr mLegendEtaSec; + std::unique_ptr mLegendPtRes; + std::unique_ptr mLegendPtRes2; + std::unique_ptr mLegendZ; + std::unique_ptr mLegendRad; + std::unique_ptr mLegendZD; + std::unique_ptr mLegendRadD; + std::vector Histo; + + float rLayer0 = 2.34; // middle radius + float rLayer1 = 3.15; + float rLayer2 = 3.93; + float rLayer3 = 19.605; + + double sigma[100]; + double sigmaerr[100]; + double meanPt[100]; + double aa[100]; + + // Debug output tree + std::unique_ptr mDBGOut; +}; + +void TrackCheckStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + + auto& pars = o2::its::study::ITSCheckTracksParamConfig::Instance(); + mOutFileName = pars.outFileName; + mMask = pars.trackLengthMask; + + std::vector xbins; + xbins.resize(pars.effHistBins + 1); + double a = std::log(pars.effPtCutHigh / pars.effPtCutLow) / pars.effHistBins; + for (int i{0}; i <= pars.effHistBins; i++) { + xbins[i] = pars.effPtCutLow * std::exp(i * a); + } + for (int yy = 0; yy < 50; yy++) { + ProcessName[yy] = " "; + } + mGoodPt = std::make_unique("goodPt", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mGoodEta = std::make_unique("goodEta", ";#eta;Number of tracks", 60, -3, 3); + mGoodPtSec = std::make_unique("goodPtSec", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mGoodEtaSec = std::make_unique("goodEtaSec", ";#eta;Number of tracks", 60, -3, 3); + mGoodChi2 = std::make_unique("goodChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); + + mFakePt = std::make_unique("fakePt", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + mFakeEta = std::make_unique("fakeEta", ";#eta;Number of tracks", 60, -3, 3); + mFakePtSec = std::make_unique("fakePtSec", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + mFakeEtaSec = std::make_unique("fakeEtaSec", ";#eta;Number of tracks", 60, -3, 3); + mFakeChi2 = std::make_unique("fakeChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); + + mMultiFake = std::make_unique("multiFake", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + + mClonePt = std::make_unique("clonePt", ";#it{p}_{T} (GeV/#it{c});Clone", pars.effHistBins, xbins.data()); + mCloneEta = std::make_unique("cloneEta", ";#eta;Number of tracks", 60, -3, 3); + + mDenominatorPt = std::make_unique("denominatorPt", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); + mDenominatorEta = std::make_unique("denominatorEta", ";#eta;Number of tracks", 60, -3, 3); + mDenominatorPtSec = std::make_unique("denominatorPtSec", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); + mDenominatorEtaSec = std::make_unique("denominatorEtaSec", ";#eta;Number of tracks", 60, -3, 3); + + processvsZ = std::make_unique("Process", ";z_{SV} [cm]; production process", 100, -50, 50., 50, 0, 50); + processvsRad = std::make_unique("ProcessR", ";decay radius [cm]; production process", 100, 0, 25., 50, 0, 50); + processvsRadOther = std::make_unique("ProcessRO", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsRadNotTracked = std::make_unique("ProcessRNoT", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsEtaNotTracked = std::make_unique("ProcessENoT", ";#eta; production process", 60, -3, 3, 50, 0, 50); + + mGoodPts.resize(4); + mFakePts.resize(4); + mTotPts.resize(4); + mGoodEtas.resize(4); + mFakeEtas.resize(4); + mTotEtas.resize(4); + mGoodRad.resize(4); + mFakeRad.resize(4); + mTotRad.resize(4); + mGoodZ.resize(4); + mFakeZ.resize(4); + mTotZ.resize(4); + mClusterFake.resize(4); + for (int i = 0; i < 4; i++) { + mGoodPts[i].resize(4); + mFakePts[i].resize(4); + mTotPts[i].resize(4); + mGoodEtas[i].resize(4); + mFakeEtas[i].resize(4); + mTotEtas[i].resize(4); + } + for (int ii = 0; ii < 4; ii++) { + + mGoodRad[ii] = std::make_unique(Form("goodRad_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, 0., 20.); + mFakeRad[ii] = std::make_unique(Form("FakeRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + mTotRad[ii] = std::make_unique(Form("TotRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + + mGoodZ[ii] = std::make_unique(Form("goodZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mFakeZ[ii] = std::make_unique(Form("FakeZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mTotZ[ii] = std::make_unique(Form("TotZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mClusterFake[ii] = std::make_unique(Form("Clusters_fake_%s", ParticleName[ii]), ";particle generating fake cluster; production process", 7, 0., 7., 50, 0, 50); + + mGoodRad[ii]->Sumw2(); + mFakeRad[ii]->Sumw2(); + mTotRad[ii]->Sumw2(); + mGoodZ[ii]->Sumw2(); + mFakeZ[ii]->Sumw2(); + mTotZ[ii]->Sumw2(); + + for (int yy = 0; yy < 4; yy++) { // divided by layer + mGoodPts[ii][yy] = std::make_unique(Form("goodPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mFakePts[ii][yy] = std::make_unique(Form("FakePts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mTotPts[ii][yy] = std::make_unique(Form("TotPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + + mGoodEtas[ii][yy] = std::make_unique(Form("goodEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mFakeEtas[ii][yy] = std::make_unique(Form("FakeEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mTotEtas[ii][yy] = std::make_unique(Form("TotEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + + mGoodPts[ii][yy]->Sumw2(); + mFakePts[ii][yy]->Sumw2(); + mTotPts[ii][yy]->Sumw2(); + mGoodEtas[ii][yy]->Sumw2(); + mFakeEtas[ii][yy]->Sumw2(); + mTotEtas[ii][yy]->Sumw2(); + } + } + + mPtResolution = std::make_unique("PtResolution", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionSec = std::make_unique("PtResolutionSec", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionPrim = std::make_unique("PtResolutionPrim", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolution2D = std::make_unique("#it{p}_{T} Resolution vs #it{p}_{T}", ";#it{p}_{T} (GeV/#it{c});#Delta p_{T}/p_{T_{MC}", 100, 0, 10, 100, -1, 1); + + mPtResolution->Sumw2(); + mPtResolutionSec->Sumw2(); + mPtResolutionPrim->Sumw2(); + + mGoodPt->Sumw2(); + mGoodEta->Sumw2(); + mGoodPtSec->Sumw2(); + mGoodEtaSec->Sumw2(); + + mFakePt->Sumw2(); + mFakePtSec->Sumw2(); + mFakeEta->Sumw2(); + mMultiFake->Sumw2(); + mClonePt->Sumw2(); + mDenominatorPt->Sumw2(); + + histLength.resize(4); // fake clusters study + histLength1Fake.resize(4); + histLength2Fake.resize(4); + histLength3Fake.resize(4); + histLengthNoCl.resize(4); + histLength1FakeNoCl.resize(4); + histLength2FakeNoCl.resize(4); + histLength3FakeNoCl.resize(4); + stackLength.resize(4); + stackLength1Fake.resize(4); + stackLength2Fake.resize(4); + stackLength3Fake.resize(4); + for (int yy = 0; yy < 4; yy++) { + histLength[yy].resize(3); + histLength1Fake[yy].resize(3); + histLength2Fake[yy].resize(3); + histLength3Fake[yy].resize(3); + histLengthNoCl[yy].resize(3); + histLength1FakeNoCl[yy].resize(3); + histLength2FakeNoCl[yy].resize(3); + histLength3FakeNoCl[yy].resize(3); + } + legends.resize(4); + legends1Fake.resize(4); + legends2Fake.resize(4); + legends3Fake.resize(4); + + for (int iH{4}; iH < 8; ++iH) { + // check distributions on layers of fake clusters for tracks of different lengths. + // Different histograms if the correct cluster exist or not + for (int jj = 0; jj < 3; jj++) { + histLength[iH - 4][jj] = new TH1I(Form("trk_len_%d_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLengthNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLengthNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLengthNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength[iH - 4] = new THStack(Form("stack_trk_len_%d", iH), Form("trk_len=%d", iH)); + } + stackLength[iH - 4]->Add(histLength[iH - 4][jj]); + stackLength[iH - 4]->Add(histLengthNoCl[iH - 4][jj]); + + histLength1Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength1Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength1Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength1FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength1FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength1FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength1Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_1f", iH), Form("trk_len=%d, 1 Fake", iH)); + } + stackLength1Fake[iH - 4]->Add(histLength1Fake[iH - 4][jj]); + stackLength1Fake[iH - 4]->Add(histLength1FakeNoCl[iH - 4][jj]); + + histLength2Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength2Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength2Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength2FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength2FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength2FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength2Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_2f", iH), Form("trk_len=%d, 2 Fake", iH)); + } + stackLength2Fake[iH - 4]->Add(histLength2Fake[iH - 4][jj]); + stackLength2Fake[iH - 4]->Add(histLength2FakeNoCl[iH - 4][jj]); + + histLength3Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength3Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength3Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + + histLength3FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength3FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength3FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength3Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_3f", iH), Form("trk_len=%d, 3 Fake", iH)); + } + stackLength3Fake[iH - 4]->Add(histLength3Fake[iH - 4][jj]); + stackLength3Fake[iH - 4]->Add(histLength3FakeNoCl[iH - 4][jj]); + } + } +} + +void TrackCheckStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + initialiseRun(recoData); + process(); +} + +void TrackCheckStudy::initialiseRun(o2::globaltracking::RecoContainer& recoData) +{ + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClusters = recoData.getITSClusters(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", + mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); + LOGP(info, "** Found {} sources from kinematic files", mKineReader->getNSources()); +} + +void TrackCheckStudy::process() +{ + LOGP(info, "** Filling particle table ... "); + mParticleInfo.resize(mKineReader->getNSources()); // sources + for (int iSource{0}; iSource < mKineReader->getNSources(); ++iSource) { + mParticleInfo[iSource].resize(mKineReader->getNEvents(iSource)); // events + for (int iEvent{0}; iEvent < mKineReader->getNEvents(iSource); ++iEvent) { + mParticleInfo[iSource][iEvent].resize(mKineReader->getTracks(iSource, iEvent).size()); // tracks + for (auto iPart{0}; iPart < mKineReader->getTracks(iEvent).size(); ++iPart) { + auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; + mParticleInfo[iSource][iEvent][iPart].event = iEvent; + mParticleInfo[iSource][iEvent][iPart].pdg = part.GetPdgCode(); + mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); + mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); + mParticleInfo[iSource][iEvent][iPart].eta = part.GetEta(); + mParticleInfo[iSource][iEvent][iPart].vx = part.Vx(); + mParticleInfo[iSource][iEvent][iPart].vy = part.Vy(); + mParticleInfo[iSource][iEvent][iPart].vz = part.Vz(); + mParticleInfo[iSource][iEvent][iPart].isPrimary = part.isPrimary(); + mParticleInfo[iSource][iEvent][iPart].mother = part.getMotherTrackId(); + mParticleInfo[iSource][iEvent][iPart].prodProcessName = part.getProdProcessAsString(); + mParticleInfo[iSource][iEvent][iPart].prodProcess = part.getProcess(); + } + } + } + LOGP(info, "** Creating particle/clusters correspondance ... "); + for (auto iSource{0}; iSource < mParticleInfo.size(); ++iSource) { + for (auto iCluster{0}; iCluster < mClusters.size(); ++iCluster) { + auto labs = mClustersMCLCont->getLabels(iCluster); // ideally I can have more than one label per cluster + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + auto& cluster = mClusters[iCluster]; + auto layer = mGeometry->getLayer(cluster.getSensorID()); + mParticleInfo[srcID][evID][trackID].clusters |= (1 << layer); + } + } + } + LOGP(info, "** Analysing tracks ... "); + int unaccounted{0}, good{0}, fakes{0}; + // ***secondary tracks*** + int nPartForSpec[4][4]; // total number [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer] + int nPartGoodorFake[4][4][2]; // number of good or fake [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer][good=1 fake=0] + for (int n = 0; n < 4; n++) { + for (int m = 0; m < 4; m++) { + nPartForSpec[n][m] = 0; + for (int h = 0; h < 2; h++) { + nPartGoodorFake[n][m][h] = 0; + } + } + } + int nlayer = 999; + int ngoodfake = 0; + int totsec = 0; + int totsecCont = 0; + + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isSet() || lab.isNoise()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + + if (srcID == 99) { // skip QED + unaccounted++; + continue; + } + + mParticleInfo[srcID][evID][trackID].isReco += !fake; + mParticleInfo[srcID][evID][trackID].isFake += fake; + if (mTracks[iTrack].isBetter(mParticleInfo[srcID][evID][trackID].track, 1.e9)) { + mParticleInfo[srcID][evID][trackID].storedStatus = fake; + mParticleInfo[srcID][evID][trackID].track = mTracks[iTrack]; + } + fakes += fake; + good += !fake; + } + LOGP(info, "** Some statistics:"); + LOGP(info, "\t- Total number of tracks: {}", mTracks.size()); + LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f} %)", unaccounted, unaccounted * 100. / mTracks.size()); + LOGP(info, "\t- Total number of fakes: {} ({:.2f} %)", fakes, fakes * 100. / mTracks.size()); + LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); + + LOGP(info, "** Filling histograms ... "); + int evID = 0; + int trackID = 0; + int totP{0}, goodP{0}, fakeP{0}; + // Currently process only sourceID = 0, to be extended later if needed + for (auto& evInfo : mParticleInfo[0]) { + trackID = 0; + for (auto& part : evInfo) { + + if (strcmp(ProcessName[part.prodProcess], " ")) { + ProcessName[part.prodProcess] = part.prodProcessName; + } + if ((part.clusters & 0x7f) == mMask) { + // part.clusters != 0x3f && part.clusters != 0x3f << 1 && + // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && + // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { + // continue; + + if (part.isPrimary) { // **Primary particle** + totP++; + mDenominatorPt->Fill(part.pt); + mDenominatorEta->Fill(part.eta); + if (part.isReco) { + mGoodPt->Fill(part.pt); + mGoodEta->Fill(part.eta); + goodP++; + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + mClonePt->Fill(part.pt); + mCloneEta->Fill(part.eta); + } + } + } + if (part.isFake) { + mFakePt->Fill(part.pt); + mFakeEta->Fill(part.eta); + fakeP++; + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + mMultiFake->Fill(part.pt); + } + } + } + } + } + + // **Secondary particle** + nlayer = 999; + ngoodfake = 2; + if (!part.isPrimary) { + int TrackID, EvID, SrcID; + int pdgcode = mParticleInfo[0][evID][part.mother].pdg; + int idxPart = 999; + float rad = sqrt(pow(part.vx, 2) + pow(part.vy, 2)); + totsec++; + + if ((rad < rLayer0) && (part.clusters == 0x7f || part.clusters == 0x3f || part.clusters == 0x1f || part.clusters == 0x0f)) { // layer 0 + nlayer = 0; + } + if (rad < rLayer1 && rad > rLayer0 && (part.clusters == 0x1e || part.clusters == 0x3e || part.clusters == 0x7e)) { // layer 1 + nlayer = 1; + } + if (rad < rLayer2 && rad > rLayer1 && (part.clusters == 0x7c || part.clusters == 0x3c)) { // layer 2 + nlayer = 2; + } + if (rad < rLayer3 && rad > rLayer2 && part.clusters == 0x78) { // layer 3 + nlayer = 3; + } + if (nlayer == 0 || nlayer == 1 || nlayer == 2 || nlayer == 3) { // check if track is trackeable + + totsecCont++; + processvsZ->Fill(part.vz, part.prodProcess); + processvsRad->Fill(rad, part.prodProcess); + mDenominatorPtSec->Fill(part.pt); + mDenominatorEtaSec->Fill(part.eta); + mTotRad[3]->Fill(rad); + mTotZ[3]->Fill(part.vz); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + if (pdgcode == PDG[0] || pdgcode == -1 * PDG[0]) { + idxPart = 0; // IperT + } + if (pdgcode == PDG[1] || pdgcode == -1 * PDG[1]) { + idxPart = 1; // Lambda + } + if (pdgcode == PDG[2] || pdgcode == -1 * PDG[2]) { + idxPart = 2; // K0s + } + if (part.isReco) { + ngoodfake = 1; + mGoodPts[3][nlayer]->Fill(part.pt); + mGoodEtas[3][nlayer]->Fill(part.eta); + mGoodPtSec->Fill(part.pt); + mGoodEtaSec->Fill(part.eta); + mGoodRad[3]->Fill(rad); + mGoodZ[3]->Fill(part.vz); + } + if (part.isFake) { + ngoodfake = 0; + mFakePts[3][nlayer]->Fill(part.pt); + mFakeEtas[3][nlayer]->Fill(part.eta); + mFakePtSec->Fill(part.pt); + mFakeEtaSec->Fill(part.eta); + mFakeRad[3]->Fill(rad); + mFakeZ[3]->Fill(part.vz); + } + if (idxPart < 3) // to change if the number of analysing particle changes + { + mTotRad[idxPart]->Fill(rad); + mTotZ[idxPart]->Fill(part.vz); + mTotPts[idxPart][nlayer]->Fill(part.pt); + mTotEtas[idxPart][nlayer]->Fill(part.eta); + if (part.isReco) { + mGoodRad[idxPart]->Fill(rad); + mGoodZ[idxPart]->Fill(part.vz); + mGoodPts[idxPart][nlayer]->Fill(part.pt); + mGoodEtas[idxPart][nlayer]->Fill(part.eta); + } + if (part.isFake) { + mFakeRad[idxPart]->Fill(rad); + mFakeZ[idxPart]->Fill(part.vz); + mFakePts[idxPart][nlayer]->Fill(part.pt); + mFakeEtas[idxPart][nlayer]->Fill(part.eta); + } + } + + if (pdgcode != 1010010030 && pdgcode != 3122 && pdgcode != 310 && pdgcode != -1010010030 && pdgcode != -310 && pdgcode != -3122) { + idxPart = 3; + processvsRadOther->Fill(rad, part.prodProcess); + } + + if (!part.isFake && !part.isReco) { + processvsEtaNotTracked->Fill(part.eta, part.prodProcess); + processvsRadNotTracked->Fill(rad, part.prodProcess); + } + if (ngoodfake == 1 || ngoodfake == 0) { + nPartGoodorFake[idxPart][nlayer][ngoodfake]++; + } + nPartForSpec[idxPart][nlayer]++; + + // Analysing fake clusters + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(part.clusters) * 8; ++bit) { + nCl += bool(part.clusters & (1 << bit)); + } + if (nCl < 3) { + continue; + } + if (idxPart < 3) { + auto& track = part.track; + auto len = track.getNClusters(); + int nclu = track.getNumberOfClusters(); + int firstclu = track.getFirstClusterEntry(); + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (track.hasHitOnLayer(iLayer)) { + if (track.isFakeOnLayer(iLayer)) { + // Reco track has fake cluster + if (part.clusters & (0x1 << iLayer)) { // Correct cluster exists + histLength[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3Fake[len - 4][idxPart]->Fill(iLayer); + } + } else { + + histLengthNoCl[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + } + auto labs = mClustersMCLCont->getLabels(mInputITSidxs[firstclu - 1 - iLayer + track.getFirstClusterLayer() + nclu]); + + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + + bool fakec; + lab.get(TrackID, EvID, SrcID, fakec); + double intHisto = 0; + for (int hg = 0; hg < 7; hg++) { + if (mParticleInfo[SrcID][EvID][TrackID].pdg == PdgcodeClusterFake[hg] || mParticleInfo[SrcID][EvID][TrackID].pdg == -1 * (PdgcodeClusterFake[hg])) { + intHisto = hg + 0.5; + } + } + if (idxPart < 3) { + mClusterFake[idxPart]->Fill(intHisto, mParticleInfo[SrcID][EvID][TrackID].prodProcess); + } + } + } + } + } + } + } + nlayer = 999; + } + trackID++; + } + evID++; + } + + int totgood{0}, totfake{0}, totI{0}, totL{0}, totK{0}, totO{0}; + for (int xx = 0; xx < 4; xx++) { + for (int yy = 0; yy < 4; yy++) { + totgood = totgood + nPartGoodorFake[xx][yy][1]; + totfake = totfake + nPartGoodorFake[xx][yy][0]; + if (xx == 0) { + totI = totI + nPartForSpec[0][yy]; + } + if (xx == 1) { + totL = totL + nPartForSpec[1][yy]; + } + if (xx == 2) { + totK = totK + nPartForSpec[2][yy]; + } + if (xx == 3) { + totO = totO + nPartForSpec[3][yy]; + } + } + } + LOGP(info, "number of primary tracks: {}, good:{}, fake:{}", totP, goodP, fakeP); + int goodI = nPartGoodorFake[0][0][1] + nPartGoodorFake[0][1][1] + nPartGoodorFake[0][2][1] + nPartGoodorFake[0][3][1]; + int goodL = nPartGoodorFake[1][0][1] + nPartGoodorFake[1][1][1] + nPartGoodorFake[1][2][1] + nPartGoodorFake[1][3][1]; + int goodK = nPartGoodorFake[2][0][1] + nPartGoodorFake[2][1][1] + nPartGoodorFake[2][2][1] + nPartGoodorFake[2][3][1]; + int fakeI = nPartGoodorFake[0][0][0] + nPartGoodorFake[0][1][0] + nPartGoodorFake[0][2][0] + nPartGoodorFake[0][3][0]; + int fakeL = nPartGoodorFake[1][0][0] + nPartGoodorFake[1][1][0] + nPartGoodorFake[1][2][0] + nPartGoodorFake[1][3][0]; + int fakeK = nPartGoodorFake[2][0][0] + nPartGoodorFake[2][1][0] + nPartGoodorFake[2][2][0] + nPartGoodorFake[2][3][0]; + LOGP(info, "** Some statistics on secondary tracks:"); + + LOGP(info, "\t- Total number of secondary tracks: {}", totsec); + LOGP(info, "\t- Total number of secondary trackeable tracks : {}", totsecCont); + LOGP(info, "\t- Total number of secondary trackeable tracks good: {}, fake: {}", totgood, totfake); + LOGP(info, "\t- Total number of secondary trackeable tracks from IperT: {} = {} %, Good={} % , fake={} %", totI, 100 * totI / totsecCont, 100 * goodI / totI, 100 * fakeI / totI); + LOGP(info, "\t- Total number of secondary trackeable tracks from Lam: {} = {} %, Good={} % , fake={} %", totL, 100 * totL / totsecCont, 100 * goodL / totL, 100 * fakeL / totL); + LOGP(info, "\t- Total number of secondary trackeable tracks from k: {} = {} %, Good={} % , fake={} %", totK, 100 * totK / totsecCont, 100 * goodK / totK, 100 * fakeK / totK); + LOGP(info, "\t- Total number of secondary trackeable tracks from Other: {} = {} %", totO, 100 * totO / totsecCont); + + LOGP(info, "** Computing efficiencies ..."); + + mEffPt = std::make_unique(*mGoodPt, *mDenominatorPt); + mEffFakePt = std::make_unique(*mFakePt, *mDenominatorPt); + mEffClonesPt = std::make_unique(*mClonePt, *mDenominatorPt); + + mEffEta = std::make_unique(*mGoodEta, *mDenominatorEta); + mEffFakeEta = std::make_unique(*mFakeEta, *mDenominatorEta); + mEffClonesEta = std::make_unique(*mCloneEta, *mDenominatorEta); + + mEffPtSec = std::make_unique(*mGoodPtSec, *mDenominatorPtSec); + mEffFakePtSec = std::make_unique(*mFakePtSec, *mDenominatorPtSec); + + mEffEtaSec = std::make_unique(*mGoodEtaSec, *mDenominatorEtaSec); + mEffFakeEtaSec = std::make_unique(*mFakeEtaSec, *mDenominatorEtaSec); + + for (int ii = 0; ii < 4; ii++) { + for (int yy = 0; yy < 4; yy++) { + mEffGoodPts[ii][yy] = std::make_unique(*mGoodPts[ii][yy], *mTotPts[ii][yy]); + mEffFakePts[ii][yy] = std::make_unique(*mFakePts[ii][yy], *mTotPts[ii][yy]); + mEffGoodEtas[ii][yy] = std::make_unique(*mGoodEtas[ii][yy], *mTotEtas[ii][yy]); + mEffFakeEtas[ii][yy] = std::make_unique(*mFakeEtas[ii][yy], *mTotEtas[ii][yy]); + } + mEffGoodRad[ii] = std::make_unique(*mGoodRad[ii], *mTotRad[ii]); + mEffFakeRad[ii] = std::make_unique(*mFakeRad[ii], *mTotRad[ii]); + mEffGoodZ[ii] = std::make_unique(*mGoodZ[ii], *mTotZ[ii]); + mEffFakeZ[ii] = std::make_unique(*mFakeZ[ii], *mTotZ[ii]); + } + + LOGP(info, "** Analysing pT resolution..."); + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isSet() || lab.isNoise()) { + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + if (srcID == 99) { + continue; // skip QED + } + mPtResolution->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + mPtResolution2D->Fill(mParticleInfo[srcID][evID][trackID].pt, (mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + if (!mParticleInfo[srcID][evID][trackID].isPrimary) { + mPtResolutionSec->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + mPtResolutionPrim->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + + for (int yy = 0; yy < 100; yy++) { + aa[yy] = 0.; + sigma[yy] = 0.; + sigmaerr[yy] = 0.; + meanPt[yy] = 0.; + } + + for (int yy = 0; yy < 100; yy++) { + TH1D* projh2X = mPtResolution2D->ProjectionY("projh2X", yy, yy + 1, ""); + TF1* f1 = new TF1("f1", "gaus", -0.2, 0.2); + projh2X->Fit("f1"); + if (f1->GetParameter(2) > 0. && f1->GetParameter(2) < 1. && f1->GetParameter(1) < 1.) { + sigma[yy] = f1->GetParameter(2); + sigmaerr[yy] = f1->GetParError(2); + meanPt[yy] = ((8. / 100.) * yy + (8. / 100.) * (yy + 1)) / 2; + aa[yy] = 0.0125; + } + } +} + +void TrackCheckStudy::setEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, const int color, const double alpha = 1, const double linew = 2, const int markerStyle = kFullCircle, const double markersize = 1.7) +{ + eff->SetName(name); + eff->SetTitle(title); + eff->SetLineColor(color); + eff->SetLineColorAlpha(color, alpha); + eff->SetMarkerColor(color); + eff->SetMarkerColorAlpha(color, alpha); + eff->SetLineWidth(linew); + eff->SetMarkerStyle(markerStyle); + eff->SetMarkerSize(markersize); + eff->SetDirectory(gDirectory); +} + +void TrackCheckStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void TrackCheckStudy::endOfStream(EndOfStreamContext& ec) +{ + TFile fout(mOutFileName.c_str(), "recreate"); + + setEfficiencyGraph(mEffPt, "Good_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kAzure + 4, 0.65); + fout.WriteTObject(mEffPt.get()); + + setEfficiencyGraph(mEffFakePt, "Fake_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kRed, 0.65); + fout.WriteTObject(mEffFakePt.get()); + + setEfficiencyGraph(mEffPtSec, "Good_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffPtSec.get()); + + setEfficiencyGraph(mEffFakePtSec, "Fake_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakePtSec.get()); + + setEfficiencyGraph(mEffClonesPt, "Clone_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); + fout.WriteTObject(mEffClonesPt.get()); + + setEfficiencyGraph(mEffEta, "Good_eta", ";#eta;efficiency primary particle", kAzure + 4, 0.65); + fout.WriteTObject(mEffEta.get()); + + setEfficiencyGraph(mEffFakeEta, "Fake_eta", ";#eta;efficiency primary particle", kRed + 1, 0.65); + fout.WriteTObject(mEffFakeEta.get()); + + setEfficiencyGraph(mEffEtaSec, "Good_etaSec", ";#eta;efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffEtaSec.get()); + + setEfficiencyGraph(mEffFakeEtaSec, "Fake_etaSec", ";#eta;efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakeEtaSec.get()); + + setEfficiencyGraph(mEffClonesEta, "Clone_eta", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); + fout.WriteTObject(mEffClonesEta.get()); + + for (int aa = 0; aa < 4; aa++) { + setEfficiencyGraph(mEffGoodRad[aa], Form("Good_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodRad[aa], Form("Fake_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Good_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodZ[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Fake_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodZ[aa].get()); + + for (int bb = 0; bb < 4; bb++) { + setEfficiencyGraph(mEffGoodPts[aa][bb], Form("EffPtGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakePts[aa][bb], Form("EffPtFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffGoodEtas[aa][bb], Form("EffEtaGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakeEtas[aa][bb], Form("EffEtaFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + + fout.WriteTObject(mEffGoodPts[aa][bb].get()); + fout.WriteTObject(mEffFakePts[aa][bb].get()); + fout.WriteTObject(mEffGoodEtas[aa][bb].get()); + fout.WriteTObject(mEffFakeEtas[aa][bb].get()); + } + for (int i = 0; i < 3; i++) { + fout.WriteTObject(histLength[aa][i], Form("trk_len_%d_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1Fake[aa][i], Form("trk_len_%d_1f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2Fake[aa][i], Form("trk_len_%d_2f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3Fake[aa][i], Form("trk_len_%d_3f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLengthNoCl[aa][i], Form("trk_len_%d_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1FakeNoCl[aa][i], Form("trk_len_%d_1f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2FakeNoCl[aa][i], Form("trk_len_%d_2f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3FakeNoCl[aa][i], Form("trk_len_%d_3f_nocl_%s", 4 + aa, name[i])); + } + } + + for (int j = 0; j < 4; j++) { + for (int i = 1; i <= 7; i++) { + mClusterFake[j]->GetXaxis()->SetBinLabel(i, ParticleName[i - 1]); + + for (int i = 1; i <= 50; i++) { + mClusterFake[j]->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + if (j == 0) { + processvsZ->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRad->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadOther->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsEtaNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + } + } + fout.WriteTObject(mClusterFake[j].get()); + } + } + fout.WriteTObject(processvsZ.get()); + fout.WriteTObject(processvsRad.get()); + fout.WriteTObject(processvsRadOther.get()); + fout.WriteTObject(processvsRadNotTracked.get()); + fout.WriteTObject(processvsEtaNotTracked.get()); + + // Paint the histograms + // todo: delegate to a dedicated helper + gStyle->SetTitleSize(0.035, "xy"); + gStyle->SetLabelSize(0.035, "xy"); + gStyle->SetPadRightMargin(0.035); + gStyle->SetPadTopMargin(0.035); + gStyle->SetPadLeftMargin(0.19); + gStyle->SetPadBottomMargin(0.17); + gStyle->SetTitleOffset(1.4, "x"); + gStyle->SetTitleOffset(1.1, "y"); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + gStyle->SetGridStyle(3); + gStyle->SetGridWidth(1); + + mCanvasPt = std::make_unique("cPt", "cPt", 1600, 1200); + mCanvasPt->cd(); + mCanvasPt->SetLogx(); + mCanvasPt->SetGrid(); + mEffPt->Draw("pz"); + mEffFakePt->Draw("pz same"); + mEffClonesPt->Draw("pz same"); + mLegendPt = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPt->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPt->AddEntry("Good_pt", "good (100% cluster purity)", "lep"); + mLegendPt->AddEntry("Fake_pt", "fake", "lep"); + mLegendPt->AddEntry("Clone_pt", "clone", "lep"); + mLegendPt->Draw(); + mCanvasPt->SaveAs("eff_pt.png"); + + mCanvasPtSec = std::make_unique("cPtSec", "cPtSec", 1600, 1200); + mCanvasPtSec->cd(); + mCanvasPtSec->SetLogx(); + mCanvasPtSec->SetGrid(); + mEffPtSec->Draw("pz"); + mEffFakePtSec->Draw("pz same"); + mLegendPtSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtSec->AddEntry("Good_ptSec", "good (100% cluster purity)", "lep"); + mLegendPtSec->AddEntry("Fake_tSec", "fake", "lep"); + mLegendPtSec->Draw(); + mCanvasPtSec->SaveAs("eff_ptSec.png"); + + mCanvasEta = std::make_unique("cEta", "cEta", 1600, 1200); + mCanvasEta->cd(); + mCanvasEta->SetGrid(); + mEffEta->Draw("pz"); + mEffFakeEta->Draw("pz same"); + mEffClonesEta->Draw("pz same"); + mLegendEta = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendEta->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendEta->AddEntry("Good_eta", "good (100% cluster purity)", "lep"); + mLegendEta->AddEntry("Fake_eta", "fake", "lep"); + mLegendEta->AddEntry("Clone_eta", "clone", "lep"); + mLegendEta->Draw(); + mCanvasEta->SaveAs("eff_eta.png"); + + mCanvasEtaSec = std::make_unique("cEtaSec", "cEtaSec", 1600, 1200); + mCanvasEtaSec->cd(); + mCanvasEtaSec->SetGrid(); + mEffEtaSec->Draw("pz"); + mEffFakeEtaSec->Draw("pz same"); + mLegendEtaSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendEtaSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendEtaSec->AddEntry("Good_etaSec", "good (100% cluster purity)", "lep"); + mLegendEtaSec->AddEntry("Fake_etaSec", "fake", "lep"); + mLegendEtaSec->Draw(); + mCanvasEtaSec->SaveAs("eff_EtaSec.png"); + + mCanvasRad = std::make_unique("cRad", "cRad", 1600, 1200); + mCanvasRad->cd(); + mCanvasRad->SetGrid(); + mEffGoodRad[3]->Draw("pz"); + mEffFakeRad[3]->Draw("pz same"); + mCanvasRad->SetLogy(); + mLegendRad = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendRad->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendRad->AddEntry(Form("Good_Rad_%s", particleToanalize[3]), "good", "lep"); + mLegendRad->AddEntry(Form("Fake_Rad_%s", particleToanalize[3]), "fake", "lep"); + mLegendRad->Draw(); + mCanvasRad->SaveAs("eff_rad_sec.png"); + + mCanvasZ = std::make_unique("cZ", "cZ", 1600, 1200); + mCanvasZ->cd(); + mCanvasZ->SetGrid(); + mCanvasZ->SetLogy(); + mEffGoodZ[3]->Draw("pz"); + mEffFakeZ[3]->Draw("pz same"); + mCanvasZ->SetLogy(); + mLegendZ = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendZ->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendZ->AddEntry(Form("Good_Z_%s", particleToanalize[3]), "good", "lep"); + mLegendZ->AddEntry(Form("Fake_Z_%s", particleToanalize[3]), "fake", "lep"); + mLegendZ->Draw(); + mCanvasZ->SaveAs("eff_Z_sec.png"); + ; + + mCanvasRadD = std::make_unique("cRadD", "cRadD", 1600, 1200); + mCanvasRadD->cd(); + mCanvasRadD->SetGrid(); + mCanvasRadD->SetLogy(); + mLegendRadD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendRadD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodRad[i]->Draw("pz"); + } else { + mEffGoodRad[i]->Draw("pz same"); + mEffFakeRad[i]->Draw("pz same"); + mLegendRadD->AddEntry(Form("Good_Rad%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendRadD->AddEntry(Form("Fake_Rad%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendRadD->Draw(); + mCanvasRadD->SaveAs("eff_RadD_sec.png"); + + mCanvasZD = std::make_unique("cZD", "cZD", 1600, 1200); + mCanvasZD->cd(); + mCanvasZD->SetGrid(); + mCanvasZD->SetLogy(); + mLegendZD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendZD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodZ[i]->Draw("pz"); + } else { + mEffGoodZ[i]->Draw("pz same"); + mEffFakeZ[i]->Draw("pz same"); + mLegendZD->AddEntry(Form("Good_Z%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendZD->AddEntry(Form("Fake_Z%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendZD->Draw(); + mCanvasZD->SaveAs("eff_ZD_sec.png"); + + mPtResolution->SetName("#it{p}_{T} resolution"); + mPtResolution->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolution->SetFillColor(kAzure + 4); + mPtResolutionPrim->SetFillColor(kRed); + mPtResolutionSec->SetFillColor(kOrange); + mPtResolutionPrim->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolutionSec->SetTitle(";#Delta #it{p}_{T}/#it{p}_{T_{MC}} ;Entries"); + mPtResolution2D->SetTitle(";#it{p}_{T_{MC}} [GeV];#Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + + fout.WriteTObject(mPtResolution.get()); + fout.WriteTObject(mPtResolutionPrim.get()); + fout.WriteTObject(mPtResolutionSec.get()); + fout.WriteTObject(mPtResolution2D.get()); + + mCanvasPtRes = std::make_unique("cPtr", "cPtr", 1600, 1200); + mCanvasPtRes->cd(); + mPtResolution->Draw("HIST"); + mLegendPtRes = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtRes->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtRes->AddEntry("mPtResolution", "All events", "lep"); + mLegendPtRes->Draw(); + mCanvasPtRes->SaveAs("ptRes.png"); + + mCanvasPtRes2 = std::make_unique("cPtr2", "cPtr2", 1600, 1200); + mCanvasPtRes2->cd(); + mPtResolution2D->Draw(); + mCanvasPtRes2->SaveAs("ptRes2.png"); + + mCanvasPtRes3 = std::make_unique("cPtr3", "cPtr3", 1600, 1200); + mCanvasPtRes3->cd(); + + auto* g1 = new TGraphErrors(100, meanPt, sigma, aa, sigmaerr); + g1->SetMarkerStyle(8); + g1->SetMarkerColor(kGreen); + g1->GetXaxis()->SetTitle(" #it{p}_{T} [GeV]"); + g1->GetYaxis()->SetTitle("#sigma #Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + g1->GetYaxis()->SetLimits(0, 1); + g1->GetXaxis()->SetLimits(0, 10.); + g1->Draw("AP"); + g1->GetYaxis()->SetRangeUser(0, 1); + g1->GetXaxis()->SetRangeUser(0, 10.); + mCanvasPtRes3->SaveAs("ptRes3.png"); + + mCanvasPtRes4 = std::make_unique("cPt4", "cPt4", 1600, 1200); + mCanvasPtRes4->cd(); + mPtResolutionPrim->SetName("mPtResolutionPrim"); + mPtResolutionSec->SetName("mPtResolutionSec"); + mPtResolutionPrim->Draw("same hist"); + mPtResolutionSec->Draw("same hist"); + mLegendPtRes2 = std::make_unique(0.19, 0.8, 0.40, 0.96); + + mLegendPtRes2->SetHeader(Form("%zu events PP", mKineReader->getNEvents(0)), "C"); + mLegendPtRes2->AddEntry("mPtResolutionPrim", "Primary events", "f"); + mLegendPtRes2->AddEntry("mPtResolutionSec", "Secondary events", "f"); + mLegendPtRes2->Draw("same"); + mLegendPtRes2->SaveAs("ptRes4.png"); + + auto canvas = new TCanvas("fc_canvas", "Fake clusters", 1600, 1000); + canvas->Divide(4, 2); + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 1); + stackLength[iH]->Draw(); + stackLength[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 5); + stackLength1Fake[iH]->Draw(); + stackLength1Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + + canvas->SaveAs("fakeClusters2.png", "recreate"); + + auto canvas2 = new TCanvas("fc_canvas2", "Fake clusters", 1600, 1000); + canvas2->Divide(4, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 1); + stackLength2Fake[iH]->Draw(); + stackLength2Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 5); + stackLength3Fake[iH]->Draw(); + stackLength3Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + canvas2->SaveAs("fakeClusters3.png", "recreate"); + + auto canvasPtfake = new TCanvas("canvasPtfake", "Fake pt", 1600, 1000); + canvasPtfake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtfake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtfake->cd(iH + 1); + } + if (v == 0) { + mEffFakePts[v][iH]->Draw(); + } else { + mEffFakePts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + } + canvasPtfake->SaveAs("PtforPartFake.png", "recreate"); + + auto canvasPtGood = new TCanvas("canvasPtGood", "Good pt", 1600, 1000); + canvasPtGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodPts[v][iH]->Draw(); + } else { + mEffGoodPts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + } + + auto canvasEtafake = new TCanvas("canvasEtafake", "Fake Eta", 1600, 1000); + canvasEtafake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtafake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtafake->cd(iH + 1); + } + if (v == 0) { + mEffFakeEtas[v][iH]->Draw(); + } else { + mEffFakeEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Fake Tracks, layer %d", iH)); + } + auto canvasEtaGood = new TCanvas("canvasEtaGood", "Good Eta", 1600, 1000); + canvasEtaGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtaGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtaGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodEtas[v][iH]->Draw(); + } else { + mEffGoodEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Good Tracks, layer %d", iH)); + } + + auto canvasI = new TCanvas("canvasI", "canvasI", 1600, 1000); + canvasI->cd(); + mClusterFake[0]->Draw("COLZ"); + canvasI->SaveAs("Iper2D.png", "recreate"); + + auto canvasL = new TCanvas("canvasL", "canvasL", 1600, 1000); + canvasL->cd(); + mClusterFake[1]->Draw("COLZ"); + canvasL->SaveAs("Lam2D.png", "recreate"); + + auto canvasK = new TCanvas("canvasK", "canvasK", 1600, 1000); + canvasK->cd(); + mClusterFake[2]->Draw("COLZ"); + canvasK->SaveAs("K2D.png", "recreate"); + + auto canvasZProd = new TCanvas("canvasZProd", "canvasZProd", 1600, 1000); + canvasZProd->cd(); + processvsZ->Draw("COLZ"); + canvasZProd->SaveAs("prodvsZ.png", "recreate"); + auto canvasRadProd = new TCanvas("canvasRadProd", "canvasRadProd", 1600, 1000); + canvasRadProd->cd(); + processvsRad->Draw("COLZ"); + canvasRadProd->SaveAs("prodvsRad.png", "recreate"); + auto canvasRadProdO = new TCanvas("canvasRadProdO", "canvasRadProdO", 1600, 1000); + canvasRadProdO->cd(); + processvsRadOther->Draw("COLZ"); + canvasRadProdO->SaveAs("prodvsRadO.png", "recreate"); + auto canvasRadProNOTr = new TCanvas("canvasRadProNOTr", "canvasRadProNOTr", 1600, 1000); + canvasRadProNOTr->cd(); + processvsRadNotTracked->Draw("COLZ"); + canvasRadProNOTr->SaveAs("prodvsRadNoTr.png", "recreate"); + auto canvasEtaProNOTr = new TCanvas("canvasEtaProNOTr", "canvasEtaProNOTr", 1600, 1000); + canvasEtaProNOTr->cd(); + processvsEtaNotTracked->Draw("COLZ"); + canvasEtaProNOTr->SaveAs("prodvsEtaNoTr.png", "recreate"); + + fout.cd(); + mCanvasPt->Write(); + mCanvasEta->Write(); + mCanvasPtSec->Write(); + mCanvasEtaSec->Write(); + mCanvasPtRes->Write(); + mCanvasPtRes2->Write(); + mCanvasPtRes3->Write(); + mCanvasPtRes4->Write(); + mCanvasRad->Write(); + mCanvasZ->Write(); + mCanvasRadD->Write(); + mCanvasZD->Write(); + canvas->Write(); + canvas2->Write(); + canvasPtfake->Write(); + canvasI->Write(); + canvasL->Write(); + canvasK->Write(); + fout.Close(); +} + +void TrackCheckStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ +} + +DataProcessorSpec getTrackCheckStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-check-tracks", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, useMC, kineReader, ggRequest)}, + Options{}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx new file mode 100644 index 0000000000000..465365ffa3d86 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx @@ -0,0 +1,655 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITSStudies/Helpers.h" +#include "ITSStudies/TrackExtension.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "ReconstructionDataFormats/DCA.h" + +#include + +#include "TFile.h" +#include "TH1D.h" +#include "TH2D.h" +#include "TEfficiency.h" + +namespace o2::its::study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using o2::steer::MCKinematicsReader; +class TrackExtensionStudy : public Task +{ + struct ParticleInfo { + float eventX; + float eventY; + float eventZ; + int pdg; + float pt; + float eta; + float phi; + int mother; + int first; + float vx; + float vy; + float vz; + uint8_t clusters = 0u; + uint8_t fakeClusters = 0u; + uint8_t isReco = 0u; + uint8_t isFake = 0u; + bool isPrimary = false; + unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + int prodProcess; + o2::its::TrackITS track; + MCTrack mcTrack; + }; + + public: + TrackExtensionStudy(std::shared_ptr dr, + mask_t src, + std::shared_ptr kineReader, + std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mKineReader(kineReader), mGGCCDBRequest(gr) + { + LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); + } + + ~TrackExtensionStudy() final = default; + void init(InitContext& /*ic*/) final; + void run(ProcessingContext& /*pc*/) final; + void endOfStream(EndOfStreamContext& /*ec*/) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + static constexpr std::array mBitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; + static constexpr std::array mBitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; + const std::bitset<7> mTopMask{"1110000"}; + const std::bitset<7> mBotMask{"0000111"}; + + void updateTimeDependentParams(ProcessingContext& pc); + std::string mOutFileName = "TrackExtensionStudy.root"; + std::shared_ptr mKineReader; + GeometryTGeo* mGeometry{}; + + gsl::span mTracksROFRecords; + gsl::span mTracks; + gsl::span mTracksMCLabels; + gsl::span mClusters; + gsl::span mInputITSidxs; + const o2::dataformats::MCLabelContainer* mClustersMCLCont{}; + + GTrackID::mask_t mTracksSrc{}; + std::shared_ptr mDataRequest; + std::vector>> mParticleInfo; // src/event/track + unsigned short mMask = 0x7f; + + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mStream; + bool mWithTree{false}; + + std::unique_ptr mHTrackCounts; + std::unique_ptr mHLengthAny, mHLengthGood, mHLengthFake; + std::unique_ptr mHChi2Any, mHChi2Good, mHChi2Fake; + std::unique_ptr mHPtAny, mHPtGood, mHPtFake; + std::unique_ptr mHExtensionAny, mHExtensionGood, mHExtensionFake; + std::unique_ptr mHExtensionPatternsAny, mHExtensionPatternsGood, mHExtensionPatternsFake, mHExtensionPatternsGoodMissed, mHExtensionPatternsGoodEmpty; + std::unique_ptr mEExtensionNum, mEExtensionDen, mEExtensionPurityNum, mEExtensionPurityDen, mEExtensionFakeNum, mEExtensionFakeDen; + std::unique_ptr mEExtensionFakeBeforeNum, mEExtensionFakeAfterNum, mEExtensionFakeMixNum; + std::unique_ptr mEExtensionTopNum, mEExtensionTopPurityNum, mEExtensionTopFakeNum; + std::unique_ptr mEExtensionBotNum, mEExtensionBotPurityNum, mEExtensionBotFakeNum; + std::unique_ptr mEExtensionMixNum, mEExtensionMixPurityNum, mEExtensionMixFakeNum; + std::array, mBitPatternsBefore.size()> mEExtensionPatternGoodNum, mEExtensionPatternFakeNum; + std::array, mBitPatternsAfter.size()>, mBitPatternsBefore.size()> mEExtensionPatternIndGoodNum, mEExtensionPatternIndFakeNum; + // DCA + std::unique_ptr mDCAxyVsPtPionsNormal, mDCAxyVsPtPionsExtended; + std::unique_ptr mDCAzVsPtPionsNormal, mDCAzVsPtPionsExtended; + + template + std::unique_ptr createHistogram(C... n, F... b) + { + auto t = std::make_unique(n..., b...); + mHistograms.push_back(static_cast(t.get())); + return std::move(t); + } + std::vector mHistograms; +}; + +void TrackExtensionStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mWithTree = ic.options().get("with-tree"); + + constexpr size_t effHistBins = 40; + constexpr float effPtCutLow = 0.01; + constexpr float effPtCutHigh = 10.; + auto xbins = helpers::makeLogBinning(effHistBins, effPtCutLow, effPtCutHigh); + + // Track Counting + mHTrackCounts = createHistogram("hTrackCounts", "Track Stats", 10, 0, 10); + mHTrackCounts->GetXaxis()->SetBinLabel(1, "Total Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(2, "Normal ANY Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(3, "Normal GOOD Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(4, "Normal FAKE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(5, "Extended ANY Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(6, "Extended GOOD Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(7, "Extended FAKE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(8, "Extended FAKE BEFORE Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(9, "Extended FAKE AFTER Tracks"); + mHTrackCounts->GetXaxis()->SetBinLabel(10, "Extended FAKE BEFORE&AFTER Tracks"); + + // Length + mHLengthAny = createHistogram("hLengthAny", "Extended Tracks Length (ANY);NCluster;Entries", 5, 3, 8); + mHLengthGood = createHistogram("hLengthGood", "Extended Tracks Length (GOOD);NCluster;Entries", 5, 3, 8); + mHLengthFake = createHistogram("hLengthFake", "Extended Tracks Length (FAKE);NCluster;Entries", 5, 3, 8); + + // Chi2 + mHChi2Any = createHistogram("hChi2Any", "Extended Tracks Length (ANY);#chi^{2};Entries", 50, 0, 100); + mHChi2Good = createHistogram("hChi2Good", "Extended Tracks Length (GOOD);#chi^{2};Entries", 50, 0, 100); + mHChi2Fake = createHistogram("hChi2Fake", "Extended Tracks Length (FAKE);#chi^{2};Entries", 50, 0, 100); + + // Pt + mHPtAny = createHistogram("hPtAny", "Extended Tracks Length (ANY);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtGood = createHistogram("hPtGood", "Extended Tracks Length (GOOD);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtFake = createHistogram("hPtFake", "Extended Tracks Length (FAKE);#it{p}_{T};Entries", effHistBins, xbins.data()); + + // Length + mHExtensionAny = createHistogram("hExtensionAny", "Extended Tracks Length (ANY);Extended Layer;Entries", 7, 0, 7); + mHExtensionGood = createHistogram("hExtensionGood", "Extended Tracks Length (GOOD);Extended Layer;Entries", 7, 0, 7); + mHExtensionFake = createHistogram("hExtensionFake", "Extended Tracks Length (FAKE);Extended Layer;Entries", 7, 0, 7); + + // Patterns + auto makePatternAxisLabels = [&](TH1* h, bool xBefore = true) { + for (int i{1}; i <= h->GetXaxis()->GetNbins(); ++i) { + if (xBefore) { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsBefore[i - 1]).c_str()); + } else { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); + } + } + for (int i{1}; i <= h->GetYaxis()->GetNbins(); ++i) { + h->GetYaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); + } + }; + mHExtensionPatternsAny = createHistogram("hExtensionPatternsAny", "Extended Tracks Pattern (ANY);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsAny.get()); + mHExtensionPatternsGood = createHistogram("hExtensionPatternsGood", "Extended Tracks Pattern (GOOD);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGood.get()); + mHExtensionPatternsFake = createHistogram("hExtensionPatternsFake", "Extended Tracks Pattern (FAKE);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsFake.get()); + mHExtensionPatternsGoodMissed = createHistogram("hExtensionPatternsGoodMissed", "Extended Tracks Pattern (GOOD) Missed Clusters;After;Missed;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodMissed.get(), false); + mHExtensionPatternsGoodEmpty = createHistogram("hExtensionPatternsGoodEmpty", "Extended Tracks Pattern (GOOD) Empty Clusters;Before;After;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodEmpty.get(), false); + + /// Effiencies + mEExtensionNum = createHistogram("hExtensionNum", "Extension Numerator", effHistBins, xbins.data()); + mEExtensionDen = createHistogram("hExtensionDen", "Extension Dennominator", effHistBins, xbins.data()); + // Purity + mEExtensionPurityNum = createHistogram("hExtensionPurityNum", "Extension Purity Numerator", effHistBins, xbins.data()); + mEExtensionPurityDen = createHistogram("hExtensionPurityDen", "Extension Purity Denominator", effHistBins, xbins.data()); + // Fake + mEExtensionFakeNum = createHistogram("hExtensionFakeNum", "Extension Fake Numerator", effHistBins, xbins.data()); + mEExtensionFakeDen = createHistogram("hExtensionFakeDen", "Extension Fake Denominator", effHistBins, xbins.data()); + mEExtensionFakeBeforeNum = createHistogram("hExtensionFakeBeforeNum", "Extension Fake Before Numerator", effHistBins, xbins.data()); + mEExtensionFakeAfterNum = createHistogram("hExtensionFakeAfterNum", "Extension Fake After Numerator", effHistBins, xbins.data()); + mEExtensionFakeMixNum = createHistogram("hExtensionFakeMixNum", "Extension Fake Mix Numerator", effHistBins, xbins.data()); + // Top + mEExtensionTopNum = createHistogram("hExtensionTopNum", "Extension Top Numerator", effHistBins, xbins.data()); + mEExtensionTopPurityNum = createHistogram("hExtensionTopPurityNum", "Extension Top Purity Numerator", effHistBins, xbins.data()); + mEExtensionTopFakeNum = createHistogram("hExtensionTopFakeNum", "Extension Top Fake Numerator", effHistBins, xbins.data()); + mEExtensionBotNum = createHistogram("hExtensionBotNum", "Extension Bot Numerator", effHistBins, xbins.data()); + mEExtensionBotPurityNum = createHistogram("hExtensionBotPurityNum", "Extension Bot Purity Numerator", effHistBins, xbins.data()); + mEExtensionBotFakeNum = createHistogram("hExtensionBotFakeNum", "Extension Bot Fake Numerator", effHistBins, xbins.data()); + mEExtensionMixNum = createHistogram("hExtensionMixNum", "Extension Mix Numerator", effHistBins, xbins.data()); + mEExtensionMixPurityNum = createHistogram("hExtensionMixPurityNum", "Extension Mix Purity Numerator", effHistBins, xbins.data()); + mEExtensionMixFakeNum = createHistogram("hExtensionMixFakeNum", "Extension Mix Fake Numerator", effHistBins, xbins.data()); + // Patterns + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + mEExtensionPatternGoodNum[i] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternFakeNum[i] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + mEExtensionPatternIndGoodNum[i][j] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternIndFakeNum[i][j] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + } + } + + /// DCA + mDCAxyVsPtPionsNormal = createHistogram("hDCAxyVsPtResNormal", "DCA_{#it{xy}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAxyVsPtPionsExtended = createHistogram("hDCAxyVsPtResExtended", "DCA_{#it{xy}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsNormal = createHistogram("hDCAzVsPtResNormal", "DCA_{#it{z}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsExtended = createHistogram("hDCAzVsPtResExtended", "DCA_{#it{z}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + + mStream = std::make_unique(mOutFileName.c_str(), "RECREATE"); +} + +void TrackExtensionStudy::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest); + updateTimeDependentParams(pc); + + mTracksROFRecords = recoData.getITSTracksROFRecords(); + mTracks = recoData.getITSTracks(); + mTracksMCLabels = recoData.getITSTracksMCLabels(); + mClusters = recoData.getITSClusters(); + mClustersMCLCont = recoData.getITSClustersMCLabels(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); + + LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", + mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); + LOGP(info, "** Found {} sources from kinematic files", mKineReader->getNSources()); + + process(); +} + +void TrackExtensionStudy::process() +{ + LOGP(info, "** Filling particle table ... "); + mParticleInfo.resize(mKineReader->getNSources()); // sources + for (int iSource{0}; iSource < mKineReader->getNSources(); ++iSource) { + mParticleInfo[iSource].resize(mKineReader->getNEvents(iSource)); // events + for (int iEvent{0}; iEvent < mKineReader->getNEvents(iSource); ++iEvent) { + const auto& mcEvent = mKineReader->getMCEventHeader(iSource, iEvent); + mParticleInfo[iSource][iEvent].resize(mKineReader->getTracks(iSource, iEvent).size()); // tracks + for (auto iPart{0}; iPart < mKineReader->getTracks(iEvent).size(); ++iPart) { + const auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; + mParticleInfo[iSource][iEvent][iPart].eventX = mcEvent.GetX(); + mParticleInfo[iSource][iEvent][iPart].eventY = mcEvent.GetY(); + mParticleInfo[iSource][iEvent][iPart].eventZ = mcEvent.GetZ(); + mParticleInfo[iSource][iEvent][iPart].pdg = part.GetPdgCode(); + mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); + mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); + mParticleInfo[iSource][iEvent][iPart].eta = part.GetEta(); + mParticleInfo[iSource][iEvent][iPart].vx = part.Vx(); + mParticleInfo[iSource][iEvent][iPart].vy = part.Vy(); + mParticleInfo[iSource][iEvent][iPart].vz = part.Vz(); + mParticleInfo[iSource][iEvent][iPart].isPrimary = part.isPrimary(); + mParticleInfo[iSource][iEvent][iPart].mother = part.getMotherTrackId(); + mParticleInfo[iSource][iEvent][iPart].prodProcess = part.getProcess(); + } + } + } + LOGP(info, "** Creating particle/clusters correspondance ... "); + for (auto iSource{0}; iSource < mParticleInfo.size(); ++iSource) { + for (auto iCluster{0}; iCluster < mClusters.size(); ++iCluster) { + auto labs = mClustersMCLCont->getLabels(iCluster); // ideally I can have more than one label per cluster + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + auto& cluster = mClusters[iCluster]; + auto layer = mGeometry->getLayer(cluster.getSensorID()); + mParticleInfo[srcID][evID][trackID].clusters |= (1 << layer); + if (fake) { + mParticleInfo[srcID][evID][trackID].fakeClusters |= (1 << layer); + } + } + } + } + + LOGP(info, "** Analysing tracks ... "); + int unaccounted{0}, good{0}, fakes{0}, extended{0}; + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + const auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isValid()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + + if (srcID == 99) { // skip QED + unaccounted++; + continue; + } + + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (mTracks[iTrack].isExtendedOnLayer(iLayer)) { + ++extended; + break; + } + } + + mParticleInfo[srcID][evID][trackID].isReco += !fake; + mParticleInfo[srcID][evID][trackID].isFake += fake; + if (mTracks[iTrack].isBetter(mParticleInfo[srcID][evID][trackID].track, 1.e9)) { + mParticleInfo[srcID][evID][trackID].storedStatus = fake; + mParticleInfo[srcID][evID][trackID].track = mTracks[iTrack]; + mParticleInfo[srcID][evID][trackID].mcTrack = *mKineReader->getTrack(lab); + } + fakes += fake; + good += !fake; + } + LOGP(info, "** Some statistics:"); + LOGP(info, "\t- Total number of tracks: {}", mTracks.size()); + LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f} %)", unaccounted, unaccounted * 100. / mTracks.size()); + LOGP(info, "\t- Total number of fakes: {} ({:.2f} %)", fakes, fakes * 100. / mTracks.size()); + LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); + LOGP(info, "\t- Total number of extensions: {} ({:.2f} %)", extended, extended * 100. / mTracks.size()); + + o2::dataformats::VertexBase collision; + o2::dataformats::DCA impactParameter; + LOGP(info, "** Filling histograms ... "); + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isValid()) { + unaccounted++; + continue; + } + int trackID, evID, srcID; + bool fake; + lab.get(trackID, evID, srcID, fake); + const auto& part = mParticleInfo[srcID][evID][trackID]; + if (!part.isPrimary) { + continue; + } + const auto& trk = part.track; + bool isGood = part.isReco && !part.isFake; + mHTrackCounts->Fill(0); + + std::bitset<7> extPattern{0}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (trk.isExtendedOnLayer(iLayer)) { + extPattern.set(iLayer); + } + } + + // Tree + while (mWithTree) { + constexpr float refRadius{70.f}; + constexpr float maxSnp{0.9f}; + auto cTrk = trk; + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(cTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; + } + std::array xyz{(float)part.mcTrack.GetStartVertexCoordinatesX(), (float)part.mcTrack.GetStartVertexCoordinatesY(), (float)part.mcTrack.GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)part.mcTrack.GetStartVertexMomentumX(), (float)part.mcTrack.GetStartVertexMomentumY(), (float)part.mcTrack.GetStartVertexMomentumZ()}; + auto pdg = O2DatabasePDG::Instance()->GetParticle(part.pdg); + if (pdg == nullptr) { + LOGP(error, "MC info not available"); + break; + } + auto mcTrk = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pdg->Charge() / 3.), true); + if (!mcTrk.rotate(cTrk.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(mcTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; + } + (*mStream) << "tree" + << "trk=" << cTrk + << "mcTrk=" << mcTrk + << "isGood=" << isGood + << "isExtended=" << extPattern.any() + << "\n"; + break; + } + + // impact parameter + while (isGood && std::abs(part.pdg) == 211) { + auto trkC = part.track; + collision.setXYZ(part.eventX, part.eventY, part.eventZ); + if (!o2::base::Propagator::Instance()->propagateToDCA(collision, trkC, o2::base::Propagator::Instance()->getNominalBz(), 2.0, o2::base::Propagator::MatCorrType::USEMatCorrTGeo, &impactParameter)) { + break; + } + + auto dcaXY = impactParameter.getY() * 1e4; + auto dcaZ = impactParameter.getZ() * 1e4; + if (!extPattern.any()) { + mDCAxyVsPtPionsNormal->Fill(part.pt, dcaXY); + mDCAzVsPtPionsNormal->Fill(part.pt, dcaZ); + } else { + mDCAxyVsPtPionsExtended->Fill(part.pt, dcaXY); + mDCAzVsPtPionsExtended->Fill(part.pt, dcaZ); + } + break; + } + + mEExtensionDen->Fill(trk.getPt()); + + if (!extPattern.any()) { + mHTrackCounts->Fill(1); + if (part.isReco || !part.isFake) { + mHTrackCounts->Fill(2); + } else { + mHTrackCounts->Fill(3); + } + continue; + } + + mHTrackCounts->Fill(4); + mHLengthAny->Fill(trk.getNClusters()); + mHChi2Any->Fill(trk.getChi2()); + mHPtAny->Fill(trk.getPt()); + mEExtensionNum->Fill(trk.getPt()); + mEExtensionPurityDen->Fill(trk.getPt()); + mEExtensionFakeDen->Fill(trk.getPt()); + if (isGood) { + mHTrackCounts->Fill(5); + mHLengthGood->Fill(trk.getNClusters()); + mHChi2Good->Fill(trk.getChi2()); + mHPtGood->Fill(trk.getPt()); + mEExtensionPurityNum->Fill(trk.getPt()); + } else { + mHTrackCounts->Fill(6); + mHLengthFake->Fill(trk.getNClusters()); + mHChi2Fake->Fill(trk.getChi2()); + mHPtFake->Fill(trk.getPt()); + mEExtensionFakeNum->Fill(trk.getPt()); + } + + std::bitset<7> clusPattern{static_cast(trk.getPattern())}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (extPattern.test(iLayer)) { + extPattern.set(iLayer); + mHExtensionAny->Fill(iLayer); + if (isGood) { + mHExtensionGood->Fill(iLayer); + } else { + mHExtensionFake->Fill(iLayer); + } + } + } + std::bitset<7> oldPattern{clusPattern & ~extPattern}, holePattern{clusPattern}; + holePattern.flip(); + auto clusN = clusPattern.to_ulong(); + auto clusIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), clusN)); + auto oldN = oldPattern.to_ulong(); + auto oldIdx = std::distance(std::begin(mBitPatternsBefore), std::find(std::begin(mBitPatternsBefore), std::end(mBitPatternsBefore), oldN)); + mHExtensionPatternsAny->Fill(oldIdx, clusIdx); + if (isGood) { + mHExtensionPatternsGood->Fill(oldIdx, clusIdx); + mEExtensionPatternGoodNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndGoodNum[oldIdx][clusIdx]->Fill(trk.getPt()); + } else { + mHExtensionPatternsFake->Fill(oldIdx, clusIdx); + mEExtensionPatternFakeNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndFakeNum[oldIdx][clusIdx]->Fill(trk.getPt()); + } + + // old pattern + bool oldFake{false}, newFake{false}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (trk.isFakeOnLayer(iLayer)) { + if (oldPattern.test(iLayer)) { + oldFake = true; + } else if (extPattern.test(iLayer)) { + newFake = true; + } + } + } + if (oldFake && newFake) { + mHTrackCounts->Fill(9); + mEExtensionFakeMixNum->Fill(trk.getPt()); + } else if (oldFake) { + mHTrackCounts->Fill(7); + mEExtensionFakeBeforeNum->Fill(trk.getPt()); + } else if (newFake) { + mHTrackCounts->Fill(8); + mEExtensionFakeAfterNum->Fill(trk.getPt()); + } + + // Check if we missed some clusters + if (isGood && holePattern.any()) { + auto missPattern{clusPattern}, emptyPattern{clusPattern}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (!holePattern.test(iLayer)) { + continue; + } + + // Check if there was actually a cluster that we missed + if ((part.clusters & (1 << iLayer)) != 0) { + missPattern.set(iLayer); + } else { + emptyPattern.set(iLayer); + } + } + + if (missPattern != clusPattern) { + auto missN = missPattern.to_ulong(); + auto missIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), missN)); + mHExtensionPatternsGoodMissed->Fill(clusIdx, missIdx); + } + if (emptyPattern != clusPattern) { + auto emptyN = emptyPattern.to_ulong(); + auto emptyIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), emptyN)); + mHExtensionPatternsGoodEmpty->Fill(clusIdx, emptyIdx); + } + } + + // Top/Bot/Mixed Extension + bool isTop = (extPattern & mTopMask).any(); + bool isBot = (extPattern & mBotMask).any(); + if (isTop && isBot) { + mEExtensionMixNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionMixPurityNum->Fill(trk.getPt()); + } else { + mEExtensionMixFakeNum->Fill(trk.getPt()); + } + } else if (isTop) { + mEExtensionTopNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionTopPurityNum->Fill(trk.getPt()); + } else { + mEExtensionTopFakeNum->Fill(trk.getPt()); + } + } else { + mEExtensionBotNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionBotPurityNum->Fill(trk.getPt()); + } else { + mEExtensionBotFakeNum->Fill(trk.getPt()); + } + } + } +} + +void TrackExtensionStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + static bool initOnceDone = false; + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + mGeometry = GeometryTGeo::Instance(); + mGeometry->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void TrackExtensionStudy::endOfStream(EndOfStreamContext& ec) +{ + LOGP(info, "Writing results to {}", mOutFileName); + mStream->GetFile()->cd(); + for (const auto h : mHistograms) { + h->Write(); + } + + LOGP(info, "Calculating efficiencies"); + auto makeEff = [](auto num, auto den, const char* name, const char* title) { + auto e = std::make_unique(*num, *den); + e->SetName(name); + e->SetTitle(title); + e->Write(); + }; + makeEff(mEExtensionNum.get(), mEExtensionDen.get(), "eExtension", "Track Extension EXT TRK/ALL"); + makeEff(mEExtensionPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionPurity", "Track Extension Purity GOOD/EXT TRK"); + makeEff(mEExtensionFakeNum.get(), mEExtensionFakeDen.get(), "eExtensionFake", "Track Extension Fake FAKE/EXT TRK"); + makeEff(mEExtensionFakeBeforeNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeBefore", "Track Extension Fake FAKE BEF/FAKE EXT TRK"); + makeEff(mEExtensionFakeAfterNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeAfter", "Track Extension Fake FAKE AFT/FAKE EXT TRK"); + makeEff(mEExtensionFakeMixNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeMix", "Track Extension Fake FAKE MIX/FAKE EXT TRK"); + makeEff(mEExtensionTopNum.get(), mEExtensionDen.get(), "eExtensionTop", "Track Extension Top"); + makeEff(mEExtensionTopPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionTopPurity", "Track Extension Purity GOOD TOP/EXT TRK"); + makeEff(mEExtensionTopFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionTopFake", "Track Extension FAKE TOP/EXT FAKE TRK"); + makeEff(mEExtensionBotNum.get(), mEExtensionDen.get(), "eExtensionBot", "Track Extension Bot"); + makeEff(mEExtensionBotPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionBotPurity", "Track Extension Purity GOOD BOT/EXT TRK"); + makeEff(mEExtensionBotFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionBotFake", "Track Extension FAKE BOT/EXT FAKE TRK"); + makeEff(mEExtensionMixNum.get(), mEExtensionDen.get(), "eExtensionMix", "Track Extension Mix"); + makeEff(mEExtensionMixPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionMixPurity", "Track Extension Purity GOOD MIX/EXT TRK"); + makeEff(mEExtensionMixFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionMixFake", "Track Extension FAKE MIX/EXT FAKE TRK"); + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + makeEff(mEExtensionPatternGoodNum[i].get(), mEExtensionPurityNum.get(), fmt::format("eExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + makeEff(mEExtensionPatternFakeNum[i].get(), mEExtensionFakeNum.get(), fmt::format("eExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + makeEff(mEExtensionPatternIndGoodNum[i][j].get(), mEExtensionPatternGoodNum[i].get(), fmt::format("eExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + makeEff(mEExtensionPatternIndFakeNum[i][j].get(), mEExtensionPatternFakeNum[i].get(), fmt::format("eExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + } + } + + mStream->Close(); +} + +void TrackExtensionStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, true); + dataRequest->requestClusters(srcClustersMask, true); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + + return DataProcessorSpec{ + "its-study-track-extension", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, kineReader, ggRequest)}, + Options{{"with-tree", o2::framework::VariantType::Bool, false, {"Produce in addition a tree"}}}}; +} + +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..8ec0d3b2ff325 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_executable(postprocessing-workflow + COMPONENT_NAME its-standalone + SOURCES standalone-postprocessing-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::ITSPostprocessing) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx new file mode 100644 index 0000000000000..30fb39c77f235 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -0,0 +1,143 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Framework/CallbacksPolicy.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" + +// Include studies hereafter +#include "ITSStudies/ImpactParameter.h" +#include "ITSStudies/AvgClusSize.h" +#include "ITSStudies/PIDStudy.h" +#include "ITSStudies/AnomalyStudy.h" +#include "ITSStudies/Efficiency.h" +#include "ITSStudies/TrackCheck.h" +#include "ITSStudies/TrackExtension.h" +#include "Steer/MCKinematicsReader.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"input-from-upstream", VariantType::Bool, false, {"read clusters from the clusterer"}}, + {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, std::string{"ITS"}, {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"cluster-size-study", VariantType::Bool, false, {"Perform the average cluster size study"}}, + {"pid-study", VariantType::Bool, false, {"Perform the PID study"}}, + {"track-study", VariantType::Bool, false, {"Perform the track study"}}, + {"impact-parameter-study", VariantType::Bool, false, {"Perform the impact parameter study"}}, + {"anomaly-study", VariantType::Bool, false, {"Perform the anomaly study"}}, + {"track-extension-study", VariantType::Bool, false, {"Perform the track extension study"}}, + {"efficiency-study", VariantType::Bool, false, {"Perform the efficiency study"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + // o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); + std::swap(workflowOptions, options); +} + +#include + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + GID::mask_t srcTrc, srcCls; + + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + auto useMC = !configcontext.options().get("disable-mc"); + + std::shared_ptr mcKinematicsReader; + if (useMC) { + mcKinematicsReader = std::make_shared("collisioncontext.root"); + } + bool anyStudy{false}; + // Declare specs related to studies hereafter + if (configcontext.options().get("impact-parameter-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getImpactParameterStudy(srcTrc, srcCls, useMC)); + } + if (configcontext.options().get("cluster-size-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getAvgClusSizeStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); + } + if (configcontext.options().get("pid-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getPIDStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); + } + if (configcontext.options().get("track-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getTrackCheckStudy(GID::getSourcesMask("ITS"), GID::getSourcesMask("ITS"), useMC, mcKinematicsReader)); + } + if (configcontext.options().get("anomaly-study")) { + anyStudy = true; + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getAnomalyStudy(srcCls, useMC)); + } + if (configcontext.options().get("track-extension-study")) { + if (!useMC) { + LOGP(fatal, "Track Extension Study needs MC!"); + } + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask("ITS"); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getTrackExtensionStudy(srcTrc, srcCls, mcKinematicsReader)); + } + if (configcontext.options().get("efficiency-study")) { + anyStudy = true; + srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); + srcCls = GID::getSourcesMask(configcontext.options().get("cluster-sources")); + if (!configcontext.options().get("input-from-upstream")) { + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); + } + specs.emplace_back(o2::its::study::getEfficiencyStudy(GID::getSourcesMask("ITS"), GID::getSourcesMask("ITS"), useMC, mcKinematicsReader)); + } + if (!anyStudy) { + LOGP(info, "No study selected, dryrunning"); + } + + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + // write the configuration used for the studies workflow + o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); + + return std::move(specs); +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index a625987bf59e5..d2126be1da2c6 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -9,19 +9,17 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -o2_add_library( - ITSReconstruction - SOURCES src/ClustererTask.cxx src/CookedTracker.cxx src/CookedConfigParam.cxx - src/RecoGeomHelper.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx - PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTReconstruction O2::DataFormatsITS - O2::CommonUtils) +o2_add_library(ITSReconstruction + SOURCES src/RecoGeomHelper.cxx + src/FastMultEstConfig.cxx + src/FastMultEst.cxx + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::CommonUtils) o2_target_root_dictionary( ITSReconstruction - HEADERS include/ITSReconstruction/ClustererTask.h - include/ITSReconstruction/CookedTracker.h - include/ITSReconstruction/CookedConfigParam.h - include/ITSReconstruction/RecoGeomHelper.h + HEADERS include/ITSReconstruction/RecoGeomHelper.h include/ITSReconstruction/FastMultEst.h - include/ITSReconstruction/FastMultEstConfig.h - LINKDEF src/CookedTrackerLinkDef.h) + include/ITSReconstruction/FastMultEstConfig.h) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h deleted file mode 100644 index 16ac9dd63c631..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClustererTask.h -/// \brief Definition of the ITS cluster finder task - -#ifndef ALICEO2_ITS_CLUSTERERTASK -#define ALICEO2_ITS_CLUSTERERTASK - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/PixelReader.h" -#include "ITSMFTReconstruction/RawPixelReader.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include -#include - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} - -namespace its -{ - -class ClustererTask -{ - using Clusterer = o2::itsmft::Clusterer; - using CompCluster = o2::itsmft::CompCluster; - using CompClusterExt = o2::itsmft::CompClusterExt; - using MCTruth = o2::dataformats::MCTruthContainer; - - public: - ClustererTask(bool useMC = true, bool raw = false); - ~ClustererTask(); - - void Init(); - Clusterer& getClusterer() { return mClusterer; } - void run(const std::string inpName, const std::string outName); - o2::itsmft::PixelReader* getReader() const { return (o2::itsmft::PixelReader*)mReader; } - - void writeTree(std::string basename, int i); - void setMaxROframe(int max) { maxROframe = max; } - int getMaxROframe() const { return maxROframe; } - - private: - int maxROframe = std::numeric_limits::max(); ///< maximal number of RO frames per a file - bool mRawDataMode = false; ///< input from raw data or MC digits - bool mUseMCTruth = true; ///< flag to use MCtruth if available - o2::itsmft::PixelReader* mReader = nullptr; ///< Pointer on the relevant Pixel reader - std::unique_ptr mReaderMC; ///< reader for MC data - std::unique_ptr> mReaderRaw; ///< reader for raw data - - Clusterer mClusterer; ///< Cluster finder - - std::vector mCompClus; //!< vector of compact clusters - - std::vector mROFRecVec; //!< vector of ROFRecord references - - MCTruth mClsLabels; //! MC labels - - std::vector mPatterns; - - ClassDefNV(ClustererTask, 2); -}; -} // namespace its -} // namespace o2 - -#endif /* ALICEO2_ITS_CLUSTERERTASK */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h deleted file mode 100644 index bfc111d0a3803..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ALICEO2_COOKEDTRACKINGPARAM_H_ -#define ALICEO2_COOKEDTRACKINGPARAM_H_ - -#include "CommonUtils/ConfigurableParamHelper.h" - -namespace o2 -{ -namespace its -{ - -struct CookedConfigParam : public o2::conf::ConfigurableParamHelper { - // seed "windows" in z and phi: makeSeeds - float zWin = 0.33; - float minPt = 0.05; - // Maximal accepted impact parameters for the seeds - float maxDCAxy = 3.; - float maxDCAz = 3.; - // Space-point resolution - float sigma = 0.0005; - // Tracking "road" from layer to layer - float roadY = 0.2; - float roadZ = 0.3; - // Minimal number of attached clusters - int minNumberOfClusters = 4; - - O2ParamDef(CookedConfigParam, "ITSCookedTracker"); -}; - -} // namespace its -} // namespace o2 -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h deleted file mode 100644 index 918f7f82cbff8..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CookedTracker.h -/// \brief Definition of the "Cooked Matrix" ITS tracker -/// \author iouri.belikov@cern.ch - -#ifndef ALICEO2_ITS_COOKEDTRACKER_H -#define ALICEO2_ITS_COOKEDTRACKER_H - -//------------------------------------------------------------------------- -// A stand-alone ITS tracker -// The pattern recongintion based on the "cooked covariance" approach -//------------------------------------------------------------------------- - -#include -#include -#include "ITSBase/GeometryTGeo.h" -#include "MathUtils/Cartesian.h" -#include "DataFormatsITSMFT/Cluster.h" -#include "DataFormatsITS/TrackITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "ReconstructionDataFormats/Vertex.h" -#include "ITSReconstruction/CookedConfigParam.h" - -using Point3Df = o2::math_utils::Point3D; - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} -namespace itsmft -{ -class TopologyDictionary; -class CompClusterExt; -} // namespace itsmft -namespace its -{ -class CookedTracker -{ - using Cluster = o2::itsmft::Cluster; - using CompClusterExt = o2::itsmft::CompClusterExt; - using Vertex = o2::dataformats::Vertex>; - - public: - CookedTracker(Int_t nThreads = 1); - CookedTracker(const CookedTracker&) = delete; - CookedTracker& operator=(const CookedTracker& tr) = delete; - ~CookedTracker() = default; - - void setConfigParams() - { - const auto& par = CookedConfigParam::Instance(); - LOG(info) << " Setting configurable parameters..."; - - gzWin = par.zWin; - gminPt = par.minPt; - gmaxDCAxy = par.maxDCAxy; - gmaxDCAz = par.maxDCAz; - gSigma2 = par.sigma * par.sigma; - gRoadY = par.roadY; - gRoadZ = par.roadZ; - gminNumberOfClusters = par.minNumberOfClusters; - } - void setParameters(const std::vector& par) - { - gzWin = par[0]; - gminPt = par[1]; - gmaxDCAxy = par[3]; - gmaxDCAz = par[4]; - gSeedingLayer1 = par[5]; - gSeedingLayer2 = par[6]; - gSeedingLayer3 = par[7]; - gSigma2 = par[8] * par[8]; - gmaxChi2PerCluster = par[9]; - gmaxChi2PerTrack = par[10]; - gRoadY = par[11]; - gRoadZ = par[12]; - gminNumberOfClusters = par[13]; - } - void setParametersCosmics() - { - // seed "windows" in z and phi: makeSeeds - gzWin = 84.; // length of the L3 - gminPt = 10.; - // Maximal accepted impact parameters for the seeds - gmaxDCAxy = 19.4; // radius of the L3 - gmaxDCAz = 42.; // half-lenght of the L3 - // Space point resolution - gSigma2 = 0.2 * 0.2; - // Tracking "road" from layer to layer - gRoadY = 1.5; // Chip size in Y - gRoadZ = 3.0; // Chip size in Z - } - - void setVertices(const std::vector& vertices) - { - mVertices = &vertices; - } - - Double_t getX() const { return mX; } - Double_t getY() const { return mY; } - Double_t getZ() const { return mZ; } - Double_t getSigmaX() const { return mSigmaX; } - Double_t getSigmaY() const { return mSigmaY; } - Double_t getSigmaZ() const { return mSigmaZ; } - o2::MCCompLabel cookLabel(TrackITSExt& t, Float_t wrong) const; - void setExternalIndices(TrackITSExt& t) const; - Double_t getBz() const; - void setBz(Double_t bz) { mBz = bz; } - - void setNumberOfThreads(Int_t n) { mNumOfThreads = n; } - Int_t getNumberOfThreads() const { return mNumOfThreads; } - - using TrackInserter = std::function; - // These functions must be implemented - template - void process(gsl::span clusters, gsl::span::iterator& it, const o2::itsmft::TopologyDictionary* dict, U& tracks, V& clusIdx, o2::itsmft::ROFRecord& rof) - { - TrackInserter inserter = [&tracks, &clusIdx, this](const TrackITSExt& t) -> int { - // convert internal track to output format - auto& trackNew = tracks.emplace_back(t); - int noc = t.getNumberOfClusters(); - int clEntry = clusIdx.size(); - for (int i = 0; i < noc; i++) { - const Cluster* c = this->getCluster(t.getClusterIndex(i)); - Int_t idx = c - &mClusterCache[0]; // Index of this cluster in event - clusIdx.emplace_back(this->mFirstInFrame + idx); - } - trackNew.setClusterRefs(clEntry, noc); - trackNew.setPattern(0x7f); // this tracker finds only complete tracks - return tracks.size(); - }; - process(clusters, it, dict, inserter, rof); - } - void process(gsl::span const& clusters, gsl::span::iterator& it, const o2::itsmft::TopologyDictionary* dict, TrackInserter& inserter, o2::itsmft::ROFRecord& rof); - const Cluster* getCluster(Int_t index) const; - - void setGeometry(o2::its::GeometryTGeo* geom); - void setMCTruthContainers(const o2::dataformats::MCTruthContainer* clsLabels, std::vector* trkLabels) - { - mClsLabels = clsLabels; - mTrkLabels = trkLabels; - } - - void setContinuousMode(bool mode) { mContinuousMode = mode; } - bool getContinuousMode() { return mContinuousMode; } - - static void setMostProbablePt(float pt) { mMostProbablePt = pt; } - static auto getMostProbablePt() { return mMostProbablePt; } - - // internal helper classes - class ThreadData; - class Layer; - - protected: - static constexpr int kNLayers = 7; - int loadClusters(); - void unloadClusters(); - std::tuple processLoadedClusters(TrackInserter& inserter); - - std::vector trackInThread(Int_t first, Int_t last); - o2::its::TrackITSExt cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz); - void makeSeeds(std::vector& seeds, Int_t first, Int_t last); - void trackSeeds(std::vector& seeds); - - Bool_t attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSExt& t, const TrackITSExt& o) const; - - void makeBackPropParam(std::vector& seeds) const; - bool makeBackPropParam(TrackITSExt& track) const; - - private: - /*** Tracking parameters ***/ - // seed "windows" in z and phi: makeSeeds - static Float_t gzWin; - static Float_t gminPt; - static Float_t mMostProbablePt; ///< settable most probable pt - // Maximal accepted impact parameters for the seeds - static Float_t gmaxDCAxy; - static Float_t gmaxDCAz; - // Layers for the seeding - static Int_t gSeedingLayer1; - static Int_t gSeedingLayer2; - static Int_t gSeedingLayer3; - // Space point resolution - static Float_t gSigma2; - // Max accepted chi2 - static Float_t gmaxChi2PerCluster; - static Float_t gmaxChi2PerTrack; - // Tracking "road" from layer to layer - static Float_t gRoadY; - static Float_t gRoadZ; - // Minimal number of attached clusters - static Int_t gminNumberOfClusters; - - bool mContinuousMode = true; ///< triggered or cont. mode - const o2::its::GeometryTGeo* mGeom = nullptr; /// interface to geometry - const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; /// Cluster MC labels - std::vector* mTrkLabels = nullptr; /// Track MC labels - std::uint32_t mFirstInFrame = 0; ///< Index of the 1st cluster of a frame (within the loaded vector of clusters) - - Int_t mNumOfThreads; ///< Number of tracking threads - - Double_t mBz; ///< Effective Z-component of the magnetic field (kG) - - const std::vector* mVertices = nullptr; - Double_t mX = 0.; ///< X-coordinate of the primary vertex - Double_t mY = 0.; ///< Y-coordinate of the primary vertex - Double_t mZ = 0.; ///< Z-coordinate of the primary vertex - - Double_t mSigmaX = 2.; ///< error of the primary vertex position in X - Double_t mSigmaY = 2.; ///< error of the primary vertex position in Y - Double_t mSigmaZ = 2.; ///< error of the primary vertex position in Z - - static Layer sLayers[kNLayers]; ///< Layers filled with clusters - std::vector mSeeds; ///< Track seeds - - std::vector mClusterCache; - - ClassDefNV(CookedTracker, 1); -}; - -class CookedTracker::Layer -{ - public: - Layer(); - Layer(const Layer&) = delete; - Layer& operator=(const Layer& tr) = delete; - - void init(); - Bool_t insertCluster(const Cluster* c); - void setR(Double_t r) { mR = r; } - void unloadClusters(); - void selectClusters(std::vector& s, Float_t phi, Float_t dy, Float_t z, Float_t dz); - Int_t findClusterIndex(Float_t z) const; - Float_t getR() const { return mR; } - const Cluster* getCluster(Int_t i) const { return mClusters[i]; } - Float_t getAlphaRef(Int_t i) const { return mAlphaRef[i]; } - Float_t getClusterPhi(Int_t i) const { return mPhi[i]; } - Int_t getNumberOfClusters() const { return mClusters.size(); } - void setGeometry(o2::its::GeometryTGeo* geom) { mGeom = geom; } - - protected: - enum { kNSectors = 21 }; - - Float_t mR; ///< mean radius of this layer - const o2::its::GeometryTGeo* mGeom = nullptr; ///< interface to geometry - std::vector mClusters; ///< All clusters - std::vector mAlphaRef; ///< alpha of the reference plane - std::vector mPhi; ///< cluster phi - std::vector> mSectors[kNSectors]; ///< Cluster indices sector-by-sector -}; -} // namespace its -} // namespace o2 -#endif /* ALICEO2_ITS_COOKEDTRACKER_H */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h index 457381862cc42..9e8299e89b404 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h @@ -45,7 +45,7 @@ struct FastMultEst { static uint32_t getCurrentRandomSeed(); int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); + const gsl::span trig, std::vector& sel); void fillNClPerLayer(const gsl::span& clusters); float process(const std::array ncl) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h index b488b29fd676e..c6bce50995a4b 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h @@ -27,11 +27,11 @@ namespace its struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; - /// acceptance correction per layer (relative to 1st one) - float accCorr[NLayers] = {1.f, 0.895, 0.825, 0.803, 0.720, 0.962, 0.911}; + /// acceptance correction per layer (cluster / track) + float accCorr[NLayers] = {2.95, 2.46, 2.19, 2.26, 2.06, 3.1, 3.1}; int firstLayer = 3; /// 1st layer to account int lastLayer = 6; /// last layer to account - float imposeNoisePerChip = 1.e-7 * 1024 * 512; // assumed noise, free parameter if<0 + float imposeNoisePerChip = 1.e-9 * 1024 * 512; // assumed noise, free parameter if<0 // cuts to reject to low or too high mult events float cutMultClusLow = 0; /// reject ROF with estimated cluster mult. below this value (no cut if <0) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h index f9d3f1ae46752..a7d814f02d011 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/RecoGeomHelper.h @@ -103,7 +103,7 @@ struct RecoGeomHelper { static constexpr float ladderWidth() { return o2::itsmft::SegmentationAlpide::SensorSizeRows; } static constexpr float ladderWidthInv() { return 1. / ladderWidth(); } - void init(); + void init(int minLayer = 0, int maxLayer = getNLayers()); void print() const; ClassDefNV(RecoGeomHelper, 0); diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx deleted file mode 100644 index fb4e4ac7b6fa2..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file ClustererTask.cxx -/// \brief Implementation of the ITS cluster finder task - -#include "DetectorsCommonDataFormats/DetID.h" -#include "ITSReconstruction/ClustererTask.h" -#include "MathUtils/Cartesian.h" -#include "MathUtils/Utils.h" -#include -#include -#include - -using namespace o2::its; - -//_____________________________________________________________________ -ClustererTask::ClustererTask(bool useMC, bool raw) : mRawDataMode(raw), - mUseMCTruth(useMC && (!raw)) -{ - LOG(info) << Class()->GetName() << ": MC digits mode: " << (mRawDataMode ? "OFF" : "ON") - << " | Use MCtruth: " << (mUseMCTruth ? "ON" : "OFF"); - - mClusterer.setNChips(o2::itsmft::ChipMappingITS::getNChips()); -} - -//_____________________________________________________________________ -ClustererTask::~ClustererTask() -{ - mCompClus.clear(); - mClsLabels.clear(); -} - -//_____________________________________________________________________ -void ClustererTask::Init() -{ - /// Inititializes the clusterer and connects input and output container - - if (mReader) { - return; // already initialized - } - - // create reader according to requested raw of MC mode - if (mRawDataMode) { - mReaderRaw = std::make_unique>(); - mReader = mReaderRaw.get(); - } else { // clusterizer of digits - mReaderMC = std::make_unique(); - mReader = mReaderMC.get(); - } - - mClusterer.print(); - - return; -} - -//_____________________________________________________________________ -void ClustererTask::run(const std::string inpName, const std::string outName) -{ - // standalone execution - Init(); // create reader, clusterer - - if (mRawDataMode) { - - mReaderRaw->openInput(inpName); - mClusterer.process(1, *mReaderRaw.get(), &mCompClus, &mPatterns, &mROFRecVec, nullptr); - - auto basename = outName.substr(0, outName.size() - sizeof("root")); - auto nFiles = int(mROFRecVec.size() / maxROframe); - int i = 0; - for (; i < nFiles; i++) { - writeTree(basename, i); - } - writeTree(basename, i); // The remainder - - } else { - - mReaderMC->openInput(inpName, o2::detectors::DetID("ITS")); - - TFile outFile(outName.data(), "new"); - if (!outFile.IsOpen()) { - LOG(fatal) << "Failed to open output file " << outName; - } - - TTree outTree("o2sim", "ITS Clusters"); - - auto compClusPtr = &mCompClus; - outTree.Branch("ITSClusterComp", &compClusPtr); - - auto rofRecVecPtr = &mROFRecVec; - outTree.Branch("ITSClustersROF", &rofRecVecPtr); - - auto clsLabelsPtr = &mClsLabels; - if (mUseMCTruth && mReaderMC->getDigitsMCTruth()) { - // digit labels are provided directly to clusterer - outTree.Branch("ITSClusterMCTruth", &clsLabelsPtr); - } else { - mUseMCTruth = false; - } - LOG(info) << Class()->GetName() << " | MCTruth: " << (mUseMCTruth ? "ON" : "OFF"); - - outTree.Branch("ITSClusterPatt", &mPatterns); - - std::vector mc2rof, *mc2rofPtr = &mc2rof; - if (mUseMCTruth) { - auto mc2rofOrig = mReaderMC->getMC2ROFRecords(); - mc2rof.reserve(mc2rofOrig.size()); - for (const auto& m2r : mc2rofOrig) { // clone from the span - mc2rof.push_back(m2r); - } - outTree.Branch("ITSClustersMC2ROF", mc2rofPtr); - } - - // loop over entries of the input tree - while (mReaderMC->readNextEntry()) { - mClusterer.process(1, *mReaderMC.get(), &mCompClus, &mPatterns, &mROFRecVec, &mClsLabels); - } - - outTree.Fill(); - outTree.Write(); - } - - mClusterer.clear(); -} - -void ClustererTask::writeTree(std::string basename, int i) -{ - auto name = basename + std::to_string(i) + ".root"; - TFile outFile(name.data(), "new"); - if (!outFile.IsOpen()) { - LOG(fatal) << "Failed to open output file " << name; - } - TTree outTree("o2sim", "ITS Clusters"); - - size_t max = (i + 1) * maxROframe; - auto lastf = (max < mROFRecVec.size()) ? mROFRecVec.begin() + max : mROFRecVec.end(); - std::vector rofRecBuffer(mROFRecVec.begin() + i * maxROframe, lastf); - std::vector* rofRecPtr = &rofRecBuffer; - outTree.Branch("ITSClustersROF", rofRecPtr); - - auto first = rofRecBuffer[0].getFirstEntry(); - auto last = rofRecBuffer.back().getFirstEntry() + rofRecBuffer.back().getNEntries(); - - std::vector compClusBuffer, *compClusPtr = &compClusBuffer; - compClusBuffer.assign(&mCompClus[first], &mCompClus[last]); - outTree.Branch("ITSClusterComp", &compClusPtr); - outTree.Branch("ITSClusterPatt", &mPatterns); - - for (auto& rof : rofRecBuffer) { - rof.setFirstEntry(rof.getFirstEntry() - first); - } - - outTree.Fill(); - outTree.Write(); -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx deleted file mode 100644 index 81087744b04a9..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITSReconstruction/CookedConfigParam.h" - -namespace o2 -{ -namespace its -{ -static auto& sITSCookedTrackerParam = o2::its::CookedConfigParam::Instance(); - -O2ParamImpl(o2::its::CookedConfigParam); -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx deleted file mode 100644 index e68d26a4d12d3..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CookedTracker.cxx -/// \brief Implementation of the "Cooked Matrix" ITS tracker -/// \author iouri.belikov@cern.ch - -//------------------------------------------------------------------------- -// A stand-alone ITS tracker -// The pattern recongintion based on the "cooked covariance" approach -//------------------------------------------------------------------------- -#include -#include -#include - -#include -#include - -#include - -#include "CommonConstants/MathConstants.h" -#include "DetectorsBase/Propagator.h" -#include "Field/MagneticField.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSReconstruction/CookedTracker.h" -#include "MathUtils/Utils.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::its; -using namespace o2::itsmft; -using namespace o2::constants::math; -using o2::field::MagneticField; -using Label = o2::MCCompLabel; - -/*** Tracking parameters ***/ -// seed "windows" in z and phi: makeSeeds -Float_t CookedTracker::gzWin = 0.33; -Float_t CookedTracker::gminPt = 0.05; -Float_t CookedTracker::mMostProbablePt = o2::track::kMostProbablePt; -// Maximal accepted impact parameters for the seeds -Float_t CookedTracker::gmaxDCAxy = 3.; -Float_t CookedTracker::gmaxDCAz = 3.; -// Layers for the seeding -Int_t CookedTracker::gSeedingLayer1 = 6; -Int_t CookedTracker::gSeedingLayer2 = 4; -Int_t CookedTracker::gSeedingLayer3 = 5; -// Space point resolution -Float_t CookedTracker::gSigma2 = 0.0005 * 0.0005; -// Max accepted chi2 -Float_t CookedTracker::gmaxChi2PerCluster = 20.; -Float_t CookedTracker::gmaxChi2PerTrack = 30.; -// Tracking "road" from layer to layer -Float_t CookedTracker::gRoadY = 0.2; -Float_t CookedTracker::gRoadZ = 0.3; -// Minimal number of attached clusters -Int_t CookedTracker::gminNumberOfClusters = 4; - -const float kPI = 3.14159f; -const float k2PI = 2 * kPI; - -//************************************************ -// TODO: -//************************************************ -// Seeding: -// Precalculate cylidnrical (r,phi) for the clusters; -// use exact r's for the clusters - -CookedTracker::Layer CookedTracker::sLayers[CookedTracker::kNLayers]; - -CookedTracker::CookedTracker(Int_t n) : mNumOfThreads(n), mBz(0.) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- - const Double_t klRadius[7] = {2.34, 3.15, 3.93, 19.61, 24.55, 34.39, 39.34}; // tdr6 - - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setR(klRadius[i]); - } -} - -//__________________________________________________________________________ -Label CookedTracker::cookLabel(TrackITSExt& t, Float_t wrong) const -{ - //-------------------------------------------------------------------- - // This function "cooks" a track label. - // A label<0 indicates that some of the clusters are wrongly assigned. - //-------------------------------------------------------------------- - Int_t noc = t.getNumberOfClusters(); - std::map labelOccurence; - - for (int i = noc; i--;) { - const Cluster* c = getCluster(t.getClusterIndex(i)); - Int_t idx = c - &mClusterCache[0] + mFirstInFrame; // Index of this cluster in event - auto labels = mClsLabels->getLabels(idx); - - for (auto lab : labels) { // check all labels of the cluster - if (lab.isEmpty()) { - break; // all following labels will be empty also - } - // was this label already accounted for ? - labelOccurence[lab]++; - } - } - Label lab; - Int_t maxL = 0; // find most encountered label - for (auto [label, count] : labelOccurence) { - if (count <= maxL) { - continue; - } - maxL = count; - lab = label; - } - - if ((1. - Float_t(maxL) / noc) > wrong) { - // change the track ID to negative - lab.setFakeFlag(); - } - // t.SetFakeRatio((1.- Float_t(maxL)/noc)); - return lab; -} - -Double_t CookedTracker::getBz() const -{ - return mBz; -} - -static Double_t f1(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the track curvature - //----------------------------------------------------------------- - Double_t d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); - Double_t a = - 0.5 * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); - Double_t b = - 0.5 * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); - - Double_t xr = TMath::Abs(d / (d * x1 - a)), yr = TMath::Abs(d / (d * y1 - b)); - - Double_t crv = xr * yr / sqrt(xr * xr + yr * yr); - if (d > 0) { - crv = -crv; - } - - return crv; -} - -static Double_t f2(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the x-coordinate of the center of curvature - //----------------------------------------------------------------- - - Double_t k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); - Double_t x0 = 0.5 * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); - - return x0; -} - -static Double_t f3(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t z1, Double_t z2) -{ - //----------------------------------------------------------------- - // Initial approximation of the tangent of the track dip angle - //----------------------------------------------------------------- - return (z1 - z2) / sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); -} - -o2::its::TrackITSExt CookedTracker::cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz) -// const Float_t r1[4], const Float_t r2[4], const Float_t tr3[4], Double_t alpha, Double_t bz) -{ - //-------------------------------------------------------------------- - // This is the main cooking function. - // Creates seed parameters out of provided clusters. - //-------------------------------------------------------------------- - - Double_t ca = TMath::Cos(alpha), sa = TMath::Sin(alpha); - Double_t x1 = r1.X() * ca + r1.Y() * sa, y1 = -r1.X() * sa + r1.Y() * ca, z1 = r1.Z(); - Double_t x2 = r2.X() * ca + r2.Y() * sa, y2 = -r2.X() * sa + r2.Y() * ca, z2 = r2.Z(); - Double_t x3 = tr3.X(), y3 = tr3.Y(), z3 = tr3.Z(); - - std::array par; - par[0] = y3; - par[1] = z3; - Double_t crv = f1(x1, y1, x2, y2, x3, y3); // curvature - Double_t x0 = f2(x1, y1, x2, y2, x3, y3); // x-coordinate of the center - Double_t tgl12 = f3(x1, y1, x2, y2, z1, z2); - Double_t tgl23 = f3(x2, y2, x3, y3, z2, z3); - - Double_t sf = crv * (x3 - x0); // FIXME: sf must never be >= kAlmost1 - par[2] = sf; - - par[3] = 0.5 * (tgl12 + tgl23); - par[4] = (TMath::Abs(bz) < Almost0) ? 1 / CookedTracker::getMostProbablePt() : crv / (bz * B2C); - - std::array cov; - /* - for (Int_t i=0; i<15; i++) cov[i]=0.; - cov[0] =gSigma2*10; - cov[2] =gSigma2*10; - cov[5] =0.007*0.007*10; //FIXME all these lines - cov[9] =0.007*0.007*10; - cov[14]=0.1*0.1*10; - */ - const Double_t dlt = 0.0005; - Double_t fy = 1. / (rad2 - rad3); - Double_t tz = fy; - const auto big = sqrt(o2::constants::math::VeryBig); - auto cy = big; - if (TMath::Abs(bz) >= Almost0) { - auto tmp = dlt * bz * B2C; - cy = (f1(x1, y1, x2, y2 + dlt, x3, y3) - crv) / tmp; - cy *= 20; // FIXME: MS contribution to the cov[14] - } - Double_t s2 = gSigma2; - - cov[0] = s2; - cov[1] = 0.; - cov[2] = s2; - cov[3] = s2 * fy; - cov[4] = 0.; - cov[5] = s2 * fy * fy; - cov[6] = 0.; - cov[7] = s2 * tz; - cov[8] = 0.; - cov[9] = s2 * tz * tz; - cov[10] = s2 * cy; - cov[11] = 0.; - cov[12] = s2 * fy * cy; - cov[13] = 0.; - cov[14] = s2 * cy * cy; - - return o2::its::TrackITSExt(x3, alpha, par, cov); -} - -void CookedTracker::makeSeeds(std::vector& seeds, Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This is the main pattern recongition function. - // Creates seeds out of two clusters and another point. - //-------------------------------------------------------------------- - const float zv = getZ(); - - Layer& layer1 = sLayers[gSeedingLayer1]; - Layer& layer2 = sLayers[gSeedingLayer2]; - Layer& layer3 = sLayers[gSeedingLayer3]; - - auto bz = getBz(); - const Double_t maxC = (TMath::Abs(bz) < Almost0) ? 0.03 : TMath::Abs(bz * B2C / gminPt); - const Double_t kpWinC = TMath::ASin(0.5 * maxC * layer1.getR()) - TMath::ASin(0.5 * maxC * layer2.getR()); - const Double_t kpWinD = 2 * (TMath::ASin(gmaxDCAxy / layer2.getR()) - TMath::ASin(gmaxDCAxy / layer1.getR())); - const Double_t kpWin = std::max(kpWinC, kpWinD); - - for (Int_t n1 = first; n1 < last; n1++) { - const Cluster* c1 = layer1.getCluster(n1); - // - //auto lab = (mClsLabels->getLabels(c1 - &mClusterCache[0] + mFirstInFrame))[0]; - // - auto xyz1 = c1->getXYZGloRot(*mGeom); - auto z1 = xyz1.Z(); - auto r1 = xyz1.rho(); - - auto phi1 = layer1.getClusterPhi(n1); - auto tgl = std::abs((z1 - zv) / r1); - - auto zr2 = zv + layer2.getR() / r1 * (z1 - zv); - auto phir2 = phi1; - auto dz2 = gzWin * (1 + 2 * tgl); - - std::vector selected2; - float dy2 = kpWin * layer2.getR(); - layer2.selectClusters(selected2, phir2, dy2, zr2, dz2); - for (auto n2 : selected2) { - const Cluster* c2 = layer2.getCluster(n2); - // - //if ((mClsLabels->getLabels(c2 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz2 = c2->getXYZGloRot(*mGeom); - auto z2 = xyz2.Z(); - auto r2 = xyz2.rho(); - - auto dx = xyz2.X() - xyz1.X(), dy = xyz2.Y() - xyz1.Y(); - auto d = (dx * xyz1.Y() - dy * xyz1.X()) / TMath::Sqrt(dx * dx + dy * dy); - auto phir3 = phi1 + TMath::ASin(d / r1) - TMath::ASin(d / layer3.getR()); - - auto zr3 = z1 + (layer3.getR() - r1) / (r2 - r1) * (z2 - z1); - auto dz3 = 0.5f * dz2; - - std::vector selected3; - float dy3 = 0.1 * kpWin * layer3.getR(); //Fixme - layer3.selectClusters(selected3, phir3, dy3, zr3, dz3); - for (auto n3 : selected3) { - const Cluster* c3 = layer3.getCluster(n3); - // - //if ((mClsLabels->getLabels(c3 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz3 = c3->getXYZGloRot(*mGeom); - auto z3 = xyz3.Z(); - auto r3 = xyz3.rho(); - - zr3 = z1 + (r3 - r1) / (r2 - r1) * (z2 - z1); - if (std::abs(z3 - zr3) > 0.2 * dz3) { - continue; - } - - const Point3Df& txyz2 = c2->getXYZ(); // tracking coordinates - - TrackITSExt seed = cookSeed(xyz1, xyz3, txyz2, layer2.getR(), layer3.getR(), layer2.getAlphaRef(n2), getBz()); - - float ip[2]; - seed.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - if (TMath::Abs(ip[0]) > gmaxDCAxy) { - continue; - } - if (TMath::Abs(ip[1]) > gmaxDCAz) { - continue; - } - { - Double_t xx0 = 0.008; // Rough layer thickness - Double_t radl = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - if (!seed.correctForMaterial(xx0, xx0 * radl * rho, kTRUE)) { - continue; - } - } - seed.setClusterIndex(gSeedingLayer1, n1); - seed.setClusterIndex(gSeedingLayer3, n3); - seed.setClusterIndex(gSeedingLayer2, n2); - seeds.push_back(seed); - } - } - } - /* - for (Int_t n1 = 0; n1 < nClusters1; n1++) { - Cluster* c1 = layer1.getCluster(n1); - ((Cluster*)c1)->goToFrameTrk(); - } - for (Int_t n2 = 0; n2 < nClusters2; n2++) { - Cluster* c2 = layer2.getCluster(n2); - ((Cluster*)c2)->goToFrameTrk(); - } - for (Int_t n3 = 0; n3 < nClusters3; n3++) { - Cluster* c3 = layer3.getCluster(n3); - ((Cluster*)c3)->goToFrameTrk(); - } - */ -} - -void CookedTracker::trackSeeds(std::vector& seeds) -{ - //-------------------------------------------------------------------- - // Loop over a subset of track seeds - //-------------------------------------------------------------------- - std::vector used[gSeedingLayer2]; - std::vector selec[gSeedingLayer2]; - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Int_t n = sLayers[l].getNumberOfClusters(); - used[l].resize(n, false); - selec[l].reserve(n / 100); - } - - for (auto& track : seeds) { - auto x = track.getX(); - auto y = track.getY(); - Float_t phi = track.getAlpha() + TMath::ATan2(y, x); - o2::math_utils::bringTo02Pi(phi); - float ip[2]; - track.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - - auto z = track.getZ(); - auto crv = track.getCurvature(getBz()); - auto tgl = track.getTgl(); - Float_t r1 = sLayers[gSeedingLayer2].getR(); - - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Float_t r2 = sLayers[l].getR(); - selec[l].clear(); - if (TMath::Abs(ip[0]) > r2) { - break; - } - if (TMath::Abs(crv) < gRoadY / (0.5 * r1 * 0.5 * r1)) { - phi += TMath::ASin(ip[0] / r2) - TMath::ASin(ip[0] / r1); - z += tgl * (TMath::Sqrt(r2 * r2 - ip[0] * ip[0]) - TMath::Sqrt(r1 * r1 - ip[0] * ip[0])); - } else { // Fixme - phi += 0.5 * crv * (r2 - r1); - z += tgl / (0.5 * crv) * (TMath::ASin(0.5 * crv * r2) - TMath::ASin(0.5 * crv * r1)); - } - sLayers[l].selectClusters(selec[l], phi, gRoadY, z, gRoadZ * (1 + 2 * std::abs(tgl))); - r1 = r2; - } - - TrackITSExt best(track); - - Int_t volID = -1; - for (auto& ci3 : selec[3]) { - TrackITSExt t3(track); - if (used[3][ci3]) { - continue; - } - if (!attachCluster(volID, 3, ci3, t3, track)) { - continue; - } - if (t3.isBetter(best, gmaxChi2PerTrack)) { - best = t3; - } - - for (auto& ci2 : selec[2]) { - TrackITSExt t2(t3); - if (used[2][ci2]) { - continue; - } - if (!attachCluster(volID, 2, ci2, t2, t3)) { - continue; - } - if (t2.isBetter(best, gmaxChi2PerTrack)) { - best = t2; - } - - for (auto& ci1 : selec[1]) { - TrackITSExt t1(t2); - if (used[1][ci1]) { - continue; - } - if (!attachCluster(volID, 1, ci1, t1, t2)) { - continue; - } - if (t1.isBetter(best, gmaxChi2PerTrack)) { - best = t1; - } - - for (auto& ci0 : selec[0]) { - TrackITSExt t0(t1); - if (used[0][ci0]) { - continue; - } - if (!attachCluster(volID, 0, ci0, t0, t1)) { - continue; - } - if (t0.isBetter(best, gmaxChi2PerTrack)) { - best = t0; - } - volID = -1; - } - } - } - } - - if (best.getNumberOfClusters() >= gminNumberOfClusters) { - Int_t noc = best.getNumberOfClusters(); - for (Int_t ic = 3; ic < noc; ic++) { - Int_t index = best.getClusterIndex(ic); - Int_t l = (index & 0xf0000000) >> 28, c = (index & 0x0fffffff); - used[l][c] = true; - } - } - track = best; - } -} - -std::vector CookedTracker::trackInThread(Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This function is passed to a tracking thread - //-------------------------------------------------------------------- - std::vector seeds; - seeds.reserve(last - first + 1); - - for (const auto& vtx : *mVertices) { - mX = vtx.getX(); - mY = vtx.getY(); - mZ = vtx.getZ(); - makeSeeds(seeds, first, last); - } - - std::sort(seeds.begin(), seeds.end()); - - trackSeeds(seeds); - - makeBackPropParam(seeds); - - return seeds; -} - -void CookedTracker::process(gsl::span const& clusters, - gsl::span::iterator& pattIt, - const o2::itsmft::TopologyDictionary* dict, - TrackInserter& inserter, - o2::itsmft::ROFRecord& rof) -{ - //-------------------------------------------------------------------- - // This is the main tracking function - //-------------------------------------------------------------------- - if (mVertices == nullptr || mVertices->empty()) { - LOG(info) << "Not a single primary vertex provided. Skipping...\n"; - return; - } - LOG(info) << "\n CookedTracker::process(), number of threads: " << mNumOfThreads; - - auto start = std::chrono::system_clock::now(); - - mFirstInFrame = rof.getFirstEntry(); - - mClusterCache.reserve(rof.getNEntries()); - auto clusters_in_frame = rof.getROFData(clusters); - for (const auto& comp : clusters_in_frame) { - - auto pattID = comp.getPatternID(); - o2::math_utils::Point3D locXYZ; - float sigmaY2 = gSigma2, sigmaZ2 = gSigma2; - if (pattID != itsmft::CompCluster::InvalidPatternID) { - sigmaY2 = gSigma2; //dict.getErr2X(pattID); - sigmaZ2 = gSigma2; //dict.getErr2Z(pattID); - if (!dict->isGroup(pattID)) { - locXYZ = dict->getClusterCoordinates(comp); - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt); - } - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt, false); - } - auto sensorID = comp.getSensorID(); - // Inverse transformation to the local --> tracking - auto trkXYZ = mGeom->getMatrixT2L(sensorID) ^ locXYZ; - - Cluster c; - c.setSensorID(sensorID); - c.setPos(trkXYZ); - c.setErrors(sigmaY2, sigmaZ2, 0.f); - mClusterCache.push_back(c); - } - - auto nClFrame = loadClusters(); - - auto end = std::chrono::system_clock::now(); - std::chrono::duration diff = end - start; - LOG(info) << "Loading clusters: " << nClFrame << " in a single frame : " << diff.count() << " s"; - - start = end; - - auto [first, number] = processLoadedClusters(inserter); - rof.setFirstEntry(first); - rof.setNEntries(number); - - unloadClusters(); - end = std::chrono::system_clock::now(); - diff = end - start; - LOG(info) << "Processing time/clusters for single frame : " << diff.count() << " / " << nClFrame << " s"; - - start = end; -} - -std::tuple CookedTracker::processLoadedClusters(TrackInserter& inserter) -{ - //-------------------------------------------------------------------- - // This is the main tracking function for single frame, it is assumed that only clusters - // which may contribute to this frame is loaded - //-------------------------------------------------------------------- - Int_t numOfClusters = sLayers[gSeedingLayer1].getNumberOfClusters(); - if (!numOfClusters) { - return {0, 0}; - } - - std::vector>> futures(mNumOfThreads); - std::vector> seedArray(mNumOfThreads); - - for (Int_t t = 0, first = 0; t < mNumOfThreads; t++) { - Int_t rem = t < (numOfClusters % mNumOfThreads) ? 1 : 0; - Int_t last = first + (numOfClusters / mNumOfThreads) + rem; - futures[t] = std::async(std::launch::async, &CookedTracker::trackInThread, this, first, last); - first = last; - } - Int_t nSeeds = 0, ngood = 0; - int nAllTracks = 0, nTracks = 0; - for (Int_t t = 0; t < mNumOfThreads; t++) { - seedArray[t] = futures[t].get(); - nSeeds += seedArray[t].size(); - for (auto& track : seedArray[t]) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - - o2::dataformats::VertexBase vtx; - track.propagateToDCA(vtx, getBz()); - - nAllTracks = inserter(track); - nTracks++; - if (mTrkLabels) { - Label label = cookLabel(track, 0.); // For comparison only - if (label.getTrackID() >= 0) { - ngood++; - } - // the inserter returns the size of the track vector, the index of the last - // inserted track is thus n - 1 - mTrkLabels->emplace_back(label); - } - } - } - - if (nSeeds) { - LOG(info) << "Found tracks: " << nTracks; - LOG(info) << "CookedTracker::processLoadedClusters(), good_tracks:/seeds: " << ngood << '/' << nSeeds << "-> " - << Float_t(ngood) / nSeeds << '\n'; - } - // returning index of the first track and the number of add tracks - // inserted in this call - return {nAllTracks - nTracks, nTracks}; -} - -//____________________________________________________________ -void CookedTracker::makeBackPropParam(std::vector& seeds) const -{ - // refit in backward direction - for (auto& track : seeds) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - makeBackPropParam(track); - } -} - -//____________________________________________________________ -bool CookedTracker::makeBackPropParam(TrackITSExt& track) const -{ - // refit in backward direction - auto backProp = track.getParamOut(); - backProp = track; - backProp.resetCovariance(); - auto propagator = o2::base::Propagator::Instance(); - - Int_t noc = track.getNumberOfClusters(); - for (int ic = noc; ic--;) { // cluster indices are stored in inward direction - Int_t index = track.getClusterIndex(ic); - const Cluster* c = getCluster(index); - float alpha = mGeom->getSensorRefAlpha(c->getSensorID()); - if (!backProp.rotate(alpha)) { - return false; - } - if (!propagator->PropagateToXBxByBz(backProp, c->getX())) { - return false; - } - if (!backProp.update(static_cast&>(*c))) { - return false; - } - } - track.getParamOut() = backProp; - return true; -} - -int CookedTracker::loadClusters() -{ - //-------------------------------------------------------------------- - // This function reads the ITSU clusters from the tree, - // sort them, distribute over the internal tracker arrays, etc - //-------------------------------------------------------------------- - - if (mClusterCache.empty()) { - return 0; - } - - for (const auto& c : mClusterCache) { - Int_t layer = mGeom->getLayer(c.getSensorID()); - sLayers[layer].insertCluster(&c); - } - - std::vector> fut; - for (Int_t l = 0; l < kNLayers; l += mNumOfThreads) { - for (Int_t t = 0; t < mNumOfThreads; t++) { - if (l + t >= kNLayers) { - break; - } - auto f = std::async(std::launch::async, &CookedTracker::Layer::init, sLayers + (l + t)); - fut.push_back(std::move(f)); - } - for (size_t t = 0; t < fut.size(); t++) { - fut[t].wait(); - } - } - - return mClusterCache.size(); -} - -void CookedTracker::unloadClusters() -{ - //-------------------------------------------------------------------- - // This function unloads ITSU clusters from the RAM - //-------------------------------------------------------------------- - mClusterCache.clear(); - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].unloadClusters(); - } -} - -const Cluster* CookedTracker::getCluster(Int_t index) const -{ - //-------------------------------------------------------------------- - // Return pointer to a given cluster - //-------------------------------------------------------------------- - Int_t l = (index & 0xf0000000) >> 28; - Int_t c = (index & 0x0fffffff) >> 00; - return sLayers[l].getCluster(c); -} - -CookedTracker::Layer::Layer() : mR(0) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- -} - -void CookedTracker::Layer::init() -{ - //-------------------------------------------------------------------- - // Sort clusters and cache their reference plane info in a thread - //-------------------------------------------------------------------- - std::sort(std::begin(mClusters), std::end(mClusters), - [](const Cluster* c1, const Cluster* c2) { return (c1->getZ() < c2->getZ()); }); - - Double_t r = 0.; - Int_t m = mClusters.size(); - for (Int_t i = 0; i < m; i++) { - const Cluster* c = mClusters[i]; - // Float_t xRef, aRef; - // mGeom->getSensorXAlphaRefPlane(c->getSensorID(),xRef, aRef); - mAlphaRef.push_back(mGeom->getSensorRefAlpha(c->getSensorID())); - auto xyz = c->getXYZGloRot(*mGeom); - r += xyz.rho(); - Float_t phi = xyz.Phi(); - o2::math_utils::bringTo02Pi(phi); - mPhi.push_back(phi); - Int_t s = phi * kNSectors / k2PI; - mSectors[s < kNSectors ? s : kNSectors - 1].emplace_back(i, c->getZ()); - } - - if (m) { - mR = r / m; - } -} - -void CookedTracker::Layer::unloadClusters() -{ - //-------------------------------------------------------------------- - // Unload clusters from this layer - //-------------------------------------------------------------------- - mClusters.clear(); - mAlphaRef.clear(); - mPhi.clear(); - for (Int_t s = 0; s < kNSectors; s++) { - mSectors[s].clear(); - } -} - -Bool_t CookedTracker::Layer::insertCluster(const Cluster* c) -{ - //-------------------------------------------------------------------- - // This function inserts a cluster to this layer - //-------------------------------------------------------------------- - mClusters.push_back(c); - return kTRUE; -} - -Int_t CookedTracker::Layer::findClusterIndex(Float_t z) const -{ - //-------------------------------------------------------------------- - // This function returns the index of the first cluster with its fZ >= "z". - //-------------------------------------------------------------------- - auto found = std::upper_bound(std::begin(mClusters), std::end(mClusters), z, - [](Float_t zc, const Cluster* c) { return (zc < c->getZ()); }); - return found - std::begin(mClusters); -} - -void CookedTracker::Layer::selectClusters(std::vector& selec, Float_t phi, Float_t dy, Float_t z, Float_t dz) -{ - //-------------------------------------------------------------------- - // This function selects clusters within the "road" - //-------------------------------------------------------------------- - Float_t zMin = z - dz; - Float_t zMax = z + dz; - - o2::math_utils::bringTo02Pi(phi); - - Float_t dphi = dy / mR; - - int smin = (phi - dphi) / k2PI * kNSectors; - int ds = (phi + dphi) / k2PI * kNSectors - smin + 1; - - smin = (smin + kNSectors) % kNSectors; - - for (int is = 0; is < ds; is++) { - Int_t s = (smin + is) % kNSectors; - - auto cmp = [](Float_t zc, std::pair p) { return (zc < p.second); }; - auto imin = std::upper_bound(std::begin(mSectors[s]), std::end(mSectors[s]), zMin, cmp); - auto imax = std::upper_bound(imin, std::end(mSectors[s]), zMax, cmp); - for (; imin != imax; imin++) { - auto [i, zz] = *imin; - auto cdphi = std::abs(mPhi[i] - phi); - if (cdphi > dphi) { - if (cdphi > kPI) { - cdphi = k2PI - cdphi; - } - if (cdphi > dphi) { - continue; // check in Phi - } - } - selec.push_back(i); - } - } -} - -Bool_t CookedTracker::attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSExt& t, const TrackITSExt& o) const -{ - //-------------------------------------------------------------------- - // Try to attach a clusters with index ci to running track hypothesis - //-------------------------------------------------------------------- - Layer& layer = sLayers[nl]; - const Cluster* c = layer.getCluster(ci); - - Int_t vid = c->getSensorID(); - - if (vid != volID) { - volID = vid; - t = o; - Double_t alpha = layer.getAlphaRef(ci); - if (!t.propagate(alpha, c->getX(), getBz())) { - return kFALSE; - } - } - - Double_t chi2 = t.getPredictedChi2(*c); - if (chi2 > gmaxChi2PerCluster) { - return kFALSE; - } - - if (!t.update(*c, chi2)) { - return kFALSE; - } - t.setClusterIndex(nl, ci); - - Double_t xx0 = (nl > 2) ? 0.008 : 0.003; // Rough layer thickness - Double_t x0 = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - t.correctForMaterial(xx0, xx0 * x0 * rho, kTRUE); - return kTRUE; -} - -void CookedTracker::setGeometry(o2::its::GeometryTGeo* geom) -{ - /// attach geometry interface - mGeom = geom; - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setGeometry(geom); - } -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h deleted file mode 100644 index 1e8596fbb224c..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::its::ClustererTask + ; -#pragma link C++ class o2::its::CookedTracker + ; -#pragma link C++ class o2::its::CookedConfigParam + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::CookedConfigParam> + ; -#pragma link C++ class o2::its::RecoGeomHelper + ; -#pragma link C++ class o2::its::FastMultEst + ; -#pragma link C++ class o2::its::FastMultEstConfig + ; - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx index 68caae8e072ac..c547996c6f356 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx @@ -105,49 +105,27 @@ float FastMultEst::processNoiseImposed(const std::array ncl) // we assume that on the used layers the observed number of clusters is defined by the // the noise ~ nu * Nchips and contribution from the signal tracks Ntr*conf.accCorr // - // minimize the form sum_lr (noise_i - mu nchips_i)^2 / (mu nchips_i) + lambda_i * (noise_i + mult*acc_i - ncl_i) - // whith noise_i being estimate of the noise clusters in nchips_i of layer i, mu is the mean noise per chip, - // mult is the number of signal clusters on the ref. (1st) layer and the acc_i is the acceptance of layer i wrt 1st. - // The lambda_i is hust a Lagrange multiplier. - const auto& conf = FastMultEstConfig::Instance(); - float w2sum = 0., wnsum = 0., wsum = 0.; + float accSum = 0., accWSum = 0., noiseSum = 0.; nLayersUsed = 0; for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { if (ncl[il] > 0) { - float nchInv = 1. / o2::itsmft::ChipMappingITS::getNChipsPerLr(il); - w2sum += conf.accCorr[il] * conf.accCorr[il] * nchInv; - wnsum += ncl[il] * nchInv * conf.accCorr[il]; - wsum += conf.accCorr[il]; + float err = 1. / ncl[il]; + accSum += conf.accCorr[il]; + accWSum += conf.accCorr[il] * conf.accCorr[il] * err; + noiseSum += o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * conf.accCorr[il] * err; nLayersUsed++; } } mult = 0; - chi2 = -1; - noisePerChip = conf.imposeNoisePerChip; - if (nLayersUsed < 1) { - return -1; - } - auto w2sumI = 1. / w2sum; - mult = (wnsum - noisePerChip * wsum) * w2sumI; - cov[0] = wnsum * w2sumI; - cov[2] = 0.; - cov[1] = 0.; - - chi2 = 0.; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float noise = ncl[il] - mult * conf.accCorr[il], estNoise = o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * noisePerChip; - float diff = noise - estNoise; - chi2 += diff * diff / estNoise; - } + if (nLayersUsed) { + mult = (accSum - noisePerChip * noiseSum) / accWSum; } - chi2 = nLayersUsed > 2 ? chi2 / (nLayersUsed - 2) : 0.; - return mult > 0 ? mult : 0; + return mult; } int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) + const gsl::span trig, std::vector& sel) { int nrof = rofs.size(), nsel = 0; const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h index c93cf03d0ed3d..67622303fc840 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h +++ b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h @@ -15,11 +15,7 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::its::ClustererTask + ; -#pragma link C++ class o2::its::CookedTracker + ; - #pragma link C++ class o2::its::RecoGeomHelper + ; - #pragma link C++ class o2::its::FastMultEst + ; #pragma link C++ class o2::its::FastMultEstConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx index 5d5467c6d4f4c..712ec6a022d16 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx @@ -16,6 +16,7 @@ #include "ITSReconstruction/RecoGeomHelper.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" using namespace o2::its; @@ -30,9 +31,9 @@ void RecoGeomHelper::RecoChip::updateLimits(const o2::math_utils::Point3D //_____________________________________________________________________ void RecoGeomHelper::RecoChip::print() const { - printf("Ch#%4d Alp: %+.3f X:%5.2f %+6.3f]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", - id, phiRange.getMin(), phiMean, phiRange.getMax(), dphiH, - xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1(), (int)chips.size(), - overlapWithNext == Undefined ? "N/A" : ((overlapWithNext == NoOverlap ? "NO" : (overlapWithNext == Above ? "Above" : "Below")))); + LOGF(info, "Ladder %3d %.3f]<%.3f dPhiH:%.3f | XYEdges: {%+6.3f,%+6.3f}{%+6.3f,%+6.3f} | %3d chips | OvlNext: %s\n", + id, phiRange.getMin(), phiMean, phiRange.getMax(), dphiH, + xyEdges.getX0(), xyEdges.getY0(), xyEdges.getX1(), xyEdges.getY1(), (int)chips.size(), + overlapWithNext == Undefined ? "N/A" : ((overlapWithNext == NoOverlap ? "NO" : (overlapWithNext == Above ? "Above" : "Below")))); for (const auto& ch : chips) { ch.print(); } @@ -220,17 +221,17 @@ void RecoGeomHelper::RecoLayer::updateLimits(const o2::math_utils::Point3D= minLayer;) { auto& lr = layers[il]; lr.id = il; lr.init(); diff --git a/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt b/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt index 2406390944f06..51ade8bab6bcc 100644 --- a/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/simulation/CMakeLists.txt @@ -12,18 +12,21 @@ o2_add_library(ITSSimulation SOURCES src/V11Geometry.cxx src/V1Layer.cxx src/V3Layer.cxx src/Detector.cxx src/V3Services.cxx src/V3Cage.cxx - src/DescriptorInnerBarrelITS2.cxx + src/DescriptorInnerBarrelITS2.cxx src/ITSDataSimulator.cxx + src/ITSSimParam.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTSimulation ROOT::Physics $<$:O2::ITS3Base> $<$:O2::ITS3Simulation>) o2_target_root_dictionary(ITSSimulation HEADERS include/ITSSimulation/Detector.h + include/ITSSimulation/ITSDataSimulator.h include/ITSSimulation/V1Layer.h include/ITSSimulation/V3Layer.h include/ITSSimulation/V3Cage.h include/ITSSimulation/V11Geometry.h include/ITSSimulation/V3Services.h - include/ITSSimulation/DescriptorInnerBarrelITS2.h) + include/ITSSimulation/DescriptorInnerBarrelITS2.h + include/ITSSimulation/ITSSimParam.h) o2_data_file(COPY data DESTINATION Detectors/ITS/simulation) @@ -40,8 +43,23 @@ o2_add_executable(digi2raw O2::CommonUtils Boost::program_options) +o2_add_executable(sim-data + COMPONENT_NAME its + TARGETVARNAME itssimdata_exe + SOURCES src/ITSDataSimulator.cxx + PUBLIC_LINK_LIBRARIES O2::ITSMFTReconstruction + O2::DataFormatsITSMFT + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::DetectorsRaw + O2::DetectorsCommonDataFormats + O2::CommonUtils + O2::ITSSimulation + Boost::program_options) + if(NOT APPLE) set_property(TARGET ${itsdigi2raw_exe} PROPERTY LINK_WHAT_YOU_USE ON) + set_property(TARGET ${itssimData_exe} PROPERTY LINK_WHAT_YOU_USE ON) endif() diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h index 8573053b06f66..57301ac4babd0 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h @@ -77,7 +77,13 @@ class Detector : public o2::base::DetImpl /// Name : Detector Name /// Active: kTRUE for active detectors (ProcessHits() will be called) /// kFALSE for inactive detectors - Detector(Bool_t active, TString name = "ITS", TString its3Version = ""); + Detector(Bool_t active, TString name = "ITS"); + + // Factory method + static o2::base::Detector* create(const char* name, bool active) + { + return new Detector(active, name); + } /// Default constructor Detector(); @@ -153,6 +159,9 @@ class Detector : public o2::base::DetImpl /// Add alignable top volumes void addAlignableVolumes() const override; + /// Add ITS chip volumes to parallel world geometry + void fillParallelWorld() const override; + /// Add alignable Layer volumes /// \param lr layer number /// \param parent path of the parent volume @@ -286,6 +295,10 @@ class Detector : public o2::base::DetImpl /// \param motherVolume the TGeoVolume owing the volume supports void createOuterBarrelSupports(TGeoVolume* motherVolume); + /// Creates the ITS Services (tubes, cables, and the like) + /// \param motherVolume the TGeoVolume owing the volume supports + void createITSServices(TGeoVolume* motherVolume); + Detector(const Detector&); Detector& operator=(const Detector&); diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h new file mode 100644 index 0000000000000..c8bb9becbd2da --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSDataSimulator.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSDataSimulator.h +/// \brief Infrastructure to simulate ALPIDE chip data. +/// \author knaumov@cern.ch + +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITSMFTReconstruction/PixelData.h" + +namespace o2 +{ +namespace itsmft +{ + +class ITSDataSimulator +{ + public: + const static uint32_t MaxChipID = 24119; + const static uint32_t MaxPixelsPerChip = + SegmentationAlpide::NRows * SegmentationAlpide::NCols; + + ITSDataSimulator(int32_t seed, uint32_t numberOfChips, + uint32_t maxPixelsPerChip, bool doDigits, bool doErrors) + : mSeed(seed), mNumberOfChips(numberOfChips), mMaxPixelsPerChip(maxPixelsPerChip), mDoDigits(doDigits), mDoErrors(doErrors) + { + srand(mSeed); + } + + ~ITSDataSimulator() = default; + + // Simulate fired pixels for a chip + std::vector generateChipData(); + + void simulate(); + + private: + int32_t mSeed; + uint32_t mMaxPixelsPerChip; + uint32_t mNumberOfChips; + bool mDoDigits; + bool mDoErrors; +}; + +} // namespace itsmft +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h new file mode 100644 index 0000000000000..231b9294136b0 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/ITSSimParam.h @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ +#define DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its +{ + +struct ITSSimParam : public o2::conf::ConfigurableParamHelper { + bool addMetalToPW = true; + bool addSensorToPW = true; + bool addChipToPW = true; + + O2ParamDef(ITSSimParam, "ITSSimParam"); +}; + +} // namespace its +} // namespace o2 + +#endif /* DETECTORS_BASE_INCLUDE_ITSSIMPARAM_H_ */ diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h index 0c38304d8370b..7844f42601a47 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h @@ -85,6 +85,48 @@ class V3Cage : public V11Geometry /// \param mgr The GeoManager (used only to get the proper material) TGeoCompositeShape* createCageEndCapCableCross(const TGeoManager* mgr = gGeoManager); + /// Creates the Beam Pipe Support inside the Cage on the A side + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createBeamPipeSupport(const TGeoManager* mgr = gGeoManager); + + /// Creates the Titanium lower part of the collar supporting the beam pipe + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppLowerCollar(); + + /// Creates the Titanium upper part of the collar supporting the beam pipe + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppUpperCollar(); + + /// Creates the CF lateral bar of the beam pipe support (aka collar beam) + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppCollarBeam(); + + /// Creates the Titanium lateral bracket of the beam pipe support + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppBracket(); + + /// Creates the lateral clamps holding the beam pipe support to the Cage + /// \param mgr The GeoManager (used only to get the proper material) + TGeoCompositeShape* createBPSuppClamp(); + + /// Creates the Cage Closing Cross element + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createCageClosingCross(const TGeoManager* mgr = gGeoManager); + + /// Creates and places the MFT rails inside the Cage + /// \param mother The mother volume to place the rails into + /// \param mgr The GeoManager (used only to get the proper material) + void createAndPlaceMFTRailsInsideCage(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + + /// Creates a pair of MFT rails inside the Cage + /// \param motmed Medium material of the mother volume + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createMFTRailsPair(const TGeoMedium* motmed, const TGeoManager* mgr = gGeoManager); + + /// Creates a hinge holding a pair of MFT rails inside the Cage + /// \param mgr The GeoManager (used only to get the proper material) + TGeoVolume* createMFTRailsHinge(const TGeoManager* mgr = gGeoManager); + // Parameters static const Double_t sCageYInBarrel; ///< Global Y translation @@ -107,6 +149,7 @@ class V3Cage : public V11Geometry static const Double_t sCageCoverRibYBaseHi; ///< Cover rib Base Height on Y static const Double_t sCageCoverRibFoldHi; ///< Cover rib Fold Height + // Cage Side Panels and Rails static const Double_t sCageSidePanelLength; ///< Side panel length along Z static const Double_t sCageSidePanelWidth; ///< Side panel width along Y static const Double_t sCageSidePanelFoilThick; ///< Side panel foil thickness @@ -124,11 +167,14 @@ class V3Cage : public V11Geometry static const Double_t sCageSidePanelGuideWide; ///< Side panel guide X width static const Double_t sCageSidePanelGuidThik1; ///< Side panel guide thickness static const Double_t sCageSidePanelGuidThik2; ///< Side panel guide thickness + static const Double_t sCageSidePanelMidBarWid; ///< Side panel middle bar width + static const Double_t sCageSidePanelSidBarWid; ///< Side panel side bar width static const Double_t sCageSidePanelRail1Ypos[2]; ///< Side panel rail 1 Y pos static const Double_t sCageSidePanelRail2Ypos; ///< Side panel rail 2 Y pos static const Double_t sCageSidePanelRail3Ypos[3]; ///< Side panel rail 3 Y pos + // Cage End Cap static const Double_t sCageEndCapDext; ///< End Cap ext diameter static const Double_t sCageEndCapDint; ///< End Cap int diameter static const Double_t sCageEndCapFoamThick; ///< End Cap foam thickness @@ -151,6 +197,90 @@ class V3Cage : public V11Geometry static const Double_t sCageECCableCrosInZLen; ///< EC Cable Cut inner length static const Double_t sCageECCableCrosSidWid; ///< EC Cable Cut Y side len + // Beam Pipe Support (A Side) + static const Double_t sBPSuppCollarIntD; ///< BP support collar int diam + static const Double_t sBPSuppCollarExtD; ///< BP support collar ext diam + static const Double_t sBPSuppCollarBushD; ///< BP support collar bushing diam + static const Double_t sBPSuppUpperCollarLen; ///< BP support upper collar len + static const Double_t sBPSuppUpperCollarHei; ///< BP support upper collar high + static const Double_t sBPSuppLowerCollarLen; ///< BP support lower collar len + static const Double_t sBPSuppLowerCollarTlX; ///< BP support lower collar tail X + static const Double_t sBPSuppLowCollHolDist; ///< BP support lower collar hole dist + static const Double_t sBPSuppLowCollTailHei; ///< BP support lower collar tail hei + static const Double_t sBPSuppCollarBeamLen; ///< BP support collar beam len + static const Double_t sBPSuppCollarBeamWid; ///< BP support collar beam wide + static const Double_t sBPSuppCollarBeamHei; ///< BP support collar beam high + static const Double_t sBPSuppBracketTotLen; ///< BP support bracket total len + static const Double_t sBPSuppBracketWidth; ///< BP support bracket width + static const Double_t sBPSuppBracketInLen; ///< BP support bracket internal len + static const Double_t sBPSuppBracketInHei; ///< BP support bracket internal height + static const Double_t sBPSuppBracketTailLen; ///< BP support bracket tail len + static const Double_t sBPSuppBracketTailHei; ///< BP support bracket tail hei + static const Double_t sBPSuppBrktCentHoleX; ///< BP support bracket central hole X pos + static const Double_t sBPSuppBrktCentHoleD; ///< BP support bracket central hole diameter + static const Double_t sBPSuppBrktLatHoleX; ///< BP support bracket lateral hole X pos + static const Double_t sBPSuppBrktLatHoleD; ///< BP support bracket lateral hole diameter + static const Double_t sBPSuppBrktLatHoleW; ///< BP support bracket lateral hole width + static const Double_t sBPSuppBrktLatHoleH; ///< BP support bracket lateral hole height + static const Double_t sBPSuppBrktHolesY; ///< BP support bracket holes Y pos + static const Double_t sBPSuppCollarM4High; ///< BP support collar screw head height + static const Double_t sBPSuppCollarM4Diam; ///< BP support collar screw head diameter + static const Double_t sBPSuppCollarM4XDist; ///< BP support collar screw X dist + static const Double_t sBPSuppCollarM4ZPos; ///< BP support collar screw Z pos + static const Double_t sBPSuppClampTotLen; ///< BP support clamp length + static const Double_t sBPSuppClampTotWid; ///< BP support clamp width + static const Double_t sBPSuppClampTotHei; ///< BP support clamp height + static const Double_t sBPSuppClampLatThick; ///< BP support clamp lateral thick + static const Double_t sBPSuppClampShelfLen; ///< BP support clamp shelf len + static const Double_t sBPSuppClampShelfHei; ///< BP support clamp shelf hei + static const Double_t sBPSuppClampsXDist; ///< BP support clamps X distance + static const Double_t sBPSuppClampInsDmin; ///< BP support clamp insert Dmin + static const Double_t sBPSuppClampInsDmax; ///< BP support clamp insert Dmax + static const Double_t sBPSuppClampInsH; ///< BP support clamp insert H + static const Double_t sBPSuppClampInsXPos; ///< BP support clamp insert X + static const Double_t sBPSuppClampInsZPos; ///< BP support clamp insert Z + static const Double_t sBPSuppClampShimLen; ///< BP support clamp shim length + static const Double_t sBPSuppClampShimWid; ///< BP support clamp shim width + static const Double_t sBPSuppClampShimThick; ///< BP support clamp shim thick + static const Double_t sBPSuppClampM5High; ///< BP support clamp screw head height + static const Double_t sBPSuppClampM5Diam; ///< BP support clamp screw head diameter + static const Double_t sBPSuppClampM5ZPos; ///< BP support clamp screw Z pos + static const Double_t sBPSuppZPos; ///< BP support global Z pos + + // Closing Cross + static const Double_t sCageCrossXWidthTot; ///< Closing cross total X wid + static const Double_t sCageCrossXWidthExt; ///< Closing cross external X wid + static const Double_t sCageCrossXWidthInt; ///< Closing cross internal X wid + static const Double_t sCageCrossYHeightTot; ///< Closing cross total Y h + static const Double_t sCageCrossYHeightInt; ///< Closing cross internal Y h + static const Double_t sCageCrossYMid; ///< Closing cross Y mid point + static const Double_t sCageCrossZLength; ///< Closing cross Z length + static const Double_t sCageCrossBarThick; ///< Closing cross bar thickness + static const Double_t sCageCrossBarPhi; ///< Closing cross bar angle + + // MFT Rails inside the Cage + static const Double_t sCageMFTRailZLen; ///< Total length of the rail + static const Double_t sCageMFTRailZPos; ///< Rail global Z position + static const Double_t sCageMFTRailTotWidth; ///< Total width of the rail + static const Double_t sCageMFTRailExtWidth; ///< Width of the external part + static const Double_t sCageMFTRailIntWidth; ///< Width of the internal part + static const Double_t sCageMFTRailBaseWidth; ///< Width of the rail base + static const Double_t sCageMFTRailTotHeight; ///< Total height of the rail + static const Double_t sCageMFTRailExtHeight; ///< Height of the external part + static const Double_t sCageMFTRailIntHeight; ///< Height of the internal part + static const Double_t sCageMFTRailsXDist; ///< X distance between rails + + // MFT Rail hinges + static const Double_t sCageMFTHingeTotWid; ///< Total width of a hinge + static const Double_t sCageMFTHingeIntWid; ///< Width of hinge inner part + static const Double_t sCageMFTHingeHeight; ///< Total height of the rail + static const Double_t sCageMFTHingeIntHei; ///< Height of hinge inner part + static const Double_t sCageMFTHingeTotLen; ///< Total length of a hinge + static const Double_t sCageMFTHingeIntLen; ///< Length of hinge inner part + static const Double_t sCageMFTHingeBulgeWid; ///< Width of a hinge bulge + static const Double_t sCageMFTHingeBulgeHei; ///< Height of a hinge bulge + static const Double_t sCageMFTHingeBulgePos; ///< X position of a hinge bulge + ClassDefOverride(V3Cage, 0); // ITS v3 support geometry }; } // namespace its diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h index 15683feac4613..bc4fd6b0dadd4 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Layer.h @@ -353,43 +353,49 @@ class V3Layer : public V11Geometry static const Int_t sIBNChipRows; ///< IB chip rows in module static const Double_t sIBChipZGap; ///< Gap between IB chips on Z - static const Double_t sIBModuleZLength; ///< IB Module Length along Z - static const Double_t sIBFPCWiderXPlus; ///< FPC protrusion at X>0 - static const Double_t sIBFPCWiderXNeg; ///< FPC protrusion at X<0 - static const Double_t sIBFlexCableAlThick; ///< Thickness of FPC Aluminum - static const Double_t sIBFPCAlGNDWidth; ///< Width of total FPC Al Gnd - static const Double_t sIBFPCAlAnodeWidth1; ///< Width of FPC Al Anode - static const Double_t sIBFPCAlAnodeWidth2; ///< Width of FPC Al Anode - static const Double_t sIBFlexCableKapThick; ///< Thickness of FPC Kapton - static const Double_t sIBFlexCablePolyThick; ///< Thickness of FPC Coverlay - static const Double_t sIBFlexCapacitorXWid; ///< IB capaictor X width - static const Double_t sIBFlexCapacitorYHi; ///< IB capaictor Y height - static const Double_t sIBFlexCapacitorZLen; ///< IB capaictor Z length - static const Double_t sIBColdPlateWidth; ///< IB cold plate X width - static const Double_t sIBColdPlateZLen; ///< IB cold plate Z length - static const Double_t sIBGlueThick; ///< IB glue thickness - static const Double_t sIBCarbonFleeceThick; ///< IB carbon fleece thickness - static const Double_t sIBCarbonPaperThick; ///< IB Carbon Paper Thickness - static const Double_t sIBCarbonPaperWidth; ///< IB Carbon Paper X Width - static const Double_t sIBCarbonPaperZLen; ///< IB Carbon Paper Z Length - static const Double_t sIBK13D2UThick; ///< IB k13d2u prepreg thickness - static const Double_t sIBCoolPipeInnerD; ///< IB cooling inner diameter - static const Double_t sIBCoolPipeThick; ///< IB cooling pipe thickness - static const Double_t sIBCoolPipeXDist; ///< IB cooling pipe separation - static const Double_t sIBCoolPipeZLen; ///< IB cooling pipe length - static const Double_t sIBTopVertexWidth1; ///< IB TopVertex width - static const Double_t sIBTopVertexWidth2; ///< IB TopVertex width - static const Double_t sIBTopVertexHeight; ///< IB TopVertex height - static const Double_t sIBTopVertexAngle; ///< IB TopVertex aperture angle - static const Double_t sIBSideVertexWidth; ///< IB SideVertex width - static const Double_t sIBSideVertexHeight; ///< IB SideVertex height - static const Double_t sIBTopFilamentSide; ///< IB TopFilament side - static const Double_t sIBTopFilamentAlpha; ///< IB TopFilament angle - static const Double_t sIBTopFilamentInterZ; ///< IB TopFilament Z interdist - static const Double_t sIBEndSupportThick; ///< IB end support thickness - static const Double_t sIBEndSupportZLen; ///< IB end support length - static const Double_t sIBEndSupportXUp; ///< IB end support X up wide - static const Double_t sIBEndSupportOpenPhi; ///< IB end support opening phi + static const Double_t sIBModuleZLength; ///< IB Module Length along Z + static const Double_t sIBFPCWiderXPlus; ///< FPC protrusion at X>0 + static const Double_t sIBFPCWiderXNeg; ///< FPC protrusion at X<0 + static const Double_t sIBFlexCableAlThick; ///< Thickness of FPC Aluminum + static const Double_t sIBFPCAlGNDWidth; ///< Width of total FPC Al Gnd + static const Double_t sIBFPCAlAnodeWidth1; ///< Width of FPC Al Anode + static const Double_t sIBFPCAlAnodeWidth2; ///< Width of FPC Al Anode + static const Double_t sIBFlexCableKapThick; ///< Thickness of FPC Kapton + static const Double_t sIBFlexCablePolyThick; ///< Thickness of FPC Coverlay + static const Double_t sIBFlexCapacitor1XWid; ///< IB small capacitor X width + static const Double_t sIBFlexCapacitor1YHi; ///< IB small capacitor Y height + static const Double_t sIBFlexCapacitor1ZLen; ///< IB small capacitor Z length + static const Double_t sIBFlexCapacitor22XWid; ///< IB large capacitor X width + static const Double_t sIBFlexCapacitor22YHi; ///< IB large capacitor Y height + static const Double_t sIBFlexCapacitor22ZLen; ///< IB large capacitor Z length + static const Double_t sIBFlexResistorXWid; ///< IB FPC resistor X width + static const Double_t sIBFlexResistorYHi; ///< IB FPC resistor Y height + static const Double_t sIBFlexResistorZLen; ///< IB FPC resistor Z length + static const Double_t sIBColdPlateWidth; ///< IB cold plate X width + static const Double_t sIBColdPlateZLen; ///< IB cold plate Z length + static const Double_t sIBGlueThick; ///< IB glue thickness + static const Double_t sIBCarbonFleeceThick; ///< IB carbon fleece thickness + static const Double_t sIBCarbonPaperThick; ///< IB Carbon Paper Thickness + static const Double_t sIBCarbonPaperWidth; ///< IB Carbon Paper X Width + static const Double_t sIBCarbonPaperZLen; ///< IB Carbon Paper Z Length + static const Double_t sIBK13D2UThick; ///< IB k13d2u prepreg thickness + static const Double_t sIBCoolPipeInnerD; ///< IB cooling inner diameter + static const Double_t sIBCoolPipeThick; ///< IB cooling pipe thickness + static const Double_t sIBCoolPipeXDist; ///< IB cooling pipe separation + static const Double_t sIBCoolPipeZLen; ///< IB cooling pipe length + static const Double_t sIBTopVertexWidth1; ///< IB TopVertex width + static const Double_t sIBTopVertexWidth2; ///< IB TopVertex width + static const Double_t sIBTopVertexHeight; ///< IB TopVertex height + static const Double_t sIBTopVertexAngle; ///< IB TopVertex aperture angle + static const Double_t sIBSideVertexWidth; ///< IB SideVertex width + static const Double_t sIBSideVertexHeight; ///< IB SideVertex height + static const Double_t sIBTopFilamentSide; ///< IB TopFilament side + static const Double_t sIBTopFilamentAlpha; ///< IB TopFilament angle + static const Double_t sIBTopFilamentInterZ; ///< IB TopFilament Z interdist + static const Double_t sIBEndSupportThick; ///< IB end support thickness + static const Double_t sIBEndSupportZLen; ///< IB end support length + static const Double_t sIBEndSupportXUp; ///< IB end support X up wide + static const Double_t sIBEndSupportOpenPhi; ///< IB end support opening phi static const Double_t sIBConnectorXWidth; ///< IB Connectors Width static const Double_t sIBConnectorYTot; ///< IB Connectors total height diff --git a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h index b281051ecb9f4..352a6ce335c7e 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Services.h @@ -106,6 +106,11 @@ class V3Services : public V11Geometry /// \param mgr The GeoManager (used only to get the proper material) void createIBGammaConvWire(TGeoVolume* mother, const TGeoManager* mgr = gGeoManager); + /// Steering method to creates the ITS Services (tubes, cables, and the like) + /// \param motherVolume the TGeoVolume owing the volume supports + /// \param mgr The GeoManager (used only to get the proper material) + void createAllITSServices(TGeoVolume* motherVolume, const TGeoManager* mgr = gGeoManager); + /// Creates the Outer Barrel Gamma Conversion Wire /// \param mother the TGeoVolume owing the volume structure /// \param mgr The GeoManager (used only to get the proper material) diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 2732d4219f2ae..63d7a8ad8dfa2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -20,17 +20,18 @@ #include "ITSSimulation/V3Layer.h" #include "ITSSimulation/V3Services.h" #include "ITSSimulation/V3Cage.h" +#include "ITSSimulation/ITSSimParam.h" #include "DetectorsBase/Stack.h" #include "SimulationDataFormat/TrackReference.h" +#include "fairlogger/Logger.h" // for LOG, LOG_IF // FairRoot includes -#include "FairDetector.h" // for FairDetector -#include // for LOG, LOG_IF -#include "FairRootManager.h" // for FairRootManager -#include "FairRun.h" // for FairRun -#include "FairRuntimeDb.h" // for FairRuntimeDb -#include "FairVolume.h" // for FairVolume +#include "FairDetector.h" // for FairDetector +#include "FairRootManager.h" // for FairRootManager +#include "FairRun.h" // for FairRun +#include "FairRuntimeDb.h" // for FairRuntimeDb +#include "FairVolume.h" // for FairVolume #include "FairRootManager.h" #include "TGeoManager.h" // for TGeoManager, gGeoManager @@ -41,8 +42,10 @@ #include "TVirtualMC.h" // for gMC, TVirtualMC #include "TVirtualMCStack.h" // for TVirtualMCStack #include "TFile.h" // for TVirtualMCStack +#include "TGeoParallelWorld.h" #include // for NULL, snprintf +#include class FairModule; @@ -58,6 +61,7 @@ using Segmentation = o2::itsmft::SegmentationAlpide; using namespace o2::its; #ifdef ENABLE_UPGRADES +#include "ITS3Simulation/DescriptorInnerBarrelITS3.h" using namespace o2::its3; #endif @@ -126,7 +130,7 @@ void Detector::configOuterBarrelITS(int nInnerBarrelLayers, int buildLevel) } } -Detector::Detector(Bool_t active, TString name, TString its3Version) +Detector::Detector(Bool_t active, TString name) : o2::base::DetImpl(name, active), mTrackData(), /* @@ -142,13 +146,10 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) mHits(o2::utils::createSimVector()) { if (name == "ITS") { - mDescriptorIB.reset(new DescriptorInnerBarrelITS2(3)); + mDescriptorIB = std::make_shared(3); } else if (name == "IT3") { #ifdef ENABLE_UPGRADES - mDescriptorIB.reset(new DescriptorInnerBarrelITS3()); - if (its3Version != "") { - ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->setVersion(its3Version.Data()); - } + mDescriptorIB = std::make_shared(); #endif } else { LOG(fatal) << "Detector name not supported (options ITS and ITS3)"; @@ -159,14 +160,14 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) TString detName = GetName(); if (detName == "ITS") { - ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->configure(buildLevelITS); + dynamic_cast(mDescriptorIB.get())->configure(buildLevelITS); } else if (detName == "IT3") { #ifdef ENABLE_UPGRADES - ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->configure(); + dynamic_cast(mDescriptorIB.get())->configure(); #endif } - mNumberInnerLayers = mDescriptorIB.get()->getNumberOfLayers(); + mNumberInnerLayers = mDescriptorIB->getNumberOfLayers(); mNumberLayers = mNumberInnerLayers + sNumberOuterLayers; mLayerName.resize(mNumberLayers); @@ -186,10 +187,11 @@ Detector::Detector(Bool_t active, TString name, TString its3Version) for (int j{0}; j < mNumberLayers; j++) { if (detName == "IT3" && j < mNumberInnerLayers) { - mLayerName[j].Form("%s%d", GeometryTGeo::getITS3SensorPattern(), j); // See V3Layer + mLayerName[j].Form("%s%d", GeometryTGeo::getITS3SensorPattern(), j); } else { mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer } + LOGP(debug, "{}: mLayerName={}", j, mLayerName[j].Data()); } if (mNumberLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry @@ -387,7 +389,7 @@ Bool_t Detector::ProcessHits(FairVolume* vol) TLorentzVector positionStop; fMC->TrackPosition(positionStop); // Retrieve the indices with the volume path - int halfbarrel(0), stave(0), halfstave(0), chipinmodule(0), module; + int halfbarrel(0), stave(0), halfstave(0), chipinmodule(0), module(0); fMC->CurrentVolOffID(1, chipinmodule); fMC->CurrentVolOffID(2, module); fMC->CurrentVolOffID(3, halfstave); @@ -476,10 +478,11 @@ void Detector::createMaterials() Float_t dInox304 = 7.85; // Ceramic (for IB capacitors) (BaTiO3) + // Density includes soldering Float_t aCeramic[3] = {137.327, 47.867, 15.999}; Float_t zCeramic[3] = {56, 22, 8}; // Ba, Ti, O Float_t wCeramic[3] = {1, 1, 3}; // Molecular composition - Float_t dCeramic = 6.02; + Float_t dCeramic = 8.28; // Rohacell (C9 H13 N1 O2) Float_t aRohac[4] = {12.01, 1.01, 14.010, 16.}; @@ -502,6 +505,18 @@ void Detector::createMaterials() Float_t wBrass[3] = {0.58, 0.39, 0.03}; Float_t dBrass = 8.46; + // Polymer for ITS services + Float_t aPoly[2] = {12.01, 1.}; + Float_t zPoly[2] = {6., 1.}; + Float_t wPoly[2] = {0.857, .143}; + Float_t dPoly = 0.9; + + // Vespel for Beam Pipe Support (same definition of Polyimide in Pipe.cxx) + Float_t aVesp[4] = {16., 14., 12., 1.}; + Float_t zVesp[4] = {8., 7., 6., 1.}; + Float_t wVesp[4] = {5., 2., 22., 10.}; + Float_t dVesp = 1.42; + o2::base::Detector::Mixture(1, "AIR$", aAir, zAir, dAir, 4, wAir); o2::base::Detector::Medium(1, "AIR$", 1, 0, ifield, fieldm, tmaxfdAir, stemaxAir, deemaxAir, epsilAir, stminAir); @@ -577,7 +592,7 @@ void Detector::createMaterials() o2::base::Detector::Medium(38, "RIST110$", 38, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // Carbon prepreg (Cage) - o2::base::Detector::Material(33, "M46J6K$", 12.0107, 6, 1.84, 999, 999); + o2::base::Detector::Material(33, "M46J6K$", 12.0107, 6, 1.48, 999, 999); o2::base::Detector::Medium(33, "M46J6K$", 33, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // PEEK CF30 @@ -627,6 +642,22 @@ void Detector::createMaterials() o2::base::Detector::Material(35, "TITANIUM$", 47.867, 22, 4.506, 999, 999); o2::base::Detector::Medium(35, "TITANIUM$", 35, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // Carbon-Fiber-Reinforced Polymer + o2::base::Detector::Material(43, "CFRP$", 12.01, 6, 1.55, 999, 999); + o2::base::Detector::Medium(43, "CFRP$", 43, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Vespel for Beam Pipe Support + o2::base::Detector::Mixture(44, "VESPEL$", aVesp, zVesp, dVesp, -4, wVesp); + o2::base::Detector::Medium(44, "VESPEL$", 44, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Carbon for ITS services + o2::base::Detector::Material(41, "C4SERVICES$", 12.01, 6, 1.75, 999, 999); + o2::base::Detector::Medium(41, "C4SERVICES$", 41, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + + // Polymer for ITS services + o2::base::Detector::Mixture(42, "POLY4SERVICES$", aPoly, zPoly, dPoly, 2, wPoly); + o2::base::Detector::Medium(42, "POLY4SERVICES$", 42, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + // For ITS3 // Araldite 2011 @@ -634,12 +665,12 @@ void Detector::createMaterials() // ERG Duocel o2::base::Detector::Material(39, "ERGDUOCEL$", 12.0107, 6, 0.06, 999, 999); - o2::base::Detector::Medium(39, "ERGDUOCEL$", 33, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(39, "ERGDUOCEL$", 39, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // Impregnated carbon fleece // (as educated guess we assume 50% carbon fleece 50% Araldite glue) o2::base::Detector::Material(40, "IMPREG_FLEECE$", 12.0107, 6, 0.5 * (dAraldite + 0.4), 999, 999); - o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 34, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 40, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); } void Detector::EndOfEvent() { Reset(); } @@ -694,8 +725,8 @@ void Detector::defineLayer(Int_t nlay, Double_t phi0, Double_t r, Int_t nstav, I // Return: // none. - LOG(info) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit - << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; + LOG(debug) << "L# " << nlay << " Phi:" << phi0 << " R:" << r << " Nst:" << nstav << " Nunit:" << nunit + << " Lthick:" << lthick << " Dthick:" << dthick << " DetID:" << dettypeID << " B:" << buildLevel; if (nlay >= mNumberLayers || nlay < 0) { LOG(error) << "Wrong layer number " << nlay; @@ -771,7 +802,7 @@ TGeoVolume* Detector::createWrapperVolume(Int_t id) switch (id) { case 0: // IB Layer 0,1,2: simple cylinder { - tube = (TGeoShape*)mDescriptorIB.get()->defineWrapperVolume(); + tube = (TGeoShape*)mDescriptorIB->defineWrapperVolume(); } break; case 1: // MB Layer 3,4: complex Pcon to avoid MFT overlaps { @@ -959,6 +990,8 @@ void Detector::constructDetectorGeometry() createOuterBarrelServices(wrapVols[2]); createOuterBarrelSupports(vITSV); + createITSServices(vALIC); + mServicesGeometry->createOBGammaConvWire(vITSV); // Finally create and place the cage @@ -1038,6 +1071,24 @@ void Detector::createOuterBarrelSupports(TGeoVolume* motherVolume) mServicesGeometry->createOBCYSSCylinder(motherVolume); } +void Detector::createITSServices(TGeoVolume* motherVolume) +{ + // + // Creates the ITS services: tubes, cables and the like + // + // Input: + // motherVolume : the volume hosting the supports + // + // Output: + // + // Return: + // + // Created: 12 Apr 2023 Mario Sitta + // + + mServicesGeometry->createAllITSServices(motherVolume); +} + void Detector::addAlignableVolumes() const { // @@ -1056,7 +1107,7 @@ void Detector::addAlignableVolumes() const TString detName = GetName(); TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); - TString sname = GeometryTGeo::composeSymNameITS((detName == "IT3")); + TString sname = GeometryTGeo::composeSymNameITS(); LOG(debug) << sname << " <-> " << path; @@ -1067,15 +1118,19 @@ void Detector::addAlignableVolumes() const Int_t lastUID = 0; for (Int_t lr = 0; lr < mNumberLayers; lr++) { if (lr < mNumberInnerLayers) { +#ifdef ENABLE_UPGRADES if (detName == "ITS") { ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); + } else { + ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); } +#else + ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); +#endif } else { addAlignableVolumesLayer(lr, path, lastUID); } } - - return; } void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const @@ -1098,8 +1153,6 @@ void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) for (Int_t hb = start; hb < nhbarrel; hb++) { addAlignableVolumesHalfBarrel(lr, hb, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent, Int_t& lastUID) const @@ -1127,8 +1180,6 @@ void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent for (int st = 0; st < nstaves; st++) { addAlignableVolumesStave(lr, hb, st, path, lastUID); } - - return; } void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& parent, Int_t& lastUID) const @@ -1155,8 +1206,6 @@ void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& p for (Int_t sst = start; sst < nhstave; sst++) { addAlignableVolumesHalfStave(lr, hb, st, sst, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const @@ -1186,8 +1235,6 @@ void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t for (Int_t md = start; md < nmodules; md++) { addAlignableVolumesModule(lr, hb, st, hst, md, path, lastUID); } - - return; } void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const @@ -1216,8 +1263,6 @@ void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst for (Int_t ic = 0; ic < nchips; ic++) { addAlignableVolumesChip(lr, hb, st, hst, md, ic, path, lastUID); } - - return; } void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, @@ -1236,17 +1281,15 @@ void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, LOG(debug) << "Add " << sname << " <-> " << path; - if (!gGeoManager->SetAlignableEntry(sname, path.Data(), modUID)) { + if (gGeoManager->SetAlignableEntry(sname, path.Data(), modUID) == nullptr) { LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; } - - return; } void Detector::defineSensitiveVolumes() { TGeoManager* geoManager = gGeoManager; - TGeoVolume* v; + TGeoVolume* v = nullptr; TString volumeName; @@ -1263,6 +1306,53 @@ void Detector::defineSensitiveVolumes() } } +void Detector::fillParallelWorld() const +{ + TGeoParallelWorld* pw = gGeoManager->GetParallelWorld(); + if (pw == nullptr) { + LOG(error) << "Parallel world was not created"; + return; + } + auto& param = ITSSimParam::Instance(); + + for (int iL{0}; iL < mNumberLayers; ++iL) { + auto const layer = mGeometry[iL]; + int nhbarrels = layer->getNumberOfHalfBarrelsPerParent(); + int nstaves = layer->getNumberOfStavesPerParent(); + int nhstaves = layer->getNumberOfHalfStavesPerParent(); + int nmodules = layer->getNumberOfModulesPerParent(); + int nchips = layer->getNumberOfChipsPerParent(); + + for (int iHB{0}; iHB < nhbarrels; ++iHB) { + for (int iS{0}; iS < nstaves; ++iS) { + for (int iHS{nhstaves > 0 ? 0 : -1}; iHS < nhstaves; ++iHS) { + for (int iM{nmodules > 0 ? 0 : -1}; iM < nmodules; ++iM) { + for (int iC{0}; iC < nchips; ++iC) { + TString sname = GeometryTGeo::composeSymNameChip(iL, iHB, iS, iHS, iM, iC); + TGeoPNEntry* pne = gGeoManager->GetAlignableEntry(sname); + auto path = pne->GetTitle(); + + if (param.addMetalToPW) { + TString metalPath = Form("%s/MetalStack_1", path); + gGeoManager->MakePhysicalNode(metalPath); + pw->AddNode(metalPath); + } + if (param.addSensorToPW) { + TString sensorPath = Form("%s/ITSUSensor%d_1", path, iL); + gGeoManager->MakePhysicalNode(sensorPath); + pw->AddNode(sensorPath); + } + if (param.addChipToPW) { + pw->AddNode(path); + } + } + } + } + } + } + } +} + Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TVector3& endPos, const TVector3& startMom, double startE, double endTime, double eLoss, unsigned char startStatus, unsigned char endStatus) @@ -1272,3 +1362,11 @@ Hit* Detector::addHit(int trackID, int detID, const TVector3& startPos, const TV } ClassImp(o2::its::Detector); + +// Define Factory method for calling from the outside +extern "C" { +o2::base::Detector* create_detector_its(const char* name, bool active) +{ + return o2::its::Detector::create(name, active); +} +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx b/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx new file mode 100644 index 0000000000000..9f7ba12a3c677 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSDataSimulator.cxx @@ -0,0 +1,147 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ITSDataSimulator.cxx +/// \author knaumov@cern.ch + +#include "DataFormatsITSMFT/Digit.h" +#include "ITSSimulation/ITSDataSimulator.h" + +#include +#include +#include + +namespace bpo = boost::program_options; +using namespace o2::itsmft; + +std::vector ITSDataSimulator::generateChipData() +{ + std::vector vec; + uint32_t numOfPixels = rand() % mMaxPixelsPerChip; + while (vec.size() < numOfPixels) { + int row = rand() % SegmentationAlpide::NRows; + int col = rand() % SegmentationAlpide::NCols; + PixelData pixel(row, col); + vec.push_back(pixel); + } + std::sort(vec.begin(), vec.end()); + if (!mDoErrors) { + // If errors are disabled, chips should not contain + // pixels fired multiple times + auto iter = std::unique(vec.begin(), vec.end()); + vec.erase(iter, vec.end()); + } + return vec; +} + +void ITSDataSimulator::simulate() +{ + // Generate the chip data + std::map> chipData; + while (chipData.size() < mNumberOfChips) { + uint32_t chipID = rand() % MaxChipID; + if (!chipData.contains(chipID)) { + chipData.emplace(chipID, generateChipData()); + } + } + + if (mDoDigits) { + std::vector digVec; + for (auto const& chip : chipData) { + uint32_t chipID = chip.first; + const std::vector& pixels = chip.second; + for (auto pixel : pixels) { + Digit dig(chipID, pixel.getRow(), pixel.getCol()); + digVec.push_back(dig); + } + } + // TODO: Save the digits to a file + } +} + +int main(int argc, char** argv) +{ + bpo::variables_map vm; + bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + + "Simulates ALPIDE data\n"); + bpo::options_description opt_hidden(""); + bpo::options_description opt_all; + bpo::positional_options_description opt_pos; + + try { + auto add_option = opt_general.add_options(); + add_option("help,h", "Print this help message"); + add_option("verbosity,v", bpo::value()->default_value(0), + "verbosity level [0 = no output]"); + add_option("digits", + bpo::value()->default_value(false)->implicit_value(true), + "generate the data in the digits format"); + add_option("enable-errors", + bpo::value()->default_value(false)->implicit_value(true), + "enable additon of errors to the raw data"); + add_option("seed", bpo::value()->default_value(0), + "random seed for data generation"); + add_option( + "max-pixels-per-chip", bpo::value()->default_value(100), + ("maximum number of fired pixels per chip (0 - " + + std::to_string(ITSDataSimulator::MaxPixelsPerChip) + + ")") + .c_str()); + add_option("number-of-chip", bpo::value()->default_value(10), + ("number of chips to be present in the data (0 - " + + std::to_string(ITSDataSimulator::MaxChipID) + ")") + .c_str()); + add_option("configKeyValues", bpo::value()->default_value(""), + "comma-separated configKeyValues"); + + opt_all.add(opt_general).add(opt_hidden); + bpo::store(bpo::command_line_parser(argc, argv) + .options(opt_all) + .positional(opt_pos) + .run(), + vm); + + if (vm.count("help")) { + std::cout << opt_general << std::endl; + exit(0); + } + + if (vm["max-pixels-per-chip"].as() > + ITSDataSimulator::MaxPixelsPerChip) { + std::cerr << "Invalid max pixels per chip, valid range (0, " + << ITSDataSimulator::MaxPixelsPerChip << ")" << std::endl; + exit(1); + } + + if (vm["number-of-chip"].as() > ITSDataSimulator::MaxChipID) { + std::cerr << "Invalid number of chips, valid range (0, " + << ITSDataSimulator::MaxChipID << ")" << std::endl; + exit(1); + } + + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << "ERROR: " << e.what() << std::endl + << std::endl; + std::cerr << opt_general << std::endl; + exit(1); + } catch (std::exception& e) { + std::cerr << e.what() << ", application will now exit" << std::endl; + exit(2); + } + + ITSDataSimulator simulator( + vm["seed"].as(), vm["number-of-chip"].as(), + vm["max-pixels-per-chip"].as(), vm["digits"].as(), + vm["enable-errors"].as()); + + simulator.simulate(); +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx b/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx new file mode 100644 index 0000000000000..564e9942a26d5 --- /dev/null +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSSimParam.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITSSimulation/ITSSimParam.h" +O2ParamImpl(o2::its::ITSSimParam); diff --git a/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h b/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h index 92e10577b6abf..bd4ce228deb1b 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h +++ b/Detectors/ITSMFT/ITS/simulation/src/ITSSimulationLinkDef.h @@ -23,5 +23,6 @@ #pragma link C++ class o2::its::Detector + ; #pragma link C++ class o2::its::DescriptorInnerBarrelITS2 + ; #pragma link C++ class o2::base::DetImpl < o2::its::Detector> + ; +#pragma link C++ class o2::its::ITSSimParam + ; #endif diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx index 1b09c1cad6e24..bd9ce1cd333a2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx @@ -19,13 +19,13 @@ #include // for LOG -//#include // for TGeoArb8 -//#include // for TGeoBBox -//#include // for TGeoConeSeg, TGeoCone +#include // for TGeoArb8 +#include // for TGeoBBox +#include // for TGeoConeSeg, TGeoCone #include // for TGeoPcon #include // for TGeoManager, gGeoManager #include // for TGeoCombiTrans, TGeoRotation, etc -//#include // for TGeoTrd1 +// #include // for TGeoTrd1 #include // for TGeoTube, TGeoTubeSeg #include // for TGeoVolume, TGeoVolumeAssembly #include // for TGeoXtru @@ -79,6 +79,8 @@ const Double_t V3Cage::sCageSidePanelGuideInHi = 204.0 * sMm; const Double_t V3Cage::sCageSidePanelGuideWide = 44.0 * sMm; const Double_t V3Cage::sCageSidePanelGuidThik1 = 6.0 * sMm; const Double_t V3Cage::sCageSidePanelGuidThik2 = 8.0 * sMm; +const Double_t V3Cage::sCageSidePanelMidBarWid = 15.0 * sMm; +const Double_t V3Cage::sCageSidePanelSidBarWid = 15.0 * sMm; const Double_t V3Cage::sCageSidePanelRail1Ypos[2] = {226.5 * sMm, 147.5 * sMm}; const Double_t V3Cage::sCageSidePanelRail2Ypos = 74.5 * sMm; @@ -106,6 +108,87 @@ const Double_t V3Cage::sCageECCableCrosInThik = 4 * sMm; const Double_t V3Cage::sCageECCableCrosInZLen = 10.2 * sMm; const Double_t V3Cage::sCageECCableCrosSidWid = 8 * sMm; +const Double_t V3Cage::sBPSuppCollarIntD = 53 * sMm; +const Double_t V3Cage::sBPSuppCollarExtD = 57 * sMm; +const Double_t V3Cage::sBPSuppCollarBushD = 52 * sMm; +const Double_t V3Cage::sBPSuppUpperCollarLen = 78 * sMm; +const Double_t V3Cage::sBPSuppUpperCollarHei = 4 * sMm; +const Double_t V3Cage::sBPSuppLowerCollarLen = 151 * sMm; +const Double_t V3Cage::sBPSuppLowerCollarTlX = 40.5 * sMm; +const Double_t V3Cage::sBPSuppLowCollHolDist = 100 * sMm; +const Double_t V3Cage::sBPSuppLowCollTailHei = 6 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamLen = 370 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamWid = 40 * sMm; +const Double_t V3Cage::sBPSuppCollarBeamHei = 12 * sMm; +const Double_t V3Cage::sBPSuppBracketTotLen = 57 * sMm; +const Double_t V3Cage::sBPSuppBracketWidth = 25 * sMm; +const Double_t V3Cage::sBPSuppBracketInLen = 20 * sMm; +const Double_t V3Cage::sBPSuppBracketInHei = 8 * sMm; +const Double_t V3Cage::sBPSuppBracketTailLen = 18.5 * sMm; +const Double_t V3Cage::sBPSuppBracketTailHei = 3 * sMm; +const Double_t V3Cage::sBPSuppBrktCentHoleX = 31.5 * sMm; +const Double_t V3Cage::sBPSuppBrktCentHoleD = 6 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleX = 24.5 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleD = 3.2 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleW = 4 * sMm; +const Double_t V3Cage::sBPSuppBrktLatHoleH = 2.5 * sMm; +const Double_t V3Cage::sBPSuppBrktHolesY = 0.5 * sMm; +const Double_t V3Cage::sBPSuppCollarM4High = 2.2 * sMm; +const Double_t V3Cage::sBPSuppCollarM4Diam = 7.5 * sMm; +const Double_t V3Cage::sBPSuppCollarM4XDist = 68 * sMm; +const Double_t V3Cage::sBPSuppCollarM4ZPos = 7 * sMm; +const Double_t V3Cage::sBPSuppClampTotLen = 55 * sMm; +const Double_t V3Cage::sBPSuppClampTotWid = 23 * sMm; +const Double_t V3Cage::sBPSuppClampTotHei = 13 * sMm; +const Double_t V3Cage::sBPSuppClampLatThick = 5 * sMm; +const Double_t V3Cage::sBPSuppClampShelfLen = 25 * sMm; +const Double_t V3Cage::sBPSuppClampShelfHei = 4.5 * sMm; +const Double_t V3Cage::sBPSuppClampsXDist = 944 * sMm; +const Double_t V3Cage::sBPSuppClampInsDmin = 7 * sMm; +const Double_t V3Cage::sBPSuppClampInsDmax = 11 * sMm; +const Double_t V3Cage::sBPSuppClampInsH = 2.9 * sMm; +const Double_t V3Cage::sBPSuppClampInsXPos = 15 * sMm; +const Double_t V3Cage::sBPSuppClampInsZPos = 7 * sMm; +const Double_t V3Cage::sBPSuppClampShimLen = 26 * sMm; +const Double_t V3Cage::sBPSuppClampShimWid = 15 * sMm; +const Double_t V3Cage::sBPSuppClampShimThick = 2.5 * sMm; +const Double_t V3Cage::sBPSuppClampM5High = 2.7 * sMm; +const Double_t V3Cage::sBPSuppClampM5Diam = 8.5 * sMm; +const Double_t V3Cage::sBPSuppClampM5ZPos = 20 * sMm; +const Double_t V3Cage::sBPSuppZPos = 1801 * sMm; + +const Double_t V3Cage::sCageCrossXWidthTot = 973 * sMm; +const Double_t V3Cage::sCageCrossXWidthExt = 944 * sMm; +const Double_t V3Cage::sCageCrossXWidthInt = 904 * sMm; +const Double_t V3Cage::sCageCrossYHeightTot = 244 * sMm; +const Double_t V3Cage::sCageCrossYHeightInt = 220 * sMm; +const Double_t V3Cage::sCageCrossYMid = (126 + 5.5) * sMm; +const Double_t V3Cage::sCageCrossZLength = 8 * sMm; +const Double_t V3Cage::sCageCrossBarThick = 20 * sMm; +const Double_t V3Cage::sCageCrossBarPhi = 25; // Deg + +// MFT rails inside Cage +const Double_t V3Cage::sCageMFTRailZLen = 1874 * sMm; +const Double_t V3Cage::sCageMFTRailZPos = 6.3 * sMm; +const Double_t V3Cage::sCageMFTRailTotWidth = 27 * sMm; +const Double_t V3Cage::sCageMFTRailExtWidth = 24 * sMm; +const Double_t V3Cage::sCageMFTRailIntWidth = 17.5 * sMm; +const Double_t V3Cage::sCageMFTRailBaseWidth = 22 * sMm; +const Double_t V3Cage::sCageMFTRailTotHeight = 8.9 * sMm; +const Double_t V3Cage::sCageMFTRailExtHeight = 5.9 * sMm; +const Double_t V3Cage::sCageMFTRailIntHeight = 3.5 * sMm; +const Double_t V3Cage::sCageMFTRailsXDist = 44 * sMm; + +const Double_t V3Cage::sCageMFTHingeTotWid = 164 * sMm; +const Double_t V3Cage::sCageMFTHingeIntWid = 141.3 * sMm; +const Double_t V3Cage::sCageMFTHingeHeight = 8 * sMm; +const Double_t V3Cage::sCageMFTHingeIntHei = 6 * sMm; +const Double_t V3Cage::sCageMFTHingeTotLen = 41 * sMm; +const Double_t V3Cage::sCageMFTHingeIntLen = 28 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgeWid = 10 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgeHei = 10 * sMm; +const Double_t V3Cage::sCageMFTHingeBulgePos = 7 * sMm; + ClassImp(V3Cage); V3Cage::V3Cage() @@ -137,13 +220,15 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) // // Local variables - Double_t zunit, xpos, zpos; + Double_t zunit, xpos, ypos, zpos; // Create the cover elements TGeoVolume* cageCover = createCageCover(mgr); TGeoVolume* cageCoverRib = createCageCoverRib(mgr); TGeoVolume* cageEndCap = createCageEndCap(mgr); TGeoVolume* cageSidePanel = createCageSidePanel(mgr); + TGeoVolume* cageBPSupport = createBeamPipeSupport(mgr); + TGeoVolume* cageClosingCross = createCageClosingCross(mgr); // Now place all elements mother->AddNode(cageCover, 1, new TGeoTranslation(0, sCageYInBarrel, 0)); @@ -170,6 +255,10 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) mother->AddNode(cageSidePanel, 1, new TGeoTranslation(xpos, sCageYInBarrel, zposSP)); mother->AddNode(cageSidePanel, 2, new TGeoCombiTrans(-xpos, sCageYInBarrel, zposSP, new TGeoRotation("", 180, 0, 0))); + Double_t zposCC = -zpos + sCageSidePanelLength + sCageCrossZLength / 2; + mother->AddNode(cageClosingCross, 1, new TGeoTranslation(0, sCageYInBarrel, zposCC)); + mother->AddNode(cageClosingCross, 2, new TGeoCombiTrans(0, sCageYInBarrel, zposCC, new TGeoRotation("", 0, 180, 0))); + // The end cap is only on C side zpos += sCageECCableCrosTotZ / 2; mother->AddNode(cageEndCap, 1, new TGeoTranslation(0, sCageYInBarrel, -zpos)); @@ -179,6 +268,14 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) mother->AddNode(cageCoverRib, 5, new TGeoTranslation(0, sCageYInBarrel, zpos)); mother->AddNode(cageCoverRib, 6, new TGeoCombiTrans(0, sCageYInBarrel, zpos, new TGeoRotation("", 180, 0, 0))); + // The Beam Pipe Support on A side + ypos = sCageYInBarrel - sBPSuppLowCollTailHei / 2; + zpos = sBPSuppZPos + sBPSuppCollarBeamWid / 2; + mother->AddNode(cageBPSupport, 1, new TGeoTranslation(0, ypos, zpos)); + + // The MFT Rails inside the Cage + createAndPlaceMFTRailsInsideCage(mother, mgr); + return; } @@ -347,6 +444,7 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) // The side panel as a TGeoVolumeAssembly // // Created: 30 Sep 2022 Mario Sitta + // Updated: 20 May 2023 Mario Sitta Mid and side bars added // // Local variables @@ -377,6 +475,18 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) // The shortest rails TGeoCompositeShape* rail3Sh = createCageSidePanelRail(sCageSidePanelRail3Len, 3); + // The middle bar: a BBox + xlen = sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelMidBarWid / 2; + zlen = (sCageSidePanelLength - sCageSidePanelRail3Len - sCageSidePanelSidBarWid) / 2; + TGeoBBox* midBarSh = new TGeoBBox(xlen, ylen, zlen); + + // The side bar: a BBox + xlen = sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelWidth / 2; + zlen = sCageSidePanelSidBarWid / 2; + TGeoBBox* sidBarSh = new TGeoBBox(xlen, ylen, zlen); + // The elements of the guide: // - the vertical part: a BBox xlen = sCageSidePanelGuidThik2 / 2; @@ -433,6 +543,14 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) rail3Vol->SetFillColor(kGray); rail3Vol->SetLineColor(kGray); + TGeoVolume* midBarVol = new TGeoVolume("CageSidePanelMiddleBar", midBarSh, medAlAlloy); + midBarVol->SetFillColor(kGray); + midBarVol->SetLineColor(kGray); + + TGeoVolume* sidBarVol = new TGeoVolume("CageSidePanelSideBar", sidBarSh, medAlAlloy); + sidBarVol->SetFillColor(kGray); + sidBarVol->SetLineColor(kGray); + TGeoVolume* guideVol = new TGeoVolume("CageSidePanelGuide", guideSh, medFabric); guideVol->SetFillColor(kViolet); guideVol->SetLineColor(kViolet); @@ -464,6 +582,12 @@ TGeoVolume* V3Cage::createCageSidePanel(const TGeoManager* mgr) sidePanelVol->AddNode(rail3Vol, j + 4, new TGeoTranslation(xpos, -ypos, zpos)); } + zpos = sCageSidePanelLength / 2 - midBarSh->GetDZ() - sCageSidePanelSidBarWid; + sidePanelVol->AddNode(midBarVol, 1, new TGeoTranslation(0, 0, -zpos)); + + zpos = sCageSidePanelLength / 2 - sidBarSh->GetDZ(); + sidePanelVol->AddNode(sidBarVol, 1, new TGeoTranslation(0, 0, -zpos)); + xpos = sCageSidePanelCoreThick / 2 + sCageSidePanelFoilThick + sCageSidePanelGuidThik2 / 2; zpos = (sCageSidePanelLength - sCageSidePanelGuideLen) / 2; sidePanelVol->AddNode(guideVol, 1, new TGeoTranslation(xpos, 0, -zpos)); @@ -488,6 +612,7 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c // The side panel core or foil as a TGeoCompositeShape // // Created: 07 Oct 2022 Mario Sitta + // Updated: 20 May 2023 Mario Sitta Mid and side bars added // // Local variables @@ -550,6 +675,30 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c rail3Mat[j + 3]->RegisterYourself(); } + // The hole for the middle bar: a BBox + xlen = 1.1 * sCageSidePanelCoreThick / 2; + ylen = sCageSidePanelMidBarWid / 2; + zlen = (sCageSidePanelLength - sCageSidePanelRail3Len) / 2; + TGeoBBox* midBarHol = new TGeoBBox(xlen, ylen, zlen); + midBarHol->SetName("midbar"); + + zpos = sCageSidePanelRail3Len / 2; + TGeoTranslation* midBarMat = new TGeoTranslation(0, 0, -zpos); + midBarMat->SetName("midbarmat"); + midBarMat->RegisterYourself(); + + // The hole for the side bar: a BBox + xlen = 1.1 * sCageSidePanelCoreThick / 2; + ylen = 1.1 * sCageSidePanelWidth / 2; + zlen = sCageSidePanelSidBarWid; + TGeoBBox* sidBarHol = new TGeoBBox(xlen, ylen, zlen); + sidBarHol->SetName("sidebar"); + + zpos = sCageSidePanelLength / 2; + TGeoTranslation* sidBarMat = new TGeoTranslation(0, 0, -zpos); + sidBarMat->SetName("sidebarmat"); + sidBarMat->RegisterYourself(); + // The actual shape: a CompositeShape TString compoShape = Form("%sbodyshape", shpref); for (Int_t j = 0; j < 4; j++) { @@ -562,6 +711,11 @@ TGeoCompositeShape* V3Cage::createCageSidePanelCoreFoil(const Double_t xthick, c compoShape += Form("-%sshortrail:shortrailmat%d", shpref, j); } + // The mid and side bar holes are present only in the core shape + if (strcmp(shpref, "core") == 0) { + compoShape += "-midbar:midbarmat-sidebar:sidebarmat"; + } + TGeoCompositeShape* corefoilSh = new TGeoCompositeShape(compoShape); // Now return the shape @@ -898,3 +1052,861 @@ TGeoCompositeShape* V3Cage::createCageEndCapCableCross(const TGeoManager* mgr) return cableCross; } + +TGeoVolume* V3Cage::createBeamPipeSupport(const TGeoManager* mgr) +{ + // + // Creates the Beam Pipe Support inside the Cage on the A side + // (from drawings ALIITSUP1064, ALIITSUP1059, ALIITSUP1057, ALIITSUP1058, + // ALIITSUP1056, ALIITSUP0823, ALIITSUP0273, ALIITSUP1060, ALIITSUP1062) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // The beam pipe support as a TGeoVolumeAssembly + // + // Created: 03 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xpos, ypos, zpos; + + // The TGeoVolumeAssembly holding all elements + TGeoVolumeAssembly* bpSuppVol = new TGeoVolumeAssembly("CageBeamPipeSupport"); + + // The lower collar + TGeoCompositeShape* lowCollarSh = createBPSuppLowerCollar(); + + // The upper collar + TGeoCompositeShape* upCollarSh = createBPSuppUpperCollar(); + + // Each one of the collar beams + TGeoCompositeShape* collarBeamSh = createBPSuppCollarBeam(); + + // Each one of the lateral brackets + TGeoCompositeShape* bracketSh = createBPSuppBracket(); + + // Each one of the lateral clamps + TGeoCompositeShape* clampSh = createBPSuppClamp(); + + // The Vespel bushing: a Tube + TGeoTube* bushSh = new TGeoTube(0.5 * sBPSuppCollarBushD, 0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppBracketWidth); + + // The clamp shim: a BBox + TGeoBBox* shimSh = new TGeoBBox(0.5 * sBPSuppClampShimWid, 0.5 * sBPSuppClampShimThick, 0.5 * sBPSuppClampShimLen); + + // The M4 screw head: a Tube + TGeoTube* m4ScrewSh = new TGeoTube(0, 0.5 * sBPSuppCollarM4Diam, 0.5 * sBPSuppCollarM4High); + + // The M5 screw head: a Tube + TGeoTube* m5ScrewSh = new TGeoTube(0, 0.5 * sBPSuppClampM5Diam, 0.5 * sBPSuppClampM5High); + + // The threaded insert head: a Cone + TGeoCone* insHeadSh = new TGeoCone(0.5 * sBPSuppClampInsH, 0, 0.5 * sBPSuppClampInsDmin, 0, 0.5 * sBPSuppClampInsDmax); + + // We have all the shapes: now create the real volumes + TGeoMedium* medCFRP = mgr->GetMedium(Form("%s_CFRP$", GetDetName())); + TGeoMedium* medTitanium = mgr->GetMedium(Form("%s_TITANIUM$", GetDetName())); + TGeoMedium* medSteel = mgr->GetMedium(Form("%s_INOX304$", GetDetName())); + TGeoMedium* medBrass = mgr->GetMedium(Form("%s_BRASS$", GetDetName())); + TGeoMedium* medVespel = mgr->GetMedium(Form("%s_VESPEL$", GetDetName())); + + Color_t kTitanium = kGray + 1; // Darker gray + + TGeoVolume* lowCollarVol = new TGeoVolume("BPSupportLowerCollar", lowCollarSh, medTitanium); + lowCollarVol->SetFillColor(kTitanium); + lowCollarVol->SetLineColor(kTitanium); + + TGeoVolume* upCollarVol = new TGeoVolume("BPSupportUpperCollar", upCollarSh, medTitanium); + upCollarVol->SetFillColor(kTitanium); + upCollarVol->SetLineColor(kTitanium); + + TGeoVolume* bushVol = new TGeoVolume("BPSupportCollarBushing", bushSh, medVespel); + bushVol->SetFillColor(kGreen); + bushVol->SetLineColor(kGreen); + + TGeoVolume* collarBeamVol = new TGeoVolume("BPSupportCollarBeam", collarBeamSh, medCFRP); + collarBeamVol->SetFillColor(kBlue); + collarBeamVol->SetLineColor(kBlue); + + TGeoVolume* bracketVol = new TGeoVolume("BPSupportBracket", bracketSh, medTitanium); + bracketVol->SetFillColor(kTitanium); + bracketVol->SetLineColor(kTitanium); + + TGeoVolume* clampVol = new TGeoVolume("BPSupportClamp", clampSh, medTitanium); + clampVol->SetFillColor(kTitanium); + clampVol->SetLineColor(kTitanium); + + TGeoVolume* shimVol = new TGeoVolume("BPSupportClampShim", shimSh, medBrass); + shimVol->SetFillColor(kOrange - 4); // Brownish + shimVol->SetLineColor(kOrange - 4); + + TGeoVolume* m4ScrewVol = new TGeoVolume("BPSupportCollarScrew", m4ScrewSh, medTitanium); + m4ScrewVol->SetFillColor(kTitanium); + m4ScrewVol->SetLineColor(kTitanium); + + TGeoVolume* m5ScrewVol = new TGeoVolume("BPSupportClampScrew", m5ScrewSh, medSteel); + m5ScrewVol->SetFillColor(kGray); + m5ScrewVol->SetLineColor(kGray); + + TGeoVolume* insHeadVol = new TGeoVolume("BPSupportClampInsert", insHeadSh, medSteel); + insHeadVol->SetFillColor(kGray); + insHeadVol->SetLineColor(kGray); + + // Then build up the beam support + bpSuppVol->AddNode(lowCollarVol, 1, nullptr); + + ypos = sBPSuppLowCollTailHei / 2; + bpSuppVol->AddNode(upCollarVol, 1, new TGeoTranslation(0, ypos, 0)); + + bpSuppVol->AddNode(bushVol, 1, new TGeoTranslation(0, ypos, 0)); + + xpos = sBPSuppCollarM4XDist / 2; + ypos += (sBPSuppUpperCollarHei + m4ScrewSh->GetDz()); + zpos = sBPSuppCollarM4ZPos; + bpSuppVol->AddNode(m4ScrewVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 2, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(m4ScrewVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + + xpos = sBPSuppLowerCollarLen / 2 - sBPSuppBracketInLen + sBPSuppCollarBeamLen / 2; + bpSuppVol->AddNode(collarBeamVol, 1, new TGeoCombiTrans(xpos, 0, 0, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(collarBeamVol, 2, new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 0, 90, 0))); + + xpos += (sBPSuppCollarBeamLen / 2 - sBPSuppBracketInLen); + bpSuppVol->AddNode(bracketVol, 1, new TGeoTranslation(xpos, 0, 0)); + bpSuppVol->AddNode(bracketVol, 2, new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 90, 180, -90))); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampTotWid + shimSh->GetDX(); + ypos = -shimSh->GetDY(); + bpSuppVol->AddNode(shimVol, 1, new TGeoTranslation(xpos, ypos, 0)); + bpSuppVol->AddNode(shimVol, 2, new TGeoTranslation(-xpos, ypos, 0)); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampLatThick; + ypos -= shimSh->GetDY(); + bpSuppVol->AddNode(clampVol, 1, new TGeoTranslation(-xpos, ypos, 0)); + bpSuppVol->AddNode(clampVol, 2, new TGeoCombiTrans(xpos, ypos, 0, new TGeoRotation("", 90, 180, -90))); + + xpos -= m5ScrewSh->GetDz(); + ypos += (0.5 * sBPSuppClampTotHei - sBPSuppClampShelfHei); + zpos = sBPSuppClampM5ZPos; + bpSuppVol->AddNode(m5ScrewVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 2, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 3, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 90, 90, -90))); + bpSuppVol->AddNode(m5ScrewVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 90, 90, -90))); + + xpos = 0.5 * sBPSuppClampsXDist - sBPSuppClampInsXPos; + ypos = sBPSuppBracketTailHei + insHeadSh->GetDz(); + zpos = sBPSuppClampInsZPos; + bpSuppVol->AddNode(insHeadVol, 1, new TGeoCombiTrans(xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 2, new TGeoCombiTrans(-xpos, ypos, zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 3, new TGeoCombiTrans(xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + bpSuppVol->AddNode(insHeadVol, 4, new TGeoCombiTrans(-xpos, ypos, -zpos, new TGeoRotation("", 0, 90, 0))); + + // Finally return the beam pipe support volume + return bpSuppVol; +} + +TGeoCompositeShape* V3Cage::createBPSuppLowerCollar() +{ + // + // Creates the lower collar which actually supports the Beam Pipe + // (ALIITSUP1056) + // + // Input: + // + // Output: + // + // Return: + // The lower collar as a TGeoCompositeShape + // + // Created: 06 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 12; + Double_t xv[nv], yv[nv], xy8[16]; + Double_t zlen; + Double_t xpos, ypos; + + // The lateral bracket: a Xtru + Double_t totlen = (sBPSuppLowerCollarLen - sBPSuppCollarIntD) / 2; + Double_t xtail = (sBPSuppLowCollHolDist - sBPSuppCollarIntD) / 2; + Double_t taillen = sBPSuppLowerCollarTlX - sBPSuppCollarIntD / 2; + + xv[0] = 0; + yv[0] = -sBPSuppCollarBeamHei / 2; + xv[1] = totlen - xtail; + yv[1] = yv[0]; + xv[2] = totlen - taillen; + yv[2] = -sBPSuppLowCollTailHei / 2; + xv[3] = totlen; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = sBPSuppLowCollTailHei / 2; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[1]; + yv[6] = -yv[1]; + xv[7] = xv[0]; + yv[7] = yv[6]; + xv[8] = xv[7]; + yv[8] = sBPSuppBracketInHei / 2; + xv[9] = sBPSuppBracketInLen; + yv[9] = yv[8]; + xv[10] = xv[9]; + yv[10] = -yv[9]; + xv[11] = xv[0]; + yv[11] = yv[10]; + + zlen = sBPSuppBracketWidth / 2; + TGeoXtru* brktlat = new TGeoXtru(2); + brktlat->DefinePolygon(nv, xv, yv); + brktlat->DefineSection(0, -zlen); + brktlat->DefineSection(1, zlen); + brktlat->SetName("latBrackBody"); + + // The central hole in lateral bracket: a Tube + zlen = sBPSuppBracketWidth / 2 + 0.001; + TGeoTube* brktcenthole = new TGeoTube(0, sBPSuppBrktCentHoleD / 2, zlen); + brktcenthole->SetName("latBrackCentHole"); + + xpos = totlen - xtail; + TGeoTranslation* brktcenthmat = new TGeoTranslation(xpos, 0, 0); + brktcenthmat->SetName("latCentHoleMat"); + brktcenthmat->RegisterYourself(); + + // The lateral hole in lateral bracket: an Arb8 + // (array of vertices is in the form (x0, y0, x1, y1, ..., x7, y7) ) + xy8[0] = 0; + xy8[1] = 0; + xy8[2] = -sBPSuppBrktLatHoleW; + xy8[3] = -sBPSuppBrktLatHoleH / 2; + xy8[4] = xy8[2]; + xy8[5] = -xy8[3]; + xy8[6] = xy8[0]; + xy8[7] = xy8[1]; + for (Int_t i = 0; i < 8; i++) { // The opposite face + xy8[8 + i] = xy8[i]; + } + TGeoArb8* brktlathole = new TGeoArb8(zlen, xy8); + brktlathole->SetName("latBrackLatHole"); + + xpos = totlen - taillen; + TGeoTranslation* brktlathmat = new TGeoTranslation(xpos, 0, 0); + brktlathmat->SetName("latLatHoleMat"); + brktlathmat->RegisterYourself(); + + // The lateral bracket: a CompositeShape + TGeoCompositeShape* latbrkt = new TGeoCompositeShape("latBrackBody-latBrackCentHole:latCentHoleMat-latBrackLatHole:latLatHoleMat"); + latbrkt->SetName("lateralBracket"); + + // The lateral bracket matrices + xpos = sBPSuppLowerCollarLen / 2; + TGeoTranslation* latmat1 = new TGeoTranslation(-xpos, 0, 0); + latmat1->SetName("latBrackMat1"); + latmat1->RegisterYourself(); + + TGeoCombiTrans* latmat2 = new TGeoCombiTrans(xpos, 0, 0, new TGeoRotation("", 90, 180, -90)); + latmat2->SetName("latBrackMat2"); + latmat2->RegisterYourself(); + + // The collar: a TubeSeg + TGeoTubeSeg* collar = new TGeoTubeSeg(0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppCollarExtD, 0.5 * sBPSuppBracketWidth, 180, 360); + collar->SetName("lowerCollar"); + + ypos = brktlat->GetY(4); // The upper face of the tail + TGeoTranslation* collmat = new TGeoTranslation(0, ypos, 0); + collmat->SetName("lowerCollMat"); + collmat->RegisterYourself(); + + // Finally create and return the lower collar + // (the origin of its reference system is at its center) + TGeoCompositeShape* collarShape = new TGeoCompositeShape("lowerCollar:lowerCollMat+lateralBracket:latBrackMat1+lateralBracket:latBrackMat2"); + + return collarShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppUpperCollar() +{ + // + // Creates the upper collar of the Beam Pipe Support (ALIITSUP0823) + // + // Input: + // + // Output: + // + // Return: + // The upper collar as a TGeoCompositeShape + // + // Created: 07 Jun 2023 Mario Sitta + // + + // Local variables + Double_t xlen; + Double_t xpos, ypos; + + // The lateral plate: a BBox + xlen = (sBPSuppUpperCollarLen - sBPSuppCollarIntD) / 2; + TGeoBBox* plate = new TGeoBBox(0.5 * xlen, 0.5 * sBPSuppUpperCollarHei, 0.5 * sBPSuppBracketWidth); + plate->SetName("lateralPlate"); + + xpos = sBPSuppUpperCollarLen / 2 - plate->GetDX(); + ypos = plate->GetDY(); + TGeoTranslation* latplmat1 = new TGeoTranslation(xpos, ypos, 0); + latplmat1->SetName("lateralPlateMat1"); + latplmat1->RegisterYourself(); + + TGeoTranslation* latplmat2 = new TGeoTranslation(-xpos, ypos, 0); + latplmat2->SetName("lateralPlateMat2"); + latplmat2->RegisterYourself(); + + // The collar: a TubeSeg + TGeoTubeSeg* collar = new TGeoTubeSeg(0.5 * sBPSuppCollarIntD, 0.5 * sBPSuppCollarExtD, 0.5 * sBPSuppBracketWidth, 0, 180); + collar->SetName("upperCollar"); + + // Finally create and return the upper collar + // (the origin of its reference system is at its center) + TGeoCompositeShape* collarShape = new TGeoCompositeShape("upperCollar+lateralPlate:lateralPlateMat1+lateralPlate:lateralPlateMat2"); + + return collarShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppCollarBeam() +{ + // + // Creates the collar beam (i.e. the lateral support bar) of the + // Beam Pipe Support (ALIITSUP1057) + // + // Input: + // + // Output: + // + // Return: + // The collar beam as a TGeoCompositeShape + // + // Created: 03 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, xwid, ylen, zlen; + Double_t xpos; + + // The central part: a Xtru + xlen = (sBPSuppCollarBeamLen - 2 * sBPSuppBracketInLen) / 2; + xwid = (sBPSuppCollarBeamWid - sBPSuppBracketWidth) / 2; + xv[0] = -xlen; + yv[0] = -sBPSuppBracketWidth / 2; + xv[1] = xv[0] + xwid; + yv[1] = -sBPSuppCollarBeamWid / 2; + xv[2] = -xv[1]; + yv[2] = yv[1]; + xv[3] = -xv[0]; + yv[3] = yv[0]; + for (Int_t i = 0; i < 4; i++) { // Reflect the lower half to the upper half + xv[4 + i] = xv[3 - i]; + yv[4 + i] = -yv[3 - i]; + } + + zlen = sBPSuppCollarBeamHei / 2; + TGeoXtru* colcent = new TGeoXtru(2); + colcent->SetName("collarCentral"); + colcent->DefinePolygon(nv, xv, yv); + colcent->DefineSection(0, -zlen); + colcent->DefineSection(1, zlen); + + // Each bracket insert: a BBox + xlen = sBPSuppBracketInLen / 2; + ylen = sBPSuppBracketWidth / 2; + zlen = sBPSuppBracketInHei / 2; + TGeoBBox* colins = new TGeoBBox("collarInsert", xlen, ylen, zlen); + + xpos = colcent->GetX(0) - colins->GetDX(); + TGeoTranslation* insmat1 = new TGeoTranslation(-xpos, 0, 0); + insmat1->SetName("colInsMat1"); + insmat1->RegisterYourself(); + + TGeoTranslation* insmat2 = new TGeoTranslation(xpos, 0, 0); + insmat2->SetName("colInsMat2"); + insmat2->RegisterYourself(); + + // Finally create and return the collar beam + // (the origin of its reference system is at its center) + TGeoCompositeShape* beamShape = new TGeoCompositeShape("collarCentral+collarInsert:colInsMat1+collarInsert:colInsMat2"); + + return beamShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppBracket() +{ + // + // Creates the lateral Titanium bracket of the Beam Pipe Support + // (ALIITSUP1058) + // + // Input: + // + // Output: + // + // Return: + // The bracket as a TGeoCompositeShape + // + // Created: 04 Jun 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 12; + Double_t xv[nv], yv[nv]; + Double_t zlen; + Double_t xpos; + + // The main body: a Xtru + xv[0] = 0; + yv[0] = -sBPSuppCollarBeamHei / 2; + xv[1] = sBPSuppBracketTotLen - sBPSuppBrktCentHoleX; + yv[1] = yv[0]; + xv[2] = sBPSuppBracketTotLen - sBPSuppBracketTailLen; + yv[2] = 0; + xv[3] = sBPSuppBracketTotLen; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = sBPSuppBracketTailHei; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[1]; + yv[6] = -yv[1]; + xv[7] = xv[0]; + yv[7] = yv[6]; + xv[8] = xv[7]; + yv[8] = sBPSuppBracketInHei / 2; + xv[9] = sBPSuppBracketInLen; + yv[9] = yv[8]; + xv[10] = xv[9]; + yv[10] = -yv[9]; + xv[11] = xv[0]; + yv[11] = yv[10]; + + zlen = sBPSuppBracketWidth / 2; + TGeoXtru* brktbody = new TGeoXtru(2); + brktbody->DefinePolygon(nv, xv, yv); + brktbody->DefineSection(0, -zlen); + brktbody->DefineSection(1, zlen); + brktbody->SetName("bracketBody"); + + // The central hole: a Tube + zlen = sBPSuppBracketWidth / 2 + 0.001; + TGeoTube* brktcenthole = new TGeoTube(0, sBPSuppBrktCentHoleD / 2, zlen); + brktcenthole->SetName("bracketCentHole"); + + xpos = sBPSuppBracketTotLen - sBPSuppBrktCentHoleX; + TGeoTranslation* brktcenthmat = new TGeoTranslation(xpos, -sBPSuppBrktHolesY, 0); + brktcenthmat->SetName("bracketCentHMat"); + brktcenthmat->RegisterYourself(); + + // The lateral hole: a Tube + TGeoTube* brktlathole = new TGeoTube(0, sBPSuppBrktLatHoleD / 2, zlen); + brktlathole->SetName("bracketLatHole"); + + xpos = sBPSuppBracketTotLen - sBPSuppBrktLatHoleX; + TGeoTranslation* brktlathmat = new TGeoTranslation(xpos, sBPSuppBrktHolesY, 0); + brktlathmat->SetName("bracketLatHMat"); + brktlathmat->RegisterYourself(); + + // Finally create and return the bracket + // (the origin of its reference system is opposite to its tail) + TGeoCompositeShape* bracketShape = new TGeoCompositeShape("bracketBody-bracketCentHole:bracketCentHMat-bracketLatHole:bracketLatHMat"); + + return bracketShape; +} + +TGeoCompositeShape* V3Cage::createBPSuppClamp() +{ + // + // Creates the lateral Titanium clamp holding the Beam Pipe Support + // to the ITS Cage (ALIITSUP1060) + // + // Input: + // + // Output: + // + // Return: + // The clamp as a TGeoCompositeShape + // + // Created: 08 Jun 2023 Mario Sitta + // + + // Local variables + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + + // The vertical wall: a BBox + xlen = sBPSuppClampLatThick / 2; + ylen = sBPSuppClampTotHei / 2; + zlen = sBPSuppClampTotLen / 2; + TGeoBBox* clampwall = new TGeoBBox(xlen, ylen, zlen); + clampwall->SetName("clampWall"); + + xpos = -clampwall->GetDX(); + ypos = clampwall->GetDY() - sBPSuppClampShelfHei; + TGeoTranslation* clampwallmat = new TGeoTranslation(xpos, ypos, 0); + clampwallmat->SetName("clampWallMat"); + clampwallmat->RegisterYourself(); + + // The horizontal shelf: a BBox + xlen = (sBPSuppClampTotWid - sBPSuppClampLatThick) / 2; + ylen = sBPSuppClampShelfHei / 2; + zlen = sBPSuppClampShelfLen / 2; + TGeoBBox* clampshelf = new TGeoBBox(xlen, ylen, zlen); + clampshelf->SetName("clampShelf"); + + xpos = clampshelf->GetDX(); + ypos = -clampshelf->GetDY(); + TGeoTranslation* clampshelfmat = new TGeoTranslation(xpos, ypos, 0); + clampshelfmat->SetName("clampShelfMat"); + clampshelfmat->RegisterYourself(); + + // Finally create and return the clamp + // (the origin of its reference system is at the conjunction + // of the vertical wall with the horizontal shelf) + TGeoCompositeShape* clampShape = new TGeoCompositeShape("clampWall:clampWallMat+clampShelf:clampShelfMat"); + + return clampShape; +} + +TGeoVolume* V3Cage::createCageClosingCross(const TGeoManager* mgr) +{ + // + // Creates the Cage Closing Cross (from drawings ALIITSUP0242) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // The closing cross as a TGeoVolume + // + // Created: 29 May 2023 Mario Sitta + // + + // Local variables + const Int_t nv = 8; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos; + + TString compoShape; + + // A single vertical post: a Xtru + xv[0] = 0.; + yv[0] = 0.; + xv[1] = (sCageCrossXWidthTot - sCageCrossXWidthInt) / 2; + yv[1] = yv[0]; + xv[2] = xv[1]; + yv[2] = (sCageCrossYHeightTot - sCageCrossYHeightInt) / 2; + xv[3] = (sCageCrossXWidthExt - sCageCrossXWidthInt) / 2; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = yv[3] + sCageCrossYHeightInt; + xv[5] = xv[2]; + yv[5] = yv[4]; + xv[6] = xv[5]; + yv[6] = sCageCrossYHeightTot; + xv[7] = xv[0]; + yv[7] = yv[6]; + + zlen = sCageCrossZLength / 2; + + TGeoXtru* vpost = new TGeoXtru(2); + vpost->SetName("crossvertpost"); + vpost->DefinePolygon(nv, xv, yv); + vpost->DefineSection(0, -zlen); + vpost->DefineSection(1, zlen); + + // The vertical post matrices + xpos = sCageCrossXWidthInt / 2; + TGeoTranslation* vpostmat1 = new TGeoTranslation("vertpostmat1", xpos, 0, 0); + vpostmat1->RegisterYourself(); + + TGeoCombiTrans* vpostmat2 = new TGeoCombiTrans(-xpos, 0, 0, new TGeoRotation("", 90, 180, -90)); + vpostmat2->SetName("vertpostmat2"); + vpostmat2->RegisterYourself(); + + compoShape = Form("crossvertpost:vertpostmat1+crossvertpost:vertpostmat2"); + + // A single oblique post: a BBox + Double_t leg = vpost->GetY(4); + xlen = TMath::Sqrt(sCageCrossXWidthInt * sCageCrossXWidthInt + leg * leg) / 2; + ylen = sCageCrossBarThick / 2; + TGeoBBox* xpost = new TGeoBBox("crossoblqpost", xlen, ylen, zlen); + + // The oblique post matrices + Double_t phi = sCageCrossBarPhi / 2; + ypos = sCageCrossYHeightTot - sCageCrossYMid; + + TGeoCombiTrans* xpostmat1 = new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", phi, 0, 0)); + xpostmat1->SetName("oblqpostmat1"); + xpostmat1->RegisterYourself(); + + TGeoCombiTrans* xpostmat2 = new TGeoCombiTrans(0, ypos, 0, new TGeoRotation("", -phi, 0, 0)); + xpostmat2->SetName("oblqpostmat2"); + xpostmat2->RegisterYourself(); + + compoShape += Form("+crossoblqpost:oblqpostmat1+crossoblqpost:oblqpostmat2"); + + // The actual closing cross shape: a CompositeShape + TGeoCompositeShape* closCrossSh = new TGeoCompositeShape(compoShape); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* closCrossVol = new TGeoVolume("CageClosingCross", closCrossSh, medAl); + closCrossVol->SetFillColor(kGray); + closCrossVol->SetLineColor(kGray); + + // Finally return the closing cross volume + return closCrossVol; +} + +void V3Cage::createAndPlaceMFTRailsInsideCage(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Creates the MFT Rails located inside the Cage and place them + // + // Input: + // motmat : the material of the mother volume (for the container box) + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 10 May 2025 Mario Sitta + // Updated: 20 May 2025 Mario Sitta Hinges added + // + + // Local variables + Double_t rdist, rpos, xpos, yposup, yposdw, zpos, alpha; + Double_t xbox, ybox; + + // Create a pair of rails (a BBox container is returned) + TGeoVolume* cageMFTRails = createMFTRailsPair(mother->GetMedium(), mgr); + + // Create hinge holding a pair of rails + TGeoVolume* cageMFTRailsHinge = createMFTRailsHinge(mgr); + + // Now compute the radial distance and the XY position of the box + xbox = (static_cast(cageMFTRails->GetShape()))->GetDX(); + ybox = (static_cast(cageMFTRails->GetShape()))->GetDY(); + + rdist = TMath::Sqrt(sCageCoverRint * sCageCoverRint - xbox * xbox); + rpos = rdist - ybox; + + // Finally place the four pairs of rails inside the mother volume + xpos = rpos * TMath::Sin(sCageEndCapCableCutPhi * TMath::DegToRad()); + yposup = rpos * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) + sCageYInBarrel; + yposdw = rpos * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) - sCageYInBarrel; + zpos = sCageMFTRailZPos; + + alpha = -sCageEndCapCableCutPhi + 180; + mother->AddNode(cageMFTRails, 1, new TGeoCombiTrans(xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + alpha = sCageEndCapCableCutPhi + 180; + mother->AddNode(cageMFTRails, 2, new TGeoCombiTrans(-xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + + alpha = sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRails, 3, new TGeoCombiTrans(xpos, -yposdw, zpos, new TGeoRotation("", alpha, 0, 0))); + alpha = -sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRails, 4, new TGeoCombiTrans(-xpos, -yposdw, zpos, new TGeoRotation("", alpha, 0, 0))); + + // And the hinges too + xpos = rdist * TMath::Sin(sCageEndCapCableCutPhi * TMath::DegToRad()); + yposup = rdist * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) + sCageYInBarrel; + yposdw = rdist * TMath::Cos(sCageEndCapCableCutPhi * TMath::DegToRad()) - sCageYInBarrel; + zpos = (static_cast(cageMFTRails->GetShape()))->GetDZ() + sCageMFTRailZPos; + + alpha = sCageEndCapCableCutPhi; + mother->AddNode(cageMFTRailsHinge, 1, new TGeoCombiTrans(xpos, yposup, zpos, new TGeoRotation("", -alpha, 0, 0))); + mother->AddNode(cageMFTRailsHinge, 2, new TGeoCombiTrans(-xpos, yposup, zpos, new TGeoRotation("", alpha, 0, 0))); + + mother->AddNode(cageMFTRailsHinge, 3, new TGeoCombiTrans(xpos, -yposdw, zpos, new TGeoRotation("", 180 + alpha, 0, 0))); + mother->AddNode(cageMFTRailsHinge, 4, new TGeoCombiTrans(-xpos, -yposdw, zpos, new TGeoRotation("", 180 - alpha, 0, 0))); + + zpos = (static_cast(cageMFTRails->GetShape()))->GetDZ() - sCageMFTRailZPos; + mother->AddNode(cageMFTRailsHinge, 5, new TGeoCombiTrans(xpos, yposup, -zpos, new TGeoRotation("", 90, 180, -90 + alpha))); // On Z<0 apply 180deg rotation on Y axis + mother->AddNode(cageMFTRailsHinge, 6, new TGeoCombiTrans(-xpos, yposup, -zpos, new TGeoRotation("", 90, 180, -90 - alpha))); + + mother->AddNode(cageMFTRailsHinge, 7, new TGeoCombiTrans(xpos, -yposdw, -zpos, new TGeoRotation("", 90, 180, 90 - alpha))); + mother->AddNode(cageMFTRailsHinge, 8, new TGeoCombiTrans(-xpos, -yposdw, -zpos, new TGeoRotation("", 90, 180, 90 + alpha))); + + return; +} + +TGeoVolume* V3Cage::createMFTRailsPair(const TGeoMedium* motmed, const TGeoManager* mgr) +{ + // + // Creates a pair of MFT Rails located inside the Cage (from drawings + // ALI-MFT-DF-0057 and elements therein) + // A box containing a pair of rails is returned (a physical box + // is preferred over an Assembly for better performance) + // + // Input: + // motmed : the medium of the mother volume (for the container box) + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // A rail pair as a TGeoVolume + // + // Created: 10 May 2025 Mario Sitta + // + + // Local variables + const Int_t nv = 16; + Double_t xv[nv], yv[nv]; + Double_t deltah, xlen, ylen, zlen; + Double_t xpos, ypos; + + // The shape of a single rail: a Xtru + xv[0] = sCageMFTRailBaseWidth / 2; + yv[0] = 0.; + xv[1] = xv[0]; + yv[1] = sCageMFTRailTotHeight - sCageMFTRailExtHeight; + xv[2] = sCageMFTRailTotWidth / 2; + yv[2] = yv[1]; + xv[3] = xv[2]; + yv[3] = sCageMFTRailTotHeight; + xv[4] = sCageMFTRailIntWidth / 2; + yv[4] = yv[3]; + deltah = (sCageMFTRailExtHeight - sCageMFTRailIntHeight) / 2; + xv[5] = xv[4]; + yv[5] = yv[4] - deltah; + xv[6] = sCageMFTRailExtWidth / 2; + yv[6] = yv[5]; + xv[7] = xv[6]; + yv[7] = yv[6] - sCageMFTRailIntHeight; + + for (Int_t i = 8; i < nv; i++) { + xv[i] = -xv[15 - i]; + yv[i] = yv[15 - i]; + } + + zlen = sCageMFTRailZLen / 2; + + TGeoXtru* mftRailSh = new TGeoXtru(2); + mftRailSh->SetName("mftrailshape"); + mftRailSh->DefinePolygon(nv, xv, yv); + mftRailSh->DefineSection(0, -zlen); + mftRailSh->DefineSection(1, zlen); + + // The air container: a BBox + xlen = 2 * sCageMFTRailTotWidth + sCageMFTRailsXDist; + ylen = sCageMFTRailTotHeight / 2; + zlen = sCageMFTRailZLen / 2; + TGeoBBox* mftRailBoxSh = new TGeoBBox(xlen / 2, ylen, zlen); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* mftRailVol = new TGeoVolume("MFTRailInsideCage", mftRailSh, medAl); + mftRailVol->SetFillColor(kGray); + mftRailVol->SetLineColor(kGray); + + TGeoVolume* mftRailBoxVol = new TGeoVolume("MFTRailPairInsideCage", mftRailBoxSh, motmed); + + // Put the two rails inside the holding box + // (rail Y origin is on its lower face) + xpos = mftRailBoxSh->GetDX() - 0.5 * sCageMFTRailTotWidth; + ypos = mftRailBoxSh->GetDY(); + mftRailBoxVol->AddNode(mftRailVol, 1, new TGeoTranslation(xpos, -ypos, 0)); + mftRailBoxVol->AddNode(mftRailVol, 2, new TGeoTranslation(-xpos, -ypos, 0)); + + // Finally return the rails volume + return mftRailBoxVol; +} + +TGeoVolume* V3Cage::createMFTRailsHinge(const TGeoManager* mgr) +{ + // + // Creates a hinge holding a pair of MFT Rails to the Cage (from drawing + // ALIMFT-0042 and elements inside CAD files) + // + // Input: + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // A rail hinge as a TGeoVolume + // + // Created: 19 May 2025 Mario Sitta + // + + // Local variables + const Int_t nv = 6; + Double_t xv[nv], yv[nv]; + Double_t xlen, ylen, zlen; + Double_t xpos, ypos, zpos; + + TString compoShape; + + // The main body: a Xtru + xv[0] = sCageMFTHingeTotWid / 2; + yv[0] = 0; + xv[1] = xv[0]; + yv[1] = sCageMFTHingeIntHei; + xv[2] = sCageMFTHingeIntWid / 2; + yv[2] = sCageMFTHingeHeight; + + for (Int_t i = 3; i < nv; i++) { + xv[i] = -xv[5 - i]; + yv[i] = yv[5 - i]; + } + + zlen = sCageMFTHingeIntLen / 2; + + TGeoXtru* mftHingeBodySh = new TGeoXtru(2); + mftHingeBodySh->SetName("mfthingebodyshape"); + mftHingeBodySh->DefinePolygon(nv, xv, yv); + mftHingeBodySh->DefineSection(0, -zlen); + mftHingeBodySh->DefineSection(1, zlen); + + // The bulge: a BBox + xlen = sCageMFTHingeBulgeWid / 2; + ylen = sCageMFTHingeBulgeHei / 2; + zlen = (sCageMFTHingeTotLen - sCageMFTHingeIntLen) / 2; + TGeoBBox* mftHingeBulgeSh = new TGeoBBox("mfthingebulgeshape", xlen, ylen, zlen); + + // The actual hinge: a CompositeShape + xpos = mftHingeBodySh->GetX(0) - (sCageMFTHingeBulgePos + mftHingeBulgeSh->GetDX()); + ypos = mftHingeBodySh->GetY(2) - mftHingeBulgeSh->GetDY(); + zpos = mftHingeBodySh->GetZ(1) + mftHingeBulgeSh->GetDZ(); + + TGeoTranslation* bulgpos1 = new TGeoTranslation(xpos, ypos, zpos); + bulgpos1->SetName("bulge1pos"); + bulgpos1->RegisterYourself(); + + TGeoTranslation* bulgpos2 = new TGeoTranslation(-xpos, ypos, zpos); + bulgpos2->SetName("bulge2pos"); + bulgpos2->RegisterYourself(); + + compoShape = Form("mfthingebodyshape+mfthingebulgeshape:bulge1pos+mfthingebulgeshape:bulge2pos"); + + TGeoCompositeShape* mftRailHingeSh = new TGeoCompositeShape(compoShape); + + // We have the shape: now create the real volume + TGeoMedium* medAl = mgr->GetMedium(Form("%s_ALUMINUM$", GetDetName())); + + TGeoVolume* mftRailHingeVol = new TGeoVolume("MFTRailHingeInsideCage", mftRailHingeSh, medAl); + mftRailHingeVol->SetFillColor(kGreen); + mftRailHingeVol->SetLineColor(kGreen); + + // Finally return the hinge volume + return mftRailHingeVol; +} diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx index 33a1bedec74eb..e930aa23de030 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Layer.cxx @@ -61,9 +61,15 @@ const Double_t V3Layer::sIBFPCAlAnodeWidth1 = 13.0 * sMm; const Double_t V3Layer::sIBFPCAlAnodeWidth2 = 14.7 * sMm; const Double_t V3Layer::sIBFlexCableKapThick = 75.0 * sMicron; const Double_t V3Layer::sIBFlexCablePolyThick = 20.0 * sMicron; -const Double_t V3Layer::sIBFlexCapacitorXWid = 0.2 * sMm; -const Double_t V3Layer::sIBFlexCapacitorYHi = 0.2 * sMm; -const Double_t V3Layer::sIBFlexCapacitorZLen = 0.4 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1XWid = 0.5 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1YHi = 0.5 * sMm; +const Double_t V3Layer::sIBFlexCapacitor1ZLen = 1.0 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22XWid = 0.7 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22YHi = 0.6 * sMm; +const Double_t V3Layer::sIBFlexCapacitor22ZLen = 1.1 * sMm; +const Double_t V3Layer::sIBFlexResistorXWid = 0.2 * sMm; +const Double_t V3Layer::sIBFlexResistorYHi = 0.2 * sMm; +const Double_t V3Layer::sIBFlexResistorZLen = 0.4 * sMm; const Double_t V3Layer::sIBColdPlateWidth = 15.4 * sMm; const Double_t V3Layer::sIBColdPlateZLen = 290.0 * sMm; const Double_t V3Layer::sIBGlueThick = 50.0 * sMicron; @@ -599,8 +605,11 @@ TGeoVolume* V3Layer::createModuleInnerB(const Double_t xchip, const Double_t zch // the module as a TGeoVolume // // Updated: 03 Apr 2021 + // Updated: 03 Nov 2025 Change volume from BBox to Xtru to avoid fake overlaps Double_t xtot, ytot, ztot; + Double_t ymid, shrinkFactor = 0.73; + Double_t xv[5], yv[5]; Double_t xpos, ypos, zpos; const Int_t nameLen = 30; char volumeName[nameLen]; @@ -619,9 +628,25 @@ TGeoVolume* V3Layer::createModuleInnerB(const Double_t xchip, const Double_t zch Double_t ygnd = (static_cast(aluGndCableVol->GetShape()))->GetDY(); Double_t yano = (static_cast(aluAnodeCableVol->GetShape()))->GetDY(); - ytot = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitorYHi / 2; + ytot = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano + sIBFlexCapacitor22YHi / 2; + ymid = sIBGlueThick / 2 + ygnd + sIBFlexCableKapThick / 2 + yano; - TGeoBBox* module = new TGeoBBox(xtot, ytot, ztot); + xv[0] = xtot; + yv[0] = -ytot; + xv[1] = xv[0]; + yv[1] = yv[0] + 6 * ymid; + xv[2] = xtot * shrinkFactor; + yv[2] = ytot; + xv[3] = -xtot; + yv[3] = yv[2]; + xv[4] = xv[3]; + yv[4] = yv[0]; + + TGeoXtru* module = new TGeoXtru(2); + module->DefinePolygon(6, xv, yv); + module->DefinePolygon(5, xv, yv); + module->DefineSection(0, -ztot); + module->DefineSection(1, ztot); // Now the volumes TGeoMedium* medAir = mgr->GetMedium(Form("%s_AIR$", GetDetName())); @@ -674,6 +699,7 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz // // Created: 13 Feb 2018 Mario Sitta // Updated: 03 Apr 2019 Mario Sitta Fix positions (180' rotation) + // Updated: 31 Oct 2025 Mario Sitta Fix dimensions and weight // // Position of the various capacitors (A.Junique private communication @@ -705,63 +731,72 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz Double_t xpos, ypos, zpos; Int_t nCapacitors; - TGeoVolume *capacitor, *resistor; + TGeoVolume *capacitorSmall, *capacitorLarge, *resistor; - // Check whether we already have the volume, otherwise create it - // (so as to avoid creating multiple copies of the very same volume + // Check whether we already have the volumes, otherwise create them + // (so as to avoid creating multiple copies of the very same volumes // for each layer) - capacitor = mgr->GetVolume("IBFPCCapacitor"); + // The "small" capacitor is the 1 uF substrate capacitor + // The "large" capacitor is the 22 uF analog/digital PS capacitor + capacitorSmall = mgr->GetVolume("IBFPCCapacitorSmall"); - if (!capacitor) { - TGeoBBox* capsh = new TGeoBBox(sIBFlexCapacitorXWid / 2, sIBFlexCapacitorYHi / 2, sIBFlexCapacitorZLen / 2); + if (!capacitorSmall) { + TGeoBBox* capSmsh = new TGeoBBox(sIBFlexCapacitor1XWid / 2, sIBFlexCapacitor1YHi / 2, sIBFlexCapacitor1ZLen / 2); + TGeoBBox* capLgsh = new TGeoBBox(sIBFlexCapacitor22XWid / 2, sIBFlexCapacitor22YHi / 2, sIBFlexCapacitor22ZLen / 2); TGeoMedium* medCeramic = mgr->GetMedium(Form("%s_CERAMIC$", GetDetName())); - capacitor = new TGeoVolume("IBFPCCapacitor", capsh, medCeramic); - capacitor->SetLineColor(kBlack); - capacitor->SetFillColor(kBlack); + capacitorSmall = new TGeoVolume("IBFPCCapacitorSmall", capSmsh, medCeramic); + capacitorSmall->SetLineColor(kBlack); + capacitorSmall->SetFillColor(kBlack); + + capacitorLarge = new TGeoVolume("IBFPCCapacitorLarge", capLgsh, medCeramic); + capacitorLarge->SetLineColor(kBlack); + capacitorLarge->SetFillColor(kBlack); - TGeoBBox* ressh = new TGeoBBox(sIBFlexCapacitorXWid / 2, // Resistors have - sIBFlexCapacitorYHi / 2, // the same dim's - sIBFlexCapacitorZLen / 2); // as capacitors + TGeoBBox* ressh = new TGeoBBox(sIBFlexResistorXWid / 2, + sIBFlexResistorYHi / 2, + sIBFlexResistorZLen / 2); resistor = new TGeoVolume("IBFPCResistor", ressh, medCeramic); resistor->SetLineColor(kBlack); resistor->SetFillColor(kBlack); } else { // Volumes already defined, get them + capacitorLarge = mgr->GetVolume("IBFPCCapacitorLarge"); resistor = mgr->GetVolume("IBFPCResistor"); } // Place all the capacitors (they are really a lot...) - ypos = yzero + sIBFlexCapacitorYHi / 2; + ypos = yzero + sIBFlexCapacitor22YHi / 2; xpos = xGroup1A; for (Int_t j = 0; j < sIBChipsPerRow; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[0]; - modvol->AddNode(capacitor, 2 * j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, 2 * j + 1, new TGeoTranslation(-xpos, ypos, -zpos)); zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1A[1]; - modvol->AddNode(capacitor, 2 * j + 2, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, 2 * j + 2, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors = 2 * sIBChipsPerRow; xpos = xGroup1B; for (Int_t j = 0; j < sIBChipsPerRow; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup1B; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; + ypos = yzero + sIBFlexCapacitor1YHi / 2; xpos = xGroup2; // We have only 8 in these group, missing the central one for (Int_t j = 0; j < sIBChipsPerRow - 1; j++) { zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup2; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += (sIBChipsPerRow - 1); xpos = xGroup3; zpos = zGroup3; - modvol->AddNode(capacitor, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); nCapacitors++; for (Int_t j = 0; j < sIBChipsPerRow; j++) { @@ -771,10 +806,11 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz xpos = xGroup4[0]; } zpos = -mIBModuleZLength / 2 + j * (2 * zchip + sIBChipZGap) + zchip + zGroup4[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorSmall, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += sIBChipsPerRow; + ypos = yzero + sIBFlexCapacitor22YHi / 2; for (Int_t j = 0; j < nGroup5A; j++) { if (j == 0) { xpos = xGroup5A[0]; @@ -782,14 +818,14 @@ void V3Layer::createIBCapacitors(TGeoVolume* modvol, Double_t zchip, Double_t yz xpos = xGroup5A[1]; } zpos = zGroup5A[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } nCapacitors += nGroup5A; xpos = xGroup5B; for (Int_t j = 0; j < nGroup5B; j++) { zpos = zGroup5B[j]; - modvol->AddNode(capacitor, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); + modvol->AddNode(capacitorLarge, j + 1 + nCapacitors, new TGeoTranslation(-xpos, ypos, -zpos)); } // Place the resistors @@ -1061,7 +1097,7 @@ TGeoVolume* V3Layer::createStaveModelInnerB4(const TGeoManager* mgr) yv[1] = layerHeight + sIBSideVertexHeight + topfil->GetDZ(); ; xv[2] = sIBEndSupportXUp / 2; - yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta); // theta is neg + yv[2] = sIBStaveHeight + sIBTopFilamentSide / sinD(-theta) - 0.01; // theta is neg for (Int_t i = 0; i < 3; i++) { xv[3 + i] = -xv[2 - i]; yv[3 + i] = yv[2 - i]; diff --git a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx index 879d911cbf24e..03fae3e1f984c 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Services.cxx @@ -27,7 +27,7 @@ #include // for TGeoPcon #include // for TGeoManager, gGeoManager #include // for TGeoCombiTrans, TGeoRotation, etc -//#include // for TGeoTrd1 +// #include // for TGeoTrd1 #include // for TGeoTube, TGeoTubeSeg #include // for TGeoVolume, TGeoVolumeAssembly #include // for TGeoXtru @@ -2908,6 +2908,216 @@ TGeoCompositeShape* V3Services::ibConvWireOutSupport() return supportShape; } +void V3Services::createAllITSServices(TGeoVolume* mother, const TGeoManager* mgr) +{ + // + // Steering method to creates the ITS services: tubes, cables and the like + // + // Input: + // motherVolume : the volume hosting the supports + // mgr : the GeoManager (used only to get the proper material) + // + // Output: + // + // Return: + // + // Created: 12 Apr 2023 Mario Sitta First version (very roughly) + // + + // The present version is very rough: all services are approximated + // as cylinders/cones with a proper thickness to guess the total + // material budget (original code by A.Morsch) + // (some figures are hardcoded) + + // Inner Barrel + static const Double_t sIBServicesZIn = 35.0 * sCm; + static const Double_t sIBServicesZMid = 44.0 * sCm; + static const Double_t sIBServicesZOut = 253.45 * sCm; + + static const Double_t sIBServicesR1max = 13.3 * sCm; + static const Double_t sIBServicesR2max = 43.53 * sCm; + + static const Double_t sIBServicesCarbonThick = 0.2 * sCm; + static const Double_t sIBServicesCopperThick = 0.018 * sCm; + static const Double_t sIBServicesPolymerThick = 0.42 * sCm; + + // Outer Barrel + static const Double_t sOBServicesZIn = 83.0 * sCm; + static const Double_t sOBServicesZOut = 248.00 * sCm; + + static const Double_t sOBServicesRmax = 47.2 * sCm; + + static const Double_t sOBServicesTotalThick = 4.42 * sCm; + static const Double_t sOBServicesCarbonThick = 0.25 * sCm; + static const Double_t sOBServicesCopperThick = 0.23 * sCm; + static const Double_t sOBServicesPolymerThick = 3.8 * sCm; + + static const Double_t sBeamPipeSupportRHole = 28.2 * sMm; + static const Double_t sBeamPipeSupportZPos = 182.1 * sCm; + static const Double_t sBeamPipeSupportXIB = 32.5 * sCm; + + // Local variables + Double_t rmin, rmax, zlen, xpos, zpos; + + // The hole where the Beam Pipe Support should pass + zlen = 0.9 * sOBServicesTotalThick; + TGeoTube* obBPSuppHole = new TGeoTube(0, sBeamPipeSupportRHole, zlen); + obBPSuppHole->SetName("bpSuppHole"); + + // The Outer Barrel services as CompositeShape's (to avoid fake overlaps + // with the Beam Pipe Support on Side A) with a Pcon as basic shape + // Carbon + TGeoPcon* ibCarbonCon = new TGeoPcon(0., 360., 3); + rmin = sIBServicesR1max - (sIBServicesCarbonThick + 1.8 * 0.462); + ibCarbonCon->DefineSection(0, sIBServicesZIn, rmin, sIBServicesR1max); + ibCarbonCon->DefineSection(1, sIBServicesZMid, rmin, sIBServicesR1max); + rmin = sIBServicesR2max - (sIBServicesCarbonThick + 0.462 / 1.8); + ibCarbonCon->DefineSection(2, sIBServicesZOut, rmin, sIBServicesR2max); + ibCarbonCon->SetName("ibCarbonCon"); + + xpos = sBeamPipeSupportXIB; + zpos = sBeamPipeSupportZPos; + TGeoCombiTrans* ibHoleMatL = new TGeoCombiTrans(xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + ibHoleMatL->SetName("ibHoleMatL"); + ibHoleMatL->RegisterYourself(); + + TGeoCombiTrans* ibHoleMatR = new TGeoCombiTrans(-xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + ibHoleMatR->SetName("ibHoleMatR"); + ibHoleMatR->RegisterYourself(); + + TGeoCompositeShape* ibCarbonSh = new TGeoCompositeShape("ibCarbonCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Copper + TGeoPcon* ibCopperCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibCarbonCon->GetRmax(0) - sIBServicesCarbonThick; + ibCopperCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibCopperCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibCarbonCon->GetRmin(2); + rmax = ibCarbonCon->GetRmax(2) - sIBServicesCarbonThick; + ibCopperCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibCopperCon->SetName("ibCopperCon"); + + TGeoCompositeShape* ibCopperSh = new TGeoCompositeShape("ibCopperCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Polymer + TGeoPcon* ibPolyCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibCopperCon->GetRmax(0) - sIBServicesCopperThick * 1.8; + ibPolyCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibPolyCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibCopperCon->GetRmin(2); + rmax = ibCopperCon->GetRmax(2) - sIBServicesCopperThick / 1.8; + ibPolyCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibPolyCon->SetName("ibPolyCon"); + + TGeoCompositeShape* ibPolySh = new TGeoCompositeShape("ibPolyCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // Water + TGeoPcon* ibWaterCon = new TGeoPcon(0., 360., 3); + rmin = ibCarbonCon->GetRmin(0); + rmax = ibPolyCon->GetRmax(0) - sIBServicesPolymerThick * 1.8; + ibWaterCon->DefineSection(0, sIBServicesZIn, rmin, rmax); + ibWaterCon->DefineSection(1, sIBServicesZMid, rmin, rmax); + rmin = ibPolyCon->GetRmin(2); + rmax = ibPolyCon->GetRmax(2) - sIBServicesPolymerThick / 1.8; + ibWaterCon->DefineSection(2, sIBServicesZOut, rmin, rmax); + ibWaterCon->SetName("ibWaterCon"); + + TGeoCompositeShape* ibWaterSh = new TGeoCompositeShape("ibWaterCon-bpSuppHole:ibHoleMatL-bpSuppHole:ibHoleMatR"); + + // The Outer Barrel services as CompositeShape's (to avoid fake overlaps + // with the Beam Pipe Support on Side A) with a Tube as basic shape + // Carbon + rmin = sOBServicesRmax - sOBServicesTotalThick; + zlen = sOBServicesZOut - sOBServicesZIn; + TGeoTube* obCarbonTub = new TGeoTube(rmin, sOBServicesRmax, zlen / 2); + obCarbonTub->SetName("obCarbonTub"); + + xpos = (rmin + sOBServicesRmax) / 2; + zpos = sBeamPipeSupportZPos - (sOBServicesZIn + zlen / 2); + TGeoCombiTrans* obHoleMatL = new TGeoCombiTrans(xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + obHoleMatL->SetName("obHoleMatL"); + obHoleMatL->RegisterYourself(); + + TGeoCombiTrans* obHoleMatR = new TGeoCombiTrans(-xpos, 0, zpos, new TGeoRotation("", 90, 90, -90)); + obHoleMatR->SetName("obHoleMatR"); + obHoleMatR->RegisterYourself(); + + TGeoCompositeShape* obCarbonSh = new TGeoCompositeShape("obCarbonTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Copper + rmax = sOBServicesRmax - sOBServicesCarbonThick; + TGeoTube* obCopperTub = new TGeoTube(rmin, rmax, zlen / 2); + obCopperTub->SetName("obCopperTub"); + + TGeoCompositeShape* obCopperSh = new TGeoCompositeShape("obCopperTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Polymer + rmax -= sOBServicesCopperThick; + TGeoTube* obPolyTub = new TGeoTube(rmin, rmax, zlen / 2); + obPolyTub->SetName("obPolyTub"); + + TGeoCompositeShape* obPolySh = new TGeoCompositeShape("obPolyTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // Water + rmax -= sOBServicesPolymerThick; + TGeoTube* obWaterTub = new TGeoTube(rmin, rmax, zlen / 2); + obWaterTub->SetName("obWaterTub"); + + TGeoCompositeShape* obWaterSh = new TGeoCompositeShape("obWaterTub-bpSuppHole:obHoleMatL-bpSuppHole:obHoleMatR"); + + // We have all shapes: now create the real volumes + TGeoMedium* medCarbon = mgr->GetMedium(Form("%s_C4SERVICES$", GetDetName())); + TGeoMedium* medCopper = mgr->GetMedium(Form("%s_COPPER$", GetDetName())); + TGeoMedium* medPolymer = mgr->GetMedium(Form("%s_POLY4SERVICES$", GetDetName())); + TGeoMedium* medWater = mgr->GetMedium(Form("%s_WATER$", GetDetName())); + + TGeoVolume* ibCarbonVol = new TGeoVolume("ITSServicesCarbonIB", ibCarbonSh, medCarbon); + ibCarbonVol->SetFillColor(kGray); + ibCarbonVol->SetLineColor(kGray); + + TGeoVolume* ibCopperVol = new TGeoVolume("ITSServicesCopperIB", ibCopperSh, medCopper); + ibCopperVol->SetFillColor(kRed); + ibCopperVol->SetLineColor(kRed); + + TGeoVolume* ibPolyVol = new TGeoVolume("ITSServicesPolymerIB", ibPolySh, medPolymer); + ibPolyVol->SetFillColor(kYellow); + ibPolyVol->SetLineColor(kYellow); + + TGeoVolume* ibWaterVol = new TGeoVolume("ITSServicesWaterIB", ibWaterSh, medWater); + ibWaterVol->SetFillColor(kBlue); + ibWaterVol->SetLineColor(kBlue); + + TGeoVolume* obCarbonVol = new TGeoVolume("ITSServicesCarbonOB", obCarbonSh, medCarbon); + obCarbonVol->SetFillColor(kGray); + obCarbonVol->SetLineColor(kGray); + + TGeoVolume* obCopperVol = new TGeoVolume("ITSServicesCopperOB", obCopperSh, medCopper); + obCopperVol->SetFillColor(kRed); + obCopperVol->SetLineColor(kRed); + + TGeoVolume* obPolyVol = new TGeoVolume("ITSServicesPolymerOB", obPolySh, medPolymer); + obPolyVol->SetFillColor(kYellow); + obPolyVol->SetLineColor(kYellow); + + TGeoVolume* obWaterVol = new TGeoVolume("ITSServicesWaterOB", obWaterSh, medWater); + obWaterVol->SetFillColor(kBlue); + obWaterVol->SetLineColor(kBlue); + + // Finally place all volumes + mother->AddNode(ibCarbonVol, 1, new TGeoTranslation(0., 30., 0.)); + ibCarbonVol->AddNode(ibCopperVol, 1, nullptr); + ibCopperVol->AddNode(ibPolyVol, 1, nullptr); + ibPolyVol->AddNode(ibWaterVol, 1, nullptr); + + zpos = sOBServicesZIn + zlen / 2; + mother->AddNode(obCarbonVol, 1, new TGeoTranslation(0., 30., zpos)); + obCarbonVol->AddNode(obCopperVol, 1, nullptr); + obCopperVol->AddNode(obPolyVol, 1, nullptr); + obPolyVol->AddNode(obWaterVol, 1, nullptr); +} + void V3Services::obConvWire(TGeoVolume* mother, const TGeoManager* mgr) { // diff --git a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx index 0998272f745d0..a82ad4001aff3 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/digi2raw.cxx @@ -42,7 +42,7 @@ constexpr int DefRDHVersion = o2::raw::RDHUtils::getVersion& m2r, std::string_view outDir, std::string_view outPrefix, std::string_view fileFor); void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV = DefRDHVersion, bool enablePadding = false, - bool noEmptyHBF = false, int superPageSizeInB = 1024 * 1024); + bool noEmptyHBF = false, bool noEmptyROF = false, int superPageSizeInB = 1024 * 1024); int main(int argc, char** argv) { @@ -63,6 +63,7 @@ int main(int argc, char** argv) add_option("rdh-version,r", bpo::value()->default_value(DefRDHVersion), "RDH version to use"); add_option("enable-padding", bpo::value()->default_value(false)->implicit_value(true), "enable GBT word padding to 128 bits even for RDH V7"); add_option("no-empty-hbf,e", bpo::value()->default_value(false)->implicit_value(true), "do not create empty HBF pages (except for HBF starting TF)"); + add_option("no-empty-rof", bpo::value()->default_value(false)->implicit_value(true), "do not create empty ROF blocks"); add_option("hbfutils-config,u", bpo::value()->default_value(std::string(o2::base::NameConf::DIGITIZATIONCONFIGFILE)), "config file for HBFUtils (or none)"); add_option("configKeyValues", bpo::value()->default_value(""), "comma-separated configKeyValues"); @@ -96,7 +97,8 @@ int main(int argc, char** argv) vm["verbosity"].as(), vm["rdh-version"].as(), vm["enable-padding"].as(), - vm["no-empty-hbf"].as()); + vm["no-empty-hbf"].as(), + vm["no-empty-rof"].as()); LOG(info) << "HBFUtils settings used for conversion:"; o2::raw::HBFUtils::Instance().print(); @@ -104,7 +106,7 @@ int main(int argc, char** argv) return 0; } -void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV, bool enablePadding, bool noEmptyHBF, int superPageSizeInB) +void digi2raw(std::string_view inpName, std::string_view outDir, std::string_view fileFor, int verbosity, uint32_t rdhV, bool enablePadding, bool noEmptyHBF, bool noEmptyROF, int superPageSizeInB) { TStopwatch swTot; swTot.Start(); @@ -178,9 +180,9 @@ void digi2raw(std::string_view inpName, std::string_view outDir, std::string_vie LOG(info) << "Processing ROF:" << rofRec.getROFrame() << " with " << nDigROF << " entries"; rofRec.print(); } - if (!nDigROF) { + if (!nDigROF && noEmptyROF) { if (verbosity) { - LOG(info) << "Frame is empty"; // ?? + LOG(info) << "Frame is empty"; } continue; } diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 16a1595a2d6c5..001ee537f50d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -13,11 +13,9 @@ o2_add_library(ITStracking TARGETVARNAME targetName SOURCES src/ClusterLines.cxx src/Cluster.cxx - src/ROframe.cxx + src/Configuration.cxx src/TimeFrame.cxx src/IOUtils.cxx - src/Label.cxx - src/Road.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx @@ -25,20 +23,28 @@ o2_add_library(ITStracking src/Vertexer.cxx src/VertexerTraits.cxx src/Smoother.cxx - PUBLIC_LINK_LIBRARIES O2::GPUCommon - Microsoft.GSL::GSL - O2::CommonConstants - O2::DataFormatsITSMFT - O2::SimulationDataFormat - O2::ITSBase - O2::ITSReconstruction - O2::ITSMFTReconstruction - O2::DataFormatsITS) + PUBLIC_LINK_LIBRARIES + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::SimulationDataFormat + O2::ITSBase + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) +# target_compile_options(${targetName} PRIVATE -O0 -g -fPIC -fno-omit-frame-pointer) -if (OpenMP_CXX_FOUND) - target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) - target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) -endif() +o2_add_library(ITSTrackingInterface + TARGETVARNAME targetName + SOURCES src/TrackingInterface.cxx + PRIVATE_LINK_LIBRARIES + O2::ITStracking + O2::Framework + O2::GPUTracking) o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h @@ -50,3 +56,5 @@ o2_target_root_dictionary(ITStracking if(CUDA_ENABLED OR HIP_ENABLED) add_subdirectory(GPU) endif() + +add_subdirectory(test) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h deleted file mode 100644 index 7bbc7bb098091..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Array.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_ARRAY_H_ -#define ITSTRACKINGGPU_ARRAY_H_ - -#include "GPUCommonDef.h" -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -namespace -{ -template -struct ArrayTraits final { - typedef T InternalArray[Size]; - - GPUhd() static constexpr T& getReference(const InternalArray& internalArray, size_t index) noexcept - { - return const_cast(internalArray[index]); - } - - GPUhd() static constexpr T* getPointer(const InternalArray& internalArray) noexcept - { - return const_cast(internalArray); - } -}; -} // namespace - -template -struct Array final { - - void copy(const Array& t) - { - memcpy(InternalArray, t.data(), Size * sizeof(T)); - } - - GPUhd() T* data() noexcept { return const_cast(InternalArray); } - GPUhd() const T* data() const noexcept { return const_cast(InternalArray); } - GPUhd() T& operator[](const int index) noexcept { return const_cast(InternalArray[index]); } - GPUhd() constexpr T& operator[](const int index) const noexcept { return const_cast(InternalArray[index]); } - GPUhd() size_t size() const noexcept { return Size; } - - T InternalArray[Size]; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h index 3a8af39d1ee63..75d75e0f67700 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h @@ -18,9 +18,6 @@ #include "GPUCommonDef.h" #include /// Required to properly compile MathUtils #include "ITStracking/ClusterLines.h" -#ifdef __HIPCC__ -#include -#endif namespace o2 { @@ -60,7 +57,7 @@ class ClusterLinesGPU final public: GPUd() ClusterLinesGPU(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid GPUd() void computeClusterCentroid(); - GPUd() inline float* getVertex() { return mVertex; } + GPUdi() float* getVertex() { return mVertex; } private: float mAMatrix[6]; // AX=B diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h deleted file mode 100644 index 294016ffdcaee..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Context.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_CONTEXT_H_ -#define ITSTRACKINGGPU_CONTEXT_H_ - -#include -#include -#include "ITStracking/Definitions.h" -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -struct DeviceProperties final { - std::string name; - int gpuProcessors; - int gpuCores; - long globalMemorySize; - long constantMemorySize; - long sharedMemorySize; - long maxClockRate; - int busWidth; - long l2CacheSize; - long registersPerBlock; - int warpSize; - int maxThreadsPerBlock; - int maxBlocksPerSM; - dim3 maxThreadsDim; - dim3 maxGridDim; -}; - -class Context final -{ - public: - static Context& getInstance(); - - Context(const Context&); - Context& operator=(const Context&); - - const DeviceProperties& getDeviceProperties(); - const DeviceProperties& getDeviceProperties(const int); - - private: - Context(bool dumpDevices = false); - ~Context() = default; - - int mDevicesNum; - std::vector mDeviceProperties; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif /* TRAKINGITSU_INCLUDE_GPU_CONTEXT_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h deleted file mode 100644 index 67e5109b04623..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file PrimaryVertexContextNVNV.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_PRIMARYVERTEXCONTEXTGPU_H_ -#define ITSTRACKINGGPU_PRIMARYVERTEXCONTEXTGPU_H_ - -#include - -#ifndef GPUCA_GPUCODE_GENRTC -#include -#include -#endif - -#include "ITStracking/Configuration.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/PrimaryVertexContext.h" -#include "ITStracking/Road.h" -#include "ITStracking/Tracklet.h" - -#include "DeviceStoreGPU.h" -#include "UniquePointer.h" - -namespace o2 -{ -namespace its -{ - -class PrimaryVertexContextNV final : public PrimaryVertexContext -{ - public: - PrimaryVertexContextNV() = default; - ~PrimaryVertexContextNV() override; - - void initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, - const std::vector>& cl, const std::array& pv, const int iteration) override; - - gpu::DeviceStoreNV& getDeviceContext(); - gpu::Array, constants::its2::LayersNumber>& getDeviceClusters(); - gpu::Array, constants::its2::TrackletsPerRoad>& getDeviceTracklets(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceTrackletsLookupTable(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceTrackletsPerClustersTable(); - gpu::Array, constants::its2::CellsPerRoad>& getDeviceCells(); - gpu::Array, constants::its2::CellsPerRoad - 1>& getDeviceCellsLookupTable(); - gpu::Array, constants::its2::CellsPerRoad - 1>& getDeviceCellsPerTrackletTable(); - std::array, constants::its2::CellsPerRoad>& getTempTableArray(); - std::array, constants::its2::CellsPerRoad>& getTempTrackletArray(); - std::array, constants::its2::CellsPerRoad - 1>& getTempCellArray(); - void updateDeviceContext(); - - private: - gpu::DeviceStoreNV mGPUContext; - gpu::UniquePointer mGPUContextDevicePointer; - std::array, constants::its2::CellsPerRoad> mTempTableArray; - std::array, constants::its2::CellsPerRoad> mTempTrackletArray; - std::array, constants::its2::CellsPerRoad - 1> mTempCellArray; -}; - -inline PrimaryVertexContextNV::~PrimaryVertexContextNV() = default; - -inline gpu::DeviceStoreNV& PrimaryVertexContextNV::getDeviceContext() -{ - return *mGPUContextDevicePointer; -} - -inline gpu::Array, constants::its2::LayersNumber>& PrimaryVertexContextNV::getDeviceClusters() -{ - return mGPUContext.getClusters(); -} - -inline gpu::Array, constants::its2::TrackletsPerRoad>& PrimaryVertexContextNV::getDeviceTracklets() -{ - return mGPUContext.getTracklets(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceTrackletsLookupTable() -{ - return mGPUContext.getTrackletsLookupTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& - PrimaryVertexContextNV::getDeviceTrackletsPerClustersTable() -{ - return mGPUContext.getTrackletsPerClusterTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getDeviceCells() -{ - return mGPUContext.getCells(); -} - -inline gpu::Array, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getDeviceCellsLookupTable() -{ - return mGPUContext.getCellsLookupTable(); -} - -inline gpu::Array, constants::its2::CellsPerRoad - 1>& - PrimaryVertexContextNV::getDeviceCellsPerTrackletTable() -{ - return mGPUContext.getCellsPerTrackletTable(); -} - -inline std::array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTableArray() -{ - return mTempTableArray; -} - -inline std::array, constants::its2::CellsPerRoad>& PrimaryVertexContextNV::getTempTrackletArray() -{ - return mTempTrackletArray; -} - -inline std::array, constants::its2::CellsPerRoad - 1>& PrimaryVertexContextNV::getTempCellArray() -{ - return mTempCellArray; -} - -inline void PrimaryVertexContextNV::updateDeviceContext() -{ - mGPUContextDevicePointer = gpu::UniquePointer{mGPUContext}; -} - -inline void PrimaryVertexContextNV::initialise(const MemoryParameters& memParam, const TrackingParameters& trkParam, - const std::vector>& cl, const std::array& pv, const int iteration) -{ - ///TODO: to be re-enabled in the future - // this->PrimaryVertexContext::initialise(memParam, cl, pv, iteration); - // mGPUContextDevicePointer = mGPUContext.initialise(mPrimaryVertex, mClusters, mTracklets, mCells, mCellsLookupTable, mMinR, mMaxR); -} - -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h deleted file mode 100644 index 0ac38e7a8ac4c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Stream.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_STREAM_H_ -#define ITSTRACKINGGPU_STREAM_H_ - -#include "ITStracking/Definitions.h" - -#ifdef __HIPCC__ -#include -#endif - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -class Stream final -{ - - public: - Stream(); - ~Stream(); - - const GPUStream& get() const; - - private: - GPUStream mStream; -}; -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif /* TRAKINGITSU_INCLUDE_GPU_STREAM_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h new file mode 100644 index 0000000000000..4a028bf12eb40 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h @@ -0,0 +1,148 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H +#define TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H + +#include "ITStracking/Configuration.h" +#include "ITStracking/TimeFrame.h" + +#include "ITStrackingGPU/ClusterLinesGPU.h" +#include "ITStrackingGPU/Stream.h" + +#include + +namespace o2::its::gpu +{ +template +struct StaticTrackingParameters { + StaticTrackingParameters& operator=(const StaticTrackingParameters& t) = default; + void set(const TrackingParameters& pars) + { + ClusterSharing = pars.ClusterSharing; + MinTrackLength = pars.MinTrackLength; + NSigmaCut = pars.NSigmaCut; + PVres = pars.PVres; + DeltaROF = pars.DeltaROF; + ZBins = pars.ZBins; + PhiBins = pars.PhiBins; + CellDeltaTanLambdaSigma = pars.CellDeltaTanLambdaSigma; + } + + /// General parameters + int ClusterSharing = 0; + int MinTrackLength = nLayers; + float NSigmaCut = 5; + float PVres = 1.e-2f; + int DeltaROF = 0; + int ZBins{256}; + int PhiBins{128}; + + /// Cell finding cuts + float CellDeltaTanLambdaSigma = 0.007f; +}; + +template +class GpuTimeFrameChunk +{ + public: + static size_t computeScalingSizeBytes(const int, const TimeFrameGPUParameters&); + static size_t computeFixedSizeBytes(const TimeFrameGPUParameters&); + static size_t computeRofPerChunk(const TimeFrameGPUParameters&, const size_t); + + GpuTimeFrameChunk() = delete; + GpuTimeFrameChunk(o2::its::TimeFrame* tf, TimeFrameGPUParameters& conf) + { + mTimeFramePtr = tf; + mTFGPUParams = &conf; + } + ~GpuTimeFrameChunk(); + + /// Most relevant operations + void allocate(const size_t, Stream&); + void reset(const Task, Stream&); + size_t loadDataOnDevice(const size_t, const size_t, const int, Stream&); + + /// Interface + Cluster* getDeviceClusters(const int); + int* getDeviceClusterExternalIndices(const int); + int* getDeviceIndexTables(const int); + Tracklet* getDeviceTracklets(const int); + int* getDeviceTrackletsLookupTables(const int); + CellSeed* getDeviceCells(const int); + int* getDeviceCellsLookupTables(const int); + int* getDeviceRoadsLookupTables(const int); + TimeFrameGPUParameters* getTimeFrameGPUParameters() const { return mTFGPUParams; } + + int* getDeviceCUBTmpBuffer() { return mCUBTmpBufferDevice; } + int* getDeviceFoundTracklets() { return mFoundTrackletsDevice; } + int* getDeviceNFoundCells() { return mNFoundCellsDevice; } + int* getDeviceCellNeigboursLookupTables(const int); + int* getDeviceCellNeighbours(const int); + CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } + int** getDeviceArrayNeighboursCell() const { return mNeighboursCellDeviceArray; } + int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLookupTablesDeviceArray; } + + /// Vertexer only + int* getDeviceNTrackletCluster(const int combid) { return mNTrackletsPerClusterDevice[combid]; } + Line* getDeviceLines() { return mLinesDevice; }; + int* getDeviceNFoundLines() { return mNFoundLinesDevice; } + int* getDeviceNExclusiveFoundLines() { return mNExclusiveFoundLinesDevice; } + unsigned char* getDeviceUsedTracklets() { return mUsedTrackletsDevice; } + int* getDeviceClusteredLines() { return mClusteredLinesDevice; } + size_t getNPopulatedRof() const { return mNPopulatedRof; } + + private: + /// Host + std::array, nLayers> mHostClusters; + std::array, nLayers> mHostIndexTables; + + /// Device + std::array mClustersDevice; + std::array mClusterExternalIndicesDevice; + std::array mIndexTablesDevice; + std::array mTrackletsDevice; + std::array mTrackletsLookupTablesDevice; + std::array mCellsDevice; + // Road* mRoadsDevice; + std::array mCellsLookupTablesDevice; + std::array mNeighboursCellDevice; + std::array mNeighboursCellLookupTablesDevice; + std::array mRoadsLookupTablesDevice; + + // These are to make them accessible using layer index + CellSeed** mCellsDeviceArray; + int** mNeighboursCellDeviceArray; + int** mNeighboursCellLookupTablesDeviceArray; + + // Small accessory buffers + int* mCUBTmpBufferDevice; + int* mFoundTrackletsDevice; + int* mNFoundCellsDevice; + + /// Vertexer only + Line* mLinesDevice; + int* mNFoundLinesDevice; + int* mNExclusiveFoundLinesDevice; + unsigned char* mUsedTrackletsDevice; + std::array mNTrackletsPerClusterDevice; + int* mClusteredLinesDevice; + + /// State and configuration + bool mAllocated = false; + size_t mNRof = 0; + size_t mNPopulatedRof = 0; + o2::its::TimeFrame* mTimeFramePtr = nullptr; + TimeFrameGPUParameters* mTFGPUParams = nullptr; +}; +} // namespace o2::its::gpu +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 6c865550423c6..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -13,238 +13,286 @@ #ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H #define TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H -#ifdef __HIPCC__ -#include -#endif +#include +#include +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Configuration.h" +#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/Array.h" -#include "ITStrackingGPU/Vector.h" -#include "ITStrackingGPU/Stream.h" - -#include - -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "GPUCommonLogger.h" - -namespace o2 -{ - -namespace its +namespace o2::its::gpu { -namespace gpu -{ - -template -struct StaticTrackingParameters { - StaticTrackingParameters& operator=(const StaticTrackingParameters& t) = default; - void set(const TrackingParameters& pars) - { - ClusterSharing = pars.ClusterSharing; - MinTrackLength = pars.MinTrackLength; - NSigmaCut = pars.NSigmaCut; - PVres = pars.PVres; - DeltaROF = pars.DeltaROF; - ZBins = pars.ZBins; - PhiBins = pars.PhiBins; - CellDeltaTanLambdaSigma = pars.CellDeltaTanLambdaSigma; - } - - /// General parameters - int ClusterSharing = 0; - int MinTrackLength = nLayers; - float NSigmaCut = 5; - float PVres = 1.e-2f; - int DeltaROF = 0; - int ZBins{256}; - int PhiBins{128}; - - /// Cell finding cuts - float CellDeltaTanLambdaSigma = 0.007f; -}; - -enum class Task { - Tracker = 0, - Vertexer = 1 -}; - -template -class GpuTimeFrameChunk -{ - public: - static size_t computeScalingSizeBytes(const int, const TimeFrameGPUParameters&); - static size_t computeFixedSizeBytes(const TimeFrameGPUParameters&); - static size_t computeRofPerChunk(const TimeFrameGPUParameters&, const size_t); - - GpuTimeFrameChunk() = delete; - GpuTimeFrameChunk(o2::its::TimeFrame* tf, TimeFrameGPUParameters& conf) - { - mTimeFramePtr = tf; - mTFGPUParams = &conf; - } - ~GpuTimeFrameChunk(); - - /// Most relevant operations - void allocate(const size_t, Stream&); - void reset(const Task, Stream&); - size_t loadDataOnDevice(const size_t, const size_t, const int, Stream&); - - /// Interface - Cluster* getDeviceClusters(const int); - TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); - int* getDeviceClusterExternalIndices(const int); - int* getDeviceIndexTables(const int); - Tracklet* getDeviceTracklets(const int); - int* getDeviceTrackletsLookupTables(const int); - Cell* getDeviceCells(const int); - int* getDeviceCellsLookupTables(const int); - TimeFrameGPUParameters* getTimeFrameGPUParameters() const { return mTFGPUParams; } - - int* getDeviceCUBTmpBuffer() { return mCUBTmpBufferDevice; } - int* getDeviceFoundTracklets() { return mFoundTrackletsDevice; } - int* getDeviceNFoundCells() { return mNFoundCellsDevice; } - int* getDeviceCellNeigboursLookupTables(const int); - int* getDeviceCellNeighbours(const int); - - /// Vertexer only - int* getDeviceNTrackletCluster(const int combid) { return mNTrackletsPerClusterDevice[combid]; } - Line* getDeviceLines() { return mLinesDevice; }; - int* getDeviceNFoundLines() { return mNFoundLinesDevice; } - int* getDeviceNExclusiveFoundLines() { return mNExclusiveFoundLinesDevice; } - unsigned char* getDeviceUsedTracklets() { return mUsedTrackletsDevice; } - int* getDeviceClusteredLines() { return mClusteredLinesDevice; } - size_t getNPopulatedRof() const { return mNPopulatedRof; } - - private: - /// Host - std::array, nLayers> mHostClusters; - std::array, nLayers> mHostIndexTables; - - /// Device - std::array mClustersDevice; - std::array mTrackingFrameInfoDevice; - std::array mClusterExternalIndicesDevice; - std::array mIndexTablesDevice; - std::array mTrackletsDevice; - std::array mTrackletsLookupTablesDevice; - std::array mCellsDevice; - std::array mCellsLookupTablesDevice; - std::array mNeighboursCellDevice; - std::array mNeighboursCellLookupTablesDevice; - - int* mCUBTmpBufferDevice; - int* mFoundTrackletsDevice; - int* mNFoundCellsDevice; - - /// Vertexer only - Line* mLinesDevice; - int* mNFoundLinesDevice; - int* mNExclusiveFoundLinesDevice; - unsigned char* mUsedTrackletsDevice; - std::array mNTrackletsPerClusterDevice; - int* mClusteredLinesDevice; - - /// State and configuration - bool mAllocated = false; - size_t mNRof = 0; - size_t mNPopulatedRof = 0; - o2::its::TimeFrame* mTimeFramePtr = nullptr; - TimeFrameGPUParameters* mTFGPUParams = nullptr; -}; - template -class TimeFrameGPU : public TimeFrame +class TimeFrameGPU final : public TimeFrame { + using typename TimeFrame::CellSeedN; + using typename TimeFrame::IndexTableUtilsN; + public: - TimeFrameGPU(); - ~TimeFrameGPU(); + TimeFrameGPU() = default; + ~TimeFrameGPU() = default; /// Most relevant operations + void pushMemoryStack(const int); + void popMemoryStack(const int); void registerHostMemory(const int); void unregisterHostMemory(const int); - void initialise(const int, const TrackingParameters&, const int, IndexTableUtils* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); - void initDevice(const int, IndexTableUtils*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); - void initDeviceChunks(const int, const int); - template - size_t loadChunkData(const size_t, const size_t, const size_t); - size_t getNChunks() const { return mMemChunks.size(); } - GpuTimeFrameChunk& getChunk(const int chunk) { return mMemChunks[chunk]; } - Stream& getStream(const size_t stream) { return mGpuStreams[stream]; } - void wipe(const int); + void initialise(const int, const TrackingParameters&, const int, IndexTableUtilsN* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); + void initDevice(IndexTableUtilsN*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); + void initDeviceSAFitting(); + void loadIndexTableUtils(const int); + void loadTrackingFrameInfoDevice(const int, const int); + void createTrackingFrameInfoDeviceArray(const int); + void loadUnsortedClustersDevice(const int, const int); + void createUnsortedClustersDeviceArray(const int, const int = nLayers); + void loadClustersDevice(const int, const int); + void createClustersDeviceArray(const int, const int = nLayers); + void loadClustersIndexTables(const int, const int); + void createClustersIndexTablesArray(const int); + void createUsedClustersDevice(const int, const int); + void createUsedClustersDeviceArray(const int, const int = nLayers); + void loadUsedClustersDevice(); + void loadROFrameClustersDevice(const int, const int); + void createROFrameClustersDeviceArray(const int); + void loadMultiplicityCutMask(const int); + void loadVertices(const int); + + /// + void createTrackletsLUTDevice(const int, const int); + void createTrackletsLUTDeviceArray(const int); + void loadTrackletsDevice(); + void loadTrackletsLUTDevice(); + void loadCellsDevice(); + void loadCellsLUTDevice(); + void loadTrackSeedsDevice(); + void loadTrackSeedsChi2Device(); + void loadRoadsDevice(); + void loadTrackSeedsDevice(bounded_vector&); + void createTrackletsBuffers(const int); + void createTrackletsBuffersArray(const int); + void createCellsBuffers(const int); + void createCellsBuffersArray(const int); + void createCellsDevice(); + void createCellsLUTDevice(const int); + void createCellsLUTDeviceArray(const int); + void createNeighboursIndexTablesDevice(const int); + void createNeighboursDevice(const unsigned int layer); + void createNeighboursLUTDevice(const int, const unsigned int); + void createTrackITSExtDevice(const size_t); + void downloadTrackITSExtDevice(); + void downloadCellsNeighboursDevice(std::vector>>&, const int); + void downloadNeighboursLUTDevice(bounded_vector&, const int); + void downloadCellsDevice(); + void downloadCellsLUTDevice(); + + /// Vertexer + void createVtxTrackletsLUTDevice(const int32_t); + void createVtxTrackletsBuffers(const int32_t); + void createVtxLinesLUTDevice(const int32_t); + void createVtxLinesBuffer(const int32_t); + + /// synchronization + auto& getStream(const size_t stream) { return mGpuStreams[stream]; } + auto& getStreams() { return mGpuStreams; } + void syncStream(const size_t stream); + void syncStreams(const bool = true); + void waitEvent(const int, const int); + void recordEvent(const int); + void recordEvents(const int = 0, const int = nLayers); + + /// cleanup + virtual void wipe() final; /// interface + virtual bool isGPU() const noexcept final { return true; } + virtual const char* getName() const noexcept { return "GPU"; } int getNClustersInRofSpan(const int, const int, const int) const; - IndexTableUtils* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } - int* getDeviceROframesClusters(const int layer) { return mROframesClustersDevice[layer]; } - std::vector>& getVerticesInChunks() { return mVerticesInChunks; } - std::vector>& getNVerticesInChunks() { return mNVerticesInChunks; } - std::vector>& getLabelsInChunks() { return mLabelsInChunks; } - int getNAllocatedROFs() const { return mNrof; } // Allocated means maximum nROF for each chunk while populated is the number of loaded ones. - StaticTrackingParameters* getDeviceTrackingParameters() { return mTrackingParamsDevice; } - Vertex* getDeviceVertices() { return mVerticesDevice; } - int* getDeviceROframesPV() { return mROframesPVDevice; } + IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } + int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } + auto& getTrackITSExt() { return mTrackITSExt; } + Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } + int* getDeviceROFramesPV() { return mROFramesPVDevice; } unsigned char* getDeviceUsedClusters(const int); + const o2::base::Propagator* getChainPropagator(); + + // Hybrid + Road* getDeviceRoads() { return mRoadsDevice; } + TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } + int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } + gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } + gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } + std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } + int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } + int** getDeviceNeighboursArray() { return mNeighboursDevice.data(); } + TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); + const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } + const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } + const Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } + const int** getDeviceArrayClustersIndexTables() const { return mClustersIndexTablesDeviceArray; } + std::vector getClusterSizes(); + uint8_t** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } + const int** getDeviceROFrameClusters() const { return mROFramesClustersDeviceArray; } + Tracklet** getDeviceArrayTracklets() { return mTrackletsDeviceArray; } + int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } + int** getDeviceArrayCellsLUT() const { return mCellsLUTDeviceArray; } + int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } + CellSeedN** getDeviceArrayCells() { return mCellsDeviceArray; } + CellSeedN* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + int* getDeviceTrackSeedsLUT() { return mTrackSeedsLUTDevice; } + auto getNTrackSeeds() const { return mNTracks; } + o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } + float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } + int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } + uint8_t* getDeviceMultCutMask() { return mMultMaskDevice; } + + // Vertexer + auto& getDeviceNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDevice; } + auto& getDeviceNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDevice; } + auto& getDeviceNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDevice; } + int32_t** getDeviceArrayNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDeviceArray; } + int32_t** getDeviceArrayNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDeviceArray; } + int32_t** getDeviceArrayNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDeviceArray; } + uint8_t* getDeviceUsedTracklets() const noexcept { return mUsedTrackletsDevice; } + int32_t* getDeviceNLinesPerCluster() const noexcept { return mNLinesPerClusterDevice; } + int32_t* getDeviceNLinesPerClusterSum() const noexcept { return mNLinesPerClusterSumDevice; } + Line* getDeviceLines() const noexcept { return mLinesDevice; } + gsl::span getDeviceTrackletsPerROFs() { return mNTrackletsPerROFDevice; } + + void setDevicePropagator(const o2::base::PropagatorImpl* p) final { this->mPropagatorDevice = p; } // Host-specific getters - gsl::span getHostNTracklets(const int chunkId); - gsl::span getHostNCells(const int chunkId); + gsl::span getNTracklets() { return mNTracklets; } + gsl::span getNCells() { return mNCells; } + auto& getArrayNCells() { return mNCells; } + gsl::span getNNeighbours() { return mNNeighbours; } + auto& getArrayNNeighbours() { return mNNeighbours; } + + // Host-available device getters + gsl::span getDeviceTrackletsLUTs() { return mTrackletsLUTDevice; } + gsl::span getDeviceCellLUTs() { return mCellsLUTDevice; } + gsl::span getDeviceTracklets() { return mTrackletsDevice; } + gsl::span getDeviceCells() { return mCellsDevice; } + + // Overridden getters + int getNumberOfTracklets() const final; + int getNumberOfCells() const final; + int getNumberOfNeighbours() const final; private: - bool mHostRegistered = false; - std::vector> mMemChunks; + void allocMemAsync(void**, size_t, Stream&, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on specific stream + void allocMem(void**, size_t, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on default stream TimeFrameGPUParameters mGpuParams; - StaticTrackingParameters mStaticTrackingParams; + + // Host-available device buffer sizes + std::array mNTracklets; + std::array mNCells; + std::array mNNeighbours; // Device pointers - StaticTrackingParameters* mTrackingParamsDevice; - IndexTableUtils* mIndexTableUtilsDevice; - std::array mROframesClustersDevice; + IndexTableUtilsN* mIndexTableUtilsDevice; + + // Hybrid pref + uint8_t* mMultMaskDevice; + Vertex* mPrimaryVerticesDevice; + int* mROFramesPVDevice; + std::array mClustersDevice; + std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; std::array mUsedClustersDevice; - Vertex* mVerticesDevice; - int* mROframesPVDevice; + std::array mROFramesClustersDevice; + const Cluster** mClustersDeviceArray; + const Cluster** mUnsortedClustersDeviceArray; + const int** mClustersIndexTablesDeviceArray; + uint8_t** mUsedClustersDeviceArray; + const int** mROFramesClustersDeviceArray; + std::array mTrackletsDevice; + std::array mTrackletsLUTDevice; + std::array mCellsLUTDevice; + std::array mNeighboursLUTDevice; + + Tracklet** mTrackletsDeviceArray{nullptr}; + int** mCellsLUTDeviceArray{nullptr}; + int** mNeighboursCellDeviceArray{nullptr}; + int** mNeighboursCellLUTDeviceArray{nullptr}; + int** mTrackletsLUTDeviceArray{nullptr}; + std::array mCellsDevice; + CellSeedN** mCellsDeviceArray; + std::array mNeighboursIndexTablesDevice; + CellSeedN* mTrackSeedsDevice{nullptr}; + int* mTrackSeedsLUTDevice{nullptr}; + unsigned int mNTracks{0}; + std::array mCellSeedsDevice; + o2::track::TrackParCovF** mCellSeedsDeviceArray; + std::array mCellSeedsChi2Device; + float** mCellSeedsChi2DeviceArray; + + Road* mRoadsDevice; + TrackITSExt* mTrackITSExtDevice; + std::array*, nLayers - 2> mNeighbourPairsDevice; + std::array mNeighboursDevice; + std::array mTrackingFrameInfoDevice; + const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; + + /// Vertexer + std::array mNTrackletsPerROFDevice; + std::array mNTrackletsPerClusterDevice; + std::array mNTrackletsPerClusterSumDevice; + uint8_t* mUsedTrackletsDevice; + int32_t* mNLinesPerClusterDevice; + int32_t* mNLinesPerClusterSumDevice; + int32_t** mNTrackletsPerROFDeviceArray; + int32_t** mNTrackletsPerClusterDeviceArray; + int32_t** mNTrackletsPerClusterSumDeviceArray; + Line* mLinesDevice; // State - std::vector mGpuStreams; - size_t mAvailMemGB; - bool mFirstInit = true; - - // Output - std::vector> mVerticesInChunks; - std::vector> mNVerticesInChunks; - std::vector> mLabelsInChunks; - - // Host memeory used only in GPU tracking - std::vector mHostNTracklets; - std::vector mHostNCells; + Streams mGpuStreams; + std::bitset mPinnedUnsortedClusters{0}; + std::bitset mPinnedClusters{0}; + std::bitset mPinnedClustersIndexTables{0}; + std::bitset mPinnedUsedClusters{0}; + std::bitset mPinnedROFramesClusters{0}; + std::bitset mPinnedTrackingFrameInfo{0}; + + // Temporary buffer for storing output tracks from GPU tracking + bounded_vector mTrackITSExt; }; template -template -size_t TimeFrameGPU::loadChunkData(const size_t chunk, const size_t offset, const size_t maxRofs) // offset: readout frame to start from, maxRofs: to manage boundaries +inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, const int rofSpanSize, const int layerId) const { - size_t nRof{0}; - - mMemChunks[chunk].reset(task, mGpuStreams[chunk]); // Reset chunks memory - if constexpr ((bool)task) { - nRof = mMemChunks[chunk].loadDataOnDevice(offset, maxRofs, 3, mGpuStreams[chunk]); - } else { - nRof = mMemChunks[chunk].loadDataOnDevice(offset, maxRofs, nLayers, mGpuStreams[chunk]); - } - LOGP(debug, "In chunk {}: loaded {} readout frames starting from {}", chunk, nRof, offset); - return nRof; + return static_cast(this->mROFramesClusters[layerId][(rofIdstart + rofSpanSize) < this->mROFramesClusters.size() ? rofIdstart + rofSpanSize : this->mROFramesClusters.size() - 1] - this->mROFramesClusters[layerId][rofIdstart]); } template -inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, const int rofSpanSize, const int layerId) const +inline std::vector TimeFrameGPU::getClusterSizes() { - return static_cast(mROframesClusters[layerId][(rofIdstart + rofSpanSize) < mROframesClusters.size() ? rofIdstart + rofSpanSize : mROframesClusters.size() - 1] - mROframesClusters[layerId][rofIdstart]); + std::vector sizes(this->mUnsortedClusters.size()); + std::transform(this->mUnsortedClusters.begin(), this->mUnsortedClusters.end(), sizes.begin(), + [](const auto& v) { return static_cast(v.size()); }); + return sizes; } -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file + +template +inline int TimeFrameGPU::getNumberOfTracklets() const +{ + return std::accumulate(mNTracklets.begin(), mNTracklets.end(), 0); +} + +template +inline int TimeFrameGPU::getNumberOfCells() const +{ + return std::accumulate(mNCells.begin(), mNCells.end(), 0); +} + +template +inline int TimeFrameGPU::getNumberOfNeighbours() const +{ + return std::accumulate(mNNeighbours.begin(), mNNeighbours.end(), 0); +} + +} // namespace o2::its::gpu + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h index b8f9c73d8fc29..e2bd7266caff9 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h @@ -11,6 +11,9 @@ /// #include "ITStracking/Definitions.h" +#ifndef TRACKINGITSGPU_INCLUDE_TRACER_H +#define TRACKINGITSGPU_INCLUDE_TRACER_H + #if defined(__CUDACC__) && defined(__USE_GPU_TRACER__) namespace o2 { @@ -30,4 +33,6 @@ class Tracer #define RANGE(name, cid) o2::its::gpu::Tracer tracer(name, cid); #else #define RANGE(name, cid) -#endif \ No newline at end of file +#endif + +#endif // TRACKINGITSGPU_INCLUDE_TRACER_H \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h index 314182fdd48c6..7d26e74692aa5 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -13,34 +13,36 @@ #ifndef ITSTRACKINGGPU_TRACKERTRAITSGPU_H_ #define ITSTRACKINGGPU_TRACKERTRAITSGPU_H_ -#include "ITStracking/Configuration.h" -#include "ITStracking/Definitions.h" #include "ITStracking/TrackerTraits.h" #include "ITStrackingGPU/TimeFrameGPU.h" -namespace o2 -{ -namespace its +namespace o2::its { template -class TrackerTraitsGPU : public TrackerTraits +class TrackerTraitsGPU final : public TrackerTraits { + using typename TrackerTraits::IndexTableUtilsN; + public: TrackerTraitsGPU() = default; - ~TrackerTraitsGPU() override = default; + ~TrackerTraitsGPU() final = default; + + void adoptTimeFrame(TimeFrame* tf) final; + void initialiseTimeFrame(const int iteration) final; + + void computeLayerTracklets(const int iteration, int, int) final; + void computeLayerCells(const int iteration) final; + void findCellsNeighbours(const int iteration) final; + void findRoads(const int iteration) final; - // void computeLayerCells() final; - void adoptTimeFrame(TimeFrame* tf) override; - void initialiseTimeFrame(const int iteration) override; - void computeLayerTracklets(const int iteration) final; - void computeLayerCells(const int iteration) override; - void setBz(float) override; - void findCellsNeighbours(const int iteration) override; - void findRoads(const int iteration) override; - void findTracks() override; - void extendTracks(const int iteration) override; - // void refitTracks(const std::vector>& tf, std::vector& tracks) override; + bool supportsExtendTracks() const noexcept final { return false; } + bool supportsFindShortPrimaries() const noexcept final { return false; } + + void setBz(float) final; + + const char* getName() const noexcept final { return "GPU"; } + bool isGPU() const noexcept final { return true; } // TimeFrameGPU information forwarding int getTFNumberOfClusters() const override; @@ -48,17 +50,10 @@ class TrackerTraitsGPU : public TrackerTraits int getTFNumberOfCells() const override; private: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; - gpu::StaticTrackingParameters* mStaticTrkPars; + IndexTableUtilsN* mDeviceIndexTableUtils; + gpu::TimeFrameGPU* mTimeFrameGPU; }; -template -inline void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) -{ - mTimeFrameGPU = static_cast*>(tf); -} -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h new file mode 100644 index 0000000000000..53992ccf3eb85 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -0,0 +1,254 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#ifndef ITSTRACKINGGPU_TRACKINGKERNELS_H_ +#define ITSTRACKINGGPU_TRACKINGKERNELS_H_ + +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/Definitions.h" +#include "ITStrackingGPU/Utils.h" +#include "DetectorsBase/Propagator.h" +#include "GPUCommonDef.h" + +namespace o2::its +{ +template +class CellSeed; +class TrackingFrameInfo; +class Tracklet; +template +class IndexTableUtils; +class Cluster; +class TrackITSExt; +class ExternalAllocator; + +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minR, + std::array& maxR, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minR, + std::array& maxR, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template +void countCellsHandler(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsDeviceArray, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template +void computeCellsHandler(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsDeviceArray, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursLUTs, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Stream& stream); + +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursLUTs, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + const int nBlocks, + const int nThreads, + gpu::Stream& stream); + +int filterCellNeighboursHandler(gpuPair*, + int*, + unsigned int, + gpu::Stream&, + o2::its::ExternalAllocator* = nullptr); + +template +void processNeighboursHandler(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, + const unsigned char** usedClusters, + std::array& neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float MaxChi2ClusterAttachment, + const float maxChi2NDF, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template +void countTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float Bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +} // namespace o2::its +#endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h deleted file mode 100644 index ce04da3dde622..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/UniquePointer.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file UniquePointer.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_UNIQUEPOINTER_H_ -#define ITSTRACKINGGPU_UNIQUEPOINTER_H_ - -#include "Utils.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -namespace -{ -template -struct UniquePointerTraits final { - typedef T* InternalPointer; - - GPUhd() static constexpr T& getReference(const InternalPointer& internalPointer) noexcept - { - return const_cast(*internalPointer); - } - - GPUhd() static constexpr T* getPointer(const InternalPointer& internalPointer) noexcept - { - return const_cast(internalPointer); - } -}; -} // namespace - -template -class UniquePointer final -{ - typedef UniquePointerTraits PointerTraits; - - public: - UniquePointer(); - explicit UniquePointer(const T&); - ~UniquePointer(); - - UniquePointer(const UniquePointer&) = delete; - UniquePointer& operator=(const UniquePointer&) = delete; - - UniquePointer(UniquePointer&&); - UniquePointer& operator=(UniquePointer&&); - - GPUhd() T* get() noexcept; - GPUhd() const T* get() const noexcept; - GPUhd() T& operator*() noexcept; - GPUhd() const T& operator*() const noexcept; - - protected: - void destroy(); - - private: - typename PointerTraits::InternalPointer mDevicePointer; -}; - -template -UniquePointer::UniquePointer() : mDevicePointer{nullptr} -{ - // Nothing to do -} - -template -UniquePointer::UniquePointer(const T& ref) -{ - try { - - utils::host::gpuMalloc(reinterpret_cast(&mDevicePointer), sizeof(T)); - utils::host::gpuMemcpyHostToDevice(mDevicePointer, &ref, sizeof(T)); - - } catch (...) { - - destroy(); - - throw; - } -} - -template -UniquePointer::~UniquePointer() -{ - destroy(); -} - -template -UniquePointer::UniquePointer(UniquePointer&& other) : mDevicePointer{other.mDevicePointer} -{ - // Nothing to do -} - -template -UniquePointer& UniquePointer::operator=(UniquePointer&& other) -{ - mDevicePointer = other.mDevicePointer; - other.mDevicePointer = nullptr; - - return *this; -} - -template -void UniquePointer::destroy() -{ - if (mDevicePointer != nullptr) { - - utils::host::gpuFree(mDevicePointer); - } -} - -template -GPUhd() T* UniquePointer::get() noexcept -{ - return PointerTraits::getPointer(mDevicePointer); -} - -template -GPUhd() const T* UniquePointer::get() const noexcept -{ - return PointerTraits::getPointer(mDevicePointer); -} - -template -GPUhd() T& UniquePointer::operator*() noexcept -{ - return PointerTraits::getReference(mDevicePointer); -} - -template -GPUhd() const T& UniquePointer::operator*() const noexcept -{ - return PointerTraits::getReference(mDevicePointer); -} -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index 42b2777d9b4dd..ee0a203f32fda 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -16,60 +16,462 @@ #ifndef ITSTRACKINGGPU_UTILS_H_ #define ITSTRACKINGGPU_UTILS_H_ +#include +#include +#include + +#include "ITStracking/MathUtils.h" +#include "ITStracking/ExternalAllocator.h" + #include "GPUCommonDef.h" -#include "Stream.h" +#include "GPUCommonHelpers.h" +#include "GPUCommonLogger.h" +#include "GPUCommonDefAPI.h" -namespace o2 -{ -namespace its +#ifdef GPUCA_GPUCODE +#include +#ifndef __HIPCC__ +#define THRUST_NAMESPACE thrust::cuda +#else +#define THRUST_NAMESPACE thrust::hip +#endif +#endif + +#ifdef ITS_GPU_LOG +#define GPULog(...) LOGP(info, __VA_ARGS__) +#else +#define GPULog(...) +#endif + +namespace o2::its { +// FWD declarations +template +class IndexTableUtils; +class Tracklet; + +template +using gpuPair = std::pair; + namespace gpu { -template -GPUhd() T* getPtrFromRuler(int index, T* src, const int* ruler, const int stride = 1) + +template +struct gpuSpan { + using value_type = T; + using ptr = T*; + using ref = T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +template +struct gpuSpan { + using value_type = T; + using ptr = const T*; + using ref = const T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() gpuSpan(const gpuSpan& other) : _data(other._data), _size(other._size) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +// Abstract stream class +class Stream { - return src + ruler[index] * stride; -} + public: +#if defined(__HIPCC__) + using Handle = hipStream_t; + static constexpr Handle DefaultStream = 0; + static constexpr unsigned int DefaultFlag = hipStreamNonBlocking; + using Event = hipEvent_t; +#elif defined(__CUDACC__) + using Handle = cudaStream_t; + static constexpr Handle DefaultStream = 0; + static constexpr unsigned int DefaultFlag = cudaStreamNonBlocking; + using Event = cudaEvent_t; +#else + using Handle = void*; + static constexpr Handle DefaultStream = nullptr; + static constexpr unsigned int DefaultFlag = 0; + using Event = void*; +#endif + + Stream(unsigned int flags = DefaultFlag) + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamCreateWithFlags(&mHandle, flags)); + GPUChkErrS(hipEventCreateWithFlags(&mEvent, hipEventDisableTiming)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamCreateWithFlags(&mHandle, flags)); + GPUChkErrS(cudaEventCreateWithFlags(&mEvent, cudaEventDisableTiming)); +#endif + } + + Stream(Handle h) : mHandle(h) {} + ~Stream() + { + if (mHandle != DefaultStream) { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamDestroy(mHandle)); + GPUChkErrS(hipEventDestroy(mEvent)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamDestroy(mHandle)); + GPUChkErrS(cudaEventDestroy(mEvent)); +#endif + } + } + + operator bool() const { return mHandle != DefaultStream; } + const Handle& get() { return mHandle; } + const Handle& getStream() { return mHandle; } + const Event& getEvent() { return mEvent; } + void sync() const + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamSynchronize(mHandle)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamSynchronize(mHandle)); +#endif + } + void record() + { +#if defined(__HIPCC__) + GPUChkErrS(hipEventRecord(mEvent, mHandle)); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventRecord(mEvent, mHandle)); +#endif + } + + private: + Handle mHandle{DefaultStream}; + Event mEvent{nullptr}; +}; -template -GPUhd() const T* getPtrFromRuler(int index, const T* src, const int* ruler, const int stride = 1) +// Abstract vector for streams. +class Streams { - return src + ruler[index] * stride; -} + public: + size_t size() const noexcept { return mStreams.size(); } + void resize(size_t n) { mStreams.resize(n); } + void clear() { mStreams.clear(); } + auto& operator[](size_t i) { return mStreams[i]; } + void push_back(const Stream& stream) { mStreams.push_back(stream); } + void sync(bool device = true) + { + if (device) { +#if defined(__HIPCC__) + GPUChkErrS(hipDeviceSynchronize()); +#elif defined(__CUDACC__) + GPUChkErrS(cudaDeviceSynchronize()); +#endif + } else { + for (auto& s : mStreams) { + s.sync(); + } + } + } + void waitEvent(size_t iStream, size_t iEvent) + { +#if defined(__HIPCC__) + GPUChkErrS(hipStreamWaitEvent(mStreams[iStream].get(), mStreams[iEvent].getEvent())); +#elif defined(__CUDACC__) + GPUChkErrS(cudaStreamWaitEvent(mStreams[iStream].get(), mStreams[iEvent].getEvent())); +#endif + } -GPUh() void gpuThrowOnError(); + private: + std::vector mStreams; +}; -namespace utils +#ifdef ITS_MEASURE_GPU_TIME +class GPUTimer { -#ifdef __CUDACC__ -void checkGPUError(const cudaError_t error, const char* file = __FILE__, const int line = __LINE__); + public: + GPUTimer(const std::string& name) + : mName(name) + { + mStreams.emplace_back(Stream::DefaultStream); + startTimers(); + } + GPUTimer(Streams& streams, const std::string& name) + : mName(name) + { + for (size_t i{0}; i < streams.size(); ++i) { + mStreams.push_back(streams[i].get()); + } + startTimers(); + } + GPUTimer(Streams& streams, const std::string& name, size_t end, size_t start = 0) + : mName(name) + { + for (size_t sta{start}; sta < end; ++sta) { + mStreams.push_back(streams[sta].get()); + } + startTimers(); + } + GPUTimer(Stream& stream, const std::string& name, const int id = 0) + : mName(name) + { + mStreams.push_back(stream.get()); + mName += ":id" + std::to_string(id); + startTimers(); + } + ~GPUTimer() + { + for (size_t i{0}; i < mStreams.size(); ++i) { + float ms = 0.0f; +#if defined(__HIPCC__) + GPUChkErrS(hipEventRecord(mStops[i], mStreams[i])); + GPUChkErrS(hipEventSynchronize(mStops[i])); + GPUChkErrS(hipEventElapsedTime(&ms, mStarts[i], mStops[i])); + GPUChkErrS(hipEventDestroy(mStarts[i])); + GPUChkErrS(hipEventDestroy(mStops[i])); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventRecord(mStops[i], mStreams[i])); + GPUChkErrS(cudaEventSynchronize(mStops[i])); + GPUChkErrS(cudaEventElapsedTime(&ms, mStarts[i], mStops[i])); + GPUChkErrS(cudaEventDestroy(mStarts[i])); + GPUChkErrS(cudaEventDestroy(mStops[i])); +#endif + LOGP(info, "Elapsed time for {}:{} {} ms", mName, i, ms); + } + } + + void startTimers() + { + mStarts.resize(mStreams.size()); + mStops.resize(mStreams.size()); + for (size_t i{0}; i < mStreams.size(); ++i) { +#if defined(__HIPCC__) + GPUChkErrS(hipEventCreate(&mStarts[i])); + GPUChkErrS(hipEventCreate(&mStops[i])); + GPUChkErrS(hipEventRecord(mStarts[i], mStreams[i])); +#elif defined(__CUDACC__) + GPUChkErrS(cudaEventCreate(&mStarts[i])); + GPUChkErrS(cudaEventCreate(&mStops[i])); + GPUChkErrS(cudaEventRecord(mStarts[i], mStreams[i])); #endif -#ifdef __HIPCC__ -void checkGPUError(const hipError_t error, const char* file = __FILE__, const int line = __LINE__); + } + } + + private: + std::string mName; + std::vector mStarts, mStops; + std::vector mStreams; +}; +#else // ITS_MEASURE_GPU_TIME not defined +class GPUTimer +{ + public: + template + GPUTimer(Args&&...) + { + } +}; #endif -// Dump device properties -void getDeviceProp(int, bool verbose = true); - -dim3 getBlockSize(const int); -dim3 getBlockSize(const int, const int); -dim3 getBlockSize(const int, const int, const int); -dim3 getBlocksGrid(const dim3&, const int); -dim3 getBlocksGrid(const dim3&, const int, const int); - -void gpuMalloc(void**, const int); -void gpuFree(void*); -void gpuMemset(void*, int, int); -void gpuMemcpyHostToDevice(void*, const void*, int); -void gpuMemcpyDeviceToHost(void*, const void*, int); -void gpuMemcpyToSymbol(const void* symbol, const void* src, int size); -void gpuMemcpyFromSymbol(void* dst, const void* symbol, int size); - -GPUd() int getLaneIndex(); -GPUd() int shareToWarp(const int, const int); -} // namespace utils +#ifdef GPUCA_GPUCODE +template +struct TypedAllocator { + using value_type = T; + using pointer = thrust::device_ptr; + using const_pointer = thrust::device_ptr; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + TypedAllocator() noexcept : mInternalAllocator(nullptr) {} + explicit TypedAllocator(ExternalAllocator* a) noexcept : mInternalAllocator(a) {} + + template + TypedAllocator(const TypedAllocator& o) noexcept : mInternalAllocator(o.mInternalAllocator) + { + } + + pointer allocate(size_type n) + { + void* raw = mInternalAllocator->allocateStack(n * sizeof(T)); + return thrust::device_pointer_cast(static_cast(raw)); + } + + void deallocate(pointer p, size_type n) noexcept + { + if (!p) { + return; + } + void* raw = thrust::raw_pointer_cast(p); + mInternalAllocator->deallocate(static_cast(raw), n * sizeof(T)); + } + + bool operator==(TypedAllocator const& o) const noexcept + { + return mInternalAllocator == o.mInternalAllocator; + } + bool operator!=(TypedAllocator const& o) const noexcept + { + return !(*this == o); + } + + private: + ExternalAllocator* mInternalAllocator; +}; + +template +GPUdii() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, + const o2::its::IndexTableUtils* utils, + const float z1, const float z2, float maxdeltaz, float maxdeltaphi) +{ + const float zRangeMin = o2::gpu::CAMath::Min(z1, z2) - maxdeltaz; + const float phiRangeMin = (maxdeltaphi > o2::constants::math::PI) ? 0.f : currentCluster.phi - maxdeltaphi; + const float zRangeMax = o2::gpu::CAMath::Max(z1, z2) + maxdeltaz; + const float phiRangeMax = (maxdeltaphi > o2::constants::math::PI) ? o2::constants::math::TwoPI : currentCluster.phi + maxdeltaphi; + + if (zRangeMax < -utils->getLayerZ(layerIndex) || + zRangeMin > utils->getLayerZ(layerIndex) || zRangeMin > zRangeMax) { + return {}; + } + + return int4{o2::gpu::CAMath::Max(0, utils->getZBinIndex(layerIndex, zRangeMin)), + utils->getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), + o2::gpu::CAMath::Min(utils->getNzBins() - 1, utils->getZBinIndex(layerIndex, zRangeMax)), + utils->getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; +} + +GPUdii() gpuSpan getPrimaryVertices(const int rof, + const int* roframesPV, + const int nROF, + const uint8_t* mask, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[rof]; + const int stop_rof = rof >= nROF - 1 ? nROF : rof + 1; + size_t delta = mask[rof] ? roframesPV[stop_rof] - start_pv_id : 0; // return empty span if ROF is excluded + return gpuSpan(&vertices[start_pv_id], delta); +}; + +GPUdii() gpuSpan getPrimaryVertices(const int romin, + const int romax, + const int* roframesPV, + const int nROF, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[romin]; + const int stop_rof = romax >= nROF - 1 ? nROF : romax + 1; + return gpuSpan(&vertices[start_pv_id], roframesPV[stop_rof] - roframesPV[romin]); +}; + +GPUdii() gpuSpan getClustersOnLayer(const int rof, + const int totROFs, + const int layer, + const int** roframesClus, + const Cluster** clusters) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[layer][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[layer][stop_rof] - start_clus_id; + return gpuSpan(&(clusters[layer][start_clus_id]), delta); +} + +GPUdii() gpuSpan getTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + const Tracklet** tracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(tracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + int** ntracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(ntracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNTrackletsPerCluster(const int rof, + const int totROFs, + const int mode, + const int** roframesClus, + const int** ntracklets) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(ntracklets[mode][start_clus_id]), delta); +} + +GPUdii() gpuSpan getNLinesPerCluster(const int rof, + const int totROFs, + const int** roframesClus, + int* nlines) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(nlines[start_clus_id]), delta); +} + +GPUdii() gpuSpan getNLinesPerCluster(const int rof, + const int totROFs, + const int** roframesClus, + const int* nlines) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[1][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[1][stop_rof] - start_clus_id; + return gpuSpan(&(nlines[start_clus_id]), delta); +} +#endif } // namespace gpu -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h deleted file mode 100644 index 3912caec8449c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Vector.h +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Vector.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_VECTOR_H_ -#define ITSTRACKINGGPU_VECTOR_H_ - -#include -#include -#include -#include - -#include "Stream.h" -#include "Utils.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -template -class Vector final -{ - static_assert(std::is_trivially_destructible::value, "Vector only supports trivially destructible objects."); - - public: - Vector(); - explicit Vector(const size_t, const size_t = 0); - Vector(const T* const, const size_t, const size_t = 0); - GPUhd() ~Vector(); - - Vector(const Vector&) = delete; - Vector& operator=(const Vector&) = delete; - - GPUhd() Vector(Vector&&); - Vector& operator=(Vector&&); - - size_t getSizeFromDevice() const; - - T getElementFromDevice(const size_t) const; - - void resize(const size_t); - void reset(const size_t, const size_t = 0); - void reset(const T* const, const size_t, const size_t = 0); - - void resetWithInt(const size_t, const int value = 0); - void copyIntoSizedVector(std::vector&); - - GPUhd() T* get() const; - GPUhd() size_t capacity() const; - GPUhd() Vector getWeakCopy() const; - GPUd() T& operator[](const size_t) const; - - GPUd() size_t size() const; - GPUhd() void dump(); - - template - GPUd() void emplace(const size_t, Args&&...); - - protected: - void destroy(); - - private: - GPUhd() Vector(const Vector&, const bool); - - T* mArrayPtr = nullptr; - size_t* mDeviceSizePtr = nullptr; - size_t mCapacity; - bool mIsWeak; -}; - -template -Vector::Vector() : Vector{nullptr, 0} -{ - // Nothing to do -} - -template -Vector::Vector(const size_t capacity, const size_t initialSize) : Vector{nullptr, capacity, initialSize} -{ - // Nothing to do -} - -template -Vector::Vector(const T* const source, const size_t size, const size_t initialSize) : mCapacity{size}, mIsWeak{false} -{ - if (size > 0) { - try { - - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(T)); - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(size_t)); - - if (source != nullptr) { - - utils::gpuMemcpyHostToDevice(mArrayPtr, source, size * sizeof(T)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); - - } else { - - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &initialSize, sizeof(size_t)); - } - - } catch (...) { - - destroy(); - - throw; - } - } -} - -template -GPUhd() Vector::Vector(const Vector& other, const bool isWeak) - : mArrayPtr{other.mArrayPtr}, - mDeviceSizePtr{other.mDeviceSizePtr}, - mCapacity{other.mCapacity}, - mIsWeak{isWeak} -{ - // Nothing to do -} - -template -GPUhd() Vector::~Vector() -{ - if (mIsWeak) { - return; - } else { -#if defined(TRACKINGITSU_GPU_DEVICE) - assert(0); -#else - destroy(); -#endif - } -} - -template -GPUhd() Vector::Vector(Vector&& other) - : mArrayPtr{other.mArrayPtr}, - mDeviceSizePtr{other.mDeviceSizePtr}, - mCapacity{other.mCapacity}, - mIsWeak{other.mIsWeak} -{ - other.mArrayPtr = nullptr; - other.mDeviceSizePtr = nullptr; -} - -template -Vector& Vector::operator=(Vector&& other) -{ - destroy(); - - mArrayPtr = other.mArrayPtr; - mDeviceSizePtr = other.mDeviceSizePtr; - mCapacity = other.mCapacity; - mIsWeak = other.mIsWeak; - - other.mArrayPtr = nullptr; - other.mDeviceSizePtr = nullptr; - - return *this; -} - -template -size_t Vector::getSizeFromDevice() const -{ - size_t size; - utils::gpuMemcpyDeviceToHost(&size, mDeviceSizePtr, sizeof(size_t)); - - return size; -} - -template -void Vector::resize(const size_t size) -{ - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); -} - -template -void Vector::reset(const size_t capacity, const size_t initialSize) -{ - reset(nullptr, capacity, initialSize); -} - -template -void Vector::reset(const T* const source, const size_t size, const size_t initialSize) -{ - if (size > mCapacity) { - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(T)); - mCapacity = size; - } - if (mDeviceSizePtr == nullptr) { - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(size_t)); - } - - if (source != nullptr) { - utils::gpuMemcpyHostToDevice(mArrayPtr, source, size * sizeof(T)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(size_t)); - } else { - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &initialSize, sizeof(size_t)); - } -} - -template -void Vector::resetWithInt(const size_t size, const int value) -{ - if (size > mCapacity) { - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - utils::gpuMalloc(reinterpret_cast(&mArrayPtr), size * sizeof(int)); - mCapacity = size; - } - if (mDeviceSizePtr == nullptr) { - utils::gpuMalloc(reinterpret_cast(&mDeviceSizePtr), sizeof(int)); - } - - utils::gpuMemset(mArrayPtr, value, size * sizeof(int)); - utils::gpuMemcpyHostToDevice(mDeviceSizePtr, &size, sizeof(int)); -} - -template -void Vector::copyIntoSizedVector(std::vector& destinationVector) -{ - utils::gpuMemcpyDeviceToHost(destinationVector.data(), mArrayPtr, destinationVector.size() * sizeof(T)); -} - -template -inline void Vector::destroy() -{ - if (mArrayPtr != nullptr) { - utils::gpuFree(mArrayPtr); - } - if (mDeviceSizePtr != nullptr) { - utils::gpuFree(mDeviceSizePtr); - } -} - -template -GPUhd() T* Vector::get() const -{ - return mArrayPtr; -} - -template -GPUhd() size_t Vector::capacity() const -{ - return mCapacity; -} - -template -GPUhd() Vector Vector::getWeakCopy() const -{ - return Vector{*this, true}; -} - -template -GPUd() T& Vector::operator[](const size_t index) const -{ - return mArrayPtr[index]; -} - -template -GPUh() T Vector::getElementFromDevice(const size_t index) const -{ - T element; - utils::gpuMemcpyDeviceToHost(&element, mArrayPtr + index, sizeof(T)); - - return element; -} - -template -GPUd() size_t Vector::size() const -{ - return *mDeviceSizePtr; -} - -template -template -GPUd() void Vector::emplace(const size_t index, Args&&... arguments) -{ - new (mArrayPtr + index) T(std::forward(arguments)...); -} - -template -GPUhd() void Vector::dump() -{ - printf("mArrayPtr = %p\nmDeviceSize = %p\nmCapacity = %d\nmIsWeak = %s\n", - mArrayPtr, mDeviceSizePtr, mCapacity, mIsWeak ? "true" : "false"); -} -} // namespace gpu -} // namespace its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h index f9d3e3fd0ed61..dddc247466c65 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h @@ -18,7 +18,6 @@ #define ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ #include -#include #include "ITStracking/VertexerTraits.h" #include "ITStracking/Configuration.h" @@ -29,36 +28,28 @@ #include "ITStrackingGPU/TimeFrameGPU.h" -namespace o2 +namespace o2::its { -namespace its -{ -class ROframe; - -using constants::its2::InversePhiBinSize; -class VertexerTraitsGPU : public VertexerTraits +template +class VertexerTraitsGPU final : public VertexerTraits { public: - VertexerTraitsGPU(); - ~VertexerTraitsGPU() override; - void initialise(const TrackingParameters&) override; - void adoptTimeFrame(TimeFrame*) override; - void computeTracklets() override; - void computeTrackletMatching() override; - void computeVertices() override; - void updateVertexingParameters(const VertexingParameters&, const TimeFrameGPUParameters&) override; + void initialise(const TrackingParameters&, const int iteration = 0) final; + void adoptTimeFrame(TimeFrame* tf) noexcept final; + void computeTracklets(const int iteration = 0) final; + void computeTrackletMatching(const int iteration = 0) final; + void computeVertices(const int iteration = 0) final; + void updateVertexingParameters(const std::vector&, const TimeFrameGPUParameters&) final; - void computeVerticesHist(); + bool isGPU() const noexcept final { return true; } + const char* getName() const noexcept final { return "GPU"; } protected: - IndexTableUtils* mDeviceIndexTableUtils; - gpu::TimeFrameGPU<7>* mTimeFrameGPU; + gpu::TimeFrameGPU* mTimeFrameGPU; TimeFrameGPUParameters mTfGPUParams; }; -inline void VertexerTraitsGPU::adoptTimeFrame(TimeFrame* tf) { mTimeFrameGPU = static_cast*>(tf); } +} // namespace o2::its -} // namespace its -} // namespace o2 #endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h new file mode 100644 index 0000000000000..67f12bad8486c --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h @@ -0,0 +1,115 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#ifndef ITSTRACKINGGPU_VERTEXINGKERNELS_H_ +#define ITSTRACKINGGPU_VERTEXINGKERNELS_H_ + +#include +#include +#include +#include "ITStracking/Tracklet.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/ClusterLines.h" +#include "ITStrackingGPU/Utils.h" + +namespace o2::its +{ + +/// Trackleting +template +void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int32_t vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + int32_t** trackletsPerClusterLUTs, + int32_t** trackletsPerClusterSumLUTs, + int32_t** trackletsPerROF, + const std::array& trackletsPerClusterLUTsHost, + const std::array& trackletsPerClusterSumLUTsHost, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + Tracklet** GPUrestrict() foundTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + const int32_t** GPUrestrict() trackletsPerROF, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); + +/// Selection +void countTrackletsMatchingInROFsHandler(const int32_t nRofs, + const int32_t deltaROF, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const Cluster** GPUrestrict() clusters, + uint8_t** GPUrestrict() usedClusters, + const Tracklet** GPUrestrict() foundTracklets, + uint8_t* GPUrestrict() usedTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + int32_t* GPUrestrict() linesPerClusterLUT, + int32_t* GPUrestrict() linesPerClusterSumLUT, + const int32_t iteration, + const float phiCut, + const float tanLambdaCut, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); + +void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, + const int32_t deltaROF, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const Cluster** GPUrestrict() clusters, + const uint8_t** GPUrestrict() usedClusters, + const Tracklet** GPUrestrict() foundTracklets, + uint8_t* GPUrestrict() usedTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + const int32_t* GPUrestrict() linesPerClusterSumLUT, + Line* GPUrestrict() lines, + const int32_t iteration, + const float phiCut, + const float tanLambdaCut, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); + +} // namespace o2::its +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index 6ab442f3d33b8..e38dbb1ef20e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -11,27 +11,29 @@ # CUDA if(CUDA_ENABLED) -find_package(CUDAToolkit) + find_package(CUDAToolkit) + message(STATUS "Building ITS CUDA tracker") + # add_compile_options(-O0 -g -lineinfo -fPIC -DGPU_FORCE_DEVICE_ASSERTS=ON) + # add_compile_definitions(ITS_MEASURE_GPU_TIME) + # add_compile_definitions(ITS_GPU_LOG) + o2_add_library(ITStrackingCUDA + SOURCES ClusterLinesGPU.cu + TrackerTraitsGPU.cxx + TimeFrameGPU.cu + TracerGPU.cu + TrackingKernels.cu + VertexingKernels.cu + VertexerTraitsGPU.cxx + PUBLIC_INCLUDE_DIRECTORIES ../ + PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::SimConfig + O2::SimulationDataFormat + O2::ReconstructionDataFormats + O2::GPUCommon + PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider + TARGETVARNAME targetName) -message(STATUS "Building ITS CUDA tracker") - -o2_add_library(ITStrackingCUDA - SOURCES ClusterLinesGPU.cu - Context.cu - Stream.cu - TrackerTraitsGPU.cu - TimeFrameGPU.cu - TracerGPU.cu - VertexerTraitsGPU.cu - Utils.cu - PUBLIC_INCLUDE_DIRECTORIES ../ - PUBLIC_LINK_LIBRARIES O2::ITStracking - O2::SimConfig - O2::SimulationDataFormat - CUDA::nvToolsExt # TODO: change to CUDA::nvtx3 when CMake bump >= 3.25 - TARGETVARNAME targetName) - -set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) -target_compile_definitions(${targetName} PRIVATE GPUCA_O2_LIB $) - -endif() \ No newline at end of file + set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) + target_compile_definitions(${targetName} PRIVATE $) + set_target_gpu_arch("CUDA" ${targetName}) +endif() diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu index e7753087715d5..79f4e40dc5f10 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu @@ -11,6 +11,7 @@ /// /// \author matteo.concas@cern.ch +#include #include "ITStrackingGPU/ClusterLinesGPU.h" namespace o2 @@ -134,4 +135,4 @@ GPUd() void ClusterLinesGPU::computeClusterCentroid() } } // namespace gpu } // namespace its -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu deleted file mode 100644 index 2df3544012e59..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITStrackingGPU/Context.h" -#include "ITStrackingGPU/Utils.h" - -#include -#include -#include - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -using utils::checkGPUError; - -Context::Context(bool dumpDevices) -{ - checkGPUError(cudaGetDeviceCount(&mDevicesNum), __FILE__, __LINE__); - - if (mDevicesNum == 0) { - throw std::runtime_error{"There are no available GPU device(s)\n"}; - } - - mDeviceProperties.resize(mDevicesNum, DeviceProperties{}); - - int currentDeviceIndex; - checkGPUError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); - - for (int iDevice{0}; iDevice < mDevicesNum; ++iDevice) { - - cudaDeviceProp deviceProperties; - - checkGPUError(cudaSetDevice(iDevice), __FILE__, __LINE__); - checkGPUError(cudaGetDeviceProperties(&deviceProperties, iDevice), __FILE__, __LINE__); - - int major = deviceProperties.major; - int minor = deviceProperties.minor; - - mDeviceProperties[iDevice].name = deviceProperties.name; - mDeviceProperties[iDevice].gpuProcessors = deviceProperties.multiProcessorCount; - mDeviceProperties[iDevice].gpuCores = getGPUCores(major, minor) * deviceProperties.multiProcessorCount; - mDeviceProperties[iDevice].globalMemorySize = deviceProperties.totalGlobalMem; - mDeviceProperties[iDevice].constantMemorySize = deviceProperties.totalConstMem; - mDeviceProperties[iDevice].sharedMemorySize = deviceProperties.sharedMemPerBlock; - mDeviceProperties[iDevice].maxClockRate = deviceProperties.memoryClockRate; - mDeviceProperties[iDevice].busWidth = deviceProperties.memoryBusWidth; - mDeviceProperties[iDevice].l2CacheSize = deviceProperties.l2CacheSize; - mDeviceProperties[iDevice].registersPerBlock = deviceProperties.regsPerBlock; - mDeviceProperties[iDevice].warpSize = deviceProperties.warpSize; - mDeviceProperties[iDevice].maxThreadsPerBlock = deviceProperties.maxThreadsPerBlock; - mDeviceProperties[iDevice].maxBlocksPerSM = getGPUMaxThreadsPerComputingUnit(); - mDeviceProperties[iDevice].maxThreadsDim = dim3{static_cast(deviceProperties.maxThreadsDim[0]), - static_cast(deviceProperties.maxThreadsDim[1]), - static_cast(deviceProperties.maxThreadsDim[2])}; - mDeviceProperties[iDevice].maxGridDim = dim3{static_cast(deviceProperties.maxGridSize[0]), - static_cast(deviceProperties.maxGridSize[1]), - static_cast(deviceProperties.maxGridSize[2])}; - if (dumpDevices) { - std::cout << "################ " << GPU_ARCH << " DEVICE " << iDevice << " ################" << std::endl; - std::cout << "Name " << mDeviceProperties[iDevice].name << std::endl; - std::cout << "minor " << minor << " major " << major << std::endl; - std::cout << "gpuProcessors " << mDeviceProperties[iDevice].gpuProcessors << std::endl; - std::cout << "gpuCores " << mDeviceProperties[iDevice].gpuCores << std::endl; - std::cout << "globalMemorySize " << mDeviceProperties[iDevice].globalMemorySize << std::endl; - std::cout << "constantMemorySize " << mDeviceProperties[iDevice].constantMemorySize << std::endl; - std::cout << "sharedMemorySize " << mDeviceProperties[iDevice].sharedMemorySize << std::endl; - std::cout << "maxClockRate " << mDeviceProperties[iDevice].maxClockRate << std::endl; - std::cout << "busWidth " << mDeviceProperties[iDevice].busWidth << std::endl; - std::cout << "l2CacheSize " << mDeviceProperties[iDevice].l2CacheSize << std::endl; - std::cout << "registersPerBlock " << mDeviceProperties[iDevice].registersPerBlock << std::endl; - std::cout << "warpSize " << mDeviceProperties[iDevice].warpSize << std::endl; - std::cout << "maxThreadsPerBlock " << mDeviceProperties[iDevice].maxThreadsPerBlock << std::endl; - std::cout << "maxBlocksPerSM " << mDeviceProperties[iDevice].maxBlocksPerSM << std::endl; - std::cout << "maxThreadsDim " << mDeviceProperties[iDevice].maxThreadsDim.x << ", " - << mDeviceProperties[iDevice].maxThreadsDim.y << ", " - << mDeviceProperties[iDevice].maxThreadsDim.z << std::endl; - std::cout << "maxGridDim " << mDeviceProperties[iDevice].maxGridDim.x << ", " - << mDeviceProperties[iDevice].maxGridDim.y << ", " - << mDeviceProperties[iDevice].maxGridDim.z << std::endl; - std::cout << std::endl; - } - } - - checkGPUError(cudaSetDevice(currentDeviceIndex), __FILE__, __LINE__); -} - -Context& Context::getInstance() -{ - static Context gpuContext; - return gpuContext; -} - -const DeviceProperties& Context::getDeviceProperties() -{ - int currentDeviceIndex; - checkGPUError(cudaGetDevice(¤tDeviceIndex), __FILE__, __LINE__); - - return getDeviceProperties(currentDeviceIndex); -} - -const DeviceProperties& Context::getDeviceProperties(const int deviceIndex) -{ - return mDeviceProperties[deviceIndex]; -} - -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu deleted file mode 100644 index 980ba507bd613..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include "ITStrackingGPU/Stream.h" -#include "ITStrackingGPU/Utils.h" -#include "GPUCommonLogger.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -using utils::checkGPUError; - -Stream::Stream() -{ - checkGPUError(cudaStreamCreate(&mStream)); -} -Stream::~Stream() -{ - LOGP(info, "Destroying stream"); - checkGPUError(cudaStreamDestroy(mStream)); -} - -const GPUStream& Stream::get() const -{ - return mStream; -} - -} // namespace gpu -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu new file mode 100644 index 0000000000000..c8512e667aea8 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu @@ -0,0 +1,293 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include + +#include "ITStracking/Constants.h" + +#include "ITStrackingGPU/Utils.h" +#include "ITStrackingGPU/TracerGPU.h" + +#include "ITStrackingGPU/TimeFrameChunk.h" + +#include +#include + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "GPUCommonLogger.h" +#include "GPUCommonHelpers.h" + +#ifndef __HIPCC__ +#define THRUST_NAMESPACE thrust::cuda +#else +#define THRUST_NAMESPACE thrust::hip +#endif + +namespace o2::its +{ +using constants::GB; +using constants::MB; +namespace gpu +{ + +template +GpuTimeFrameChunk::~GpuTimeFrameChunk() +{ + if (mAllocated) { + for (int i = 0; i < nLayers; ++i) { + GPUChkErrS(cudaFree(mClustersDevice[i])); + // GPUChkErrS(cudaFree(mTrackingFrameInfoDevice[i])); + GPUChkErrS(cudaFree(mClusterExternalIndicesDevice[i])); + GPUChkErrS(cudaFree(mIndexTablesDevice[i])); + if (i < nLayers - 1) { + GPUChkErrS(cudaFree(mTrackletsDevice[i])); + GPUChkErrS(cudaFree(mTrackletsLookupTablesDevice[i])); + if (i < nLayers - 2) { + GPUChkErrS(cudaFree(mCellsDevice[i])); + GPUChkErrS(cudaFree(mCellsLookupTablesDevice[i])); + GPUChkErrS(cudaFree(mRoadsLookupTablesDevice[i])); + if (i < nLayers - 3) { + GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDevice[i])); + GPUChkErrS(cudaFree(mNeighboursCellDevice[i])); + } + } + } + } + // GPUChkErrS(cudaFree(mRoadsDevice)); + GPUChkErrS(cudaFree(mCUBTmpBufferDevice)); + GPUChkErrS(cudaFree(mFoundTrackletsDevice)); + GPUChkErrS(cudaFree(mNFoundCellsDevice)); + GPUChkErrS(cudaFree(mCellsDeviceArray)); + GPUChkErrS(cudaFree(mNeighboursCellDeviceArray)); + GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDeviceArray)); + } +} + +template +void GpuTimeFrameChunk::allocate(const size_t nrof, Stream& stream) +{ + RANGE("device_partition_allocation", 2); + mNRof = nrof; + // for (int i = 0; i < nLayers; ++i) { + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClustersDevice[i])), sizeof(Cluster) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackingFrameInfoDevice[i])), sizeof(TrackingFrameInfo) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClusterExternalIndicesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mIndexTablesDevice[i])), sizeof(int) * (256 * 128 + 1) * nrof, &stream, true); + // if (i < nLayers - 1) { + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsDevice[i])), sizeof(Tracklet) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // if (i < nLayers - 2) { + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->validatedTrackletsCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsDevice[i])), sizeof(CellSeed) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsLookupTablesDevice[i]), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); + // if (i < nLayers - 3) { + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); + // } + // if (i < 2) { + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNTrackletsPerClusterDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // } + // } + // } + // } + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCUBTmpBufferDevice), mTFGPUParams->tmpCUBBufferSize * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mLinesDevice), sizeof(Line) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNExclusiveFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof + 1, &stream, true); // + 1 for cub::DeviceScan::ExclusiveSum, to cover cases where we have maximum number of clusters per ROF + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mUsedTrackletsDevice), sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mClusteredLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * nrof, &stream, true); + // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsDevice), sizeof(Road) * mTFGPUParams->maxRoadPerRofSize * nrof, &stream, true); + + // /// Invariant allocations + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mFoundTrackletsDevice), (nLayers - 1) * sizeof(int) * nrof, &stream, true); // No need to reset, we always read it after writing + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundCellsDevice), (nLayers - 2) * sizeof(int) * nrof, &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); + // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellLookupTablesDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); + + // /// Copy pointers of allocated memory to regrouping arrays + // GPUChkErrS(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, stream.get())); + // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellDeviceArray, mNeighboursCellDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); + // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellLookupTablesDeviceArray, mNeighboursCellLookupTablesDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); + + mAllocated = true; +} + +template +void GpuTimeFrameChunk::reset(const Task task, Stream& stream) +{ + RANGE("buffer_reset", 0); + // if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) + // for (int i = 0; i < 2; i++) { + // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); + // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; + // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); + // GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); + // } + // GPUChkErrS(cudaMemsetAsync(mUsedTrackletsDevice, false, sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); + // GPUChkErrS(cudaMemsetAsync(mClusteredLinesDevice, -1, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * mNRof, stream.get())); + // } else { + // for (int i = 0; i < nLayers; ++i) { + // if (i < nLayers - 1) { + // GPUChkErrS(cudaMemsetAsync(mTrackletsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); + // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); + // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; + // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); + // if (i < nLayers - 2) { + // GPUChkErrS(cudaMemsetAsync(mCellsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->cellsLUTsize * mNRof, stream.get())); + // GPUChkErrS(cudaMemsetAsync(mRoadsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); + // if (i < nLayers - 3) { + // GPUChkErrS(cudaMemsetAsync(mNeighboursCellLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); + // GPUChkErrS(cudaMemsetAsync(mNeighboursCellDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); + // } + // } + // } + // } + // GPUChkErrS(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); + // } +} + +template +size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) +{ + size_t rofsize = nLayers * sizeof(int); // number of clusters per ROF + // rofsize += nLayers * sizeof(Cluster) * config.clustersPerROfCapacity; // clusters + // rofsize += nLayers * sizeof(TrackingFrameInfo) * config.clustersPerROfCapacity; // tracking frame info + // rofsize += nLayers * sizeof(int) * config.clustersPerROfCapacity; // external cluster indices + // rofsize += nLayers * sizeof(int) * (256 * 128 + 1); // index tables + // rofsize += (nLayers - 1) * sizeof(int) * config.clustersPerROfCapacity; // tracklets lookup tables + // rofsize += (nLayers - 1) * sizeof(Tracklet) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // tracklets + // rofsize += 2 * sizeof(int) * config.clustersPerROfCapacity; // tracklets found per cluster (vertexer) + // rofsize += sizeof(unsigned char) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // used tracklets (vertexer) + // rofsize += (nLayers - 2) * sizeof(int) * config.validatedTrackletsCapacity; // cells lookup tables + // rofsize += (nLayers - 2) * sizeof(CellSeed) * config.maxNeighboursSize; // cells + // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours lookup tables + // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours + // rofsize += sizeof(Road) * config.maxRoadPerRofSize; // roads + // rofsize += (nLayers - 2) * sizeof(int) * config.maxNeighboursSize; // road LUT + // rofsize += sizeof(Line) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // lines + // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines + // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines exclusive sum + // rofsize += sizeof(int) * config.clustersPerROfCapacity * config.maxTrackletsPerCluster; // lines used in clusterlines + + // rofsize += (nLayers - 1) * sizeof(int); // total found tracklets + // rofsize += (nLayers - 2) * sizeof(int); // total found cells + + return rofsize * nrof; +} + +template +size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) +{ + size_t total = config.tmpCUBBufferSize; // CUB tmp buffers + total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once + return total; +} + +template +size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) +{ + return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); +} + +/// Interface +template +Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) +{ + return mClustersDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) +{ + return mClusterExternalIndicesDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceIndexTables(const int layer) +{ + return mIndexTablesDevice[layer]; +} + +template +Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) +{ + return mTrackletsDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceTrackletsLookupTables(const int layer) +{ + return mTrackletsLookupTablesDevice[layer]; +} + +template +CellSeed* GpuTimeFrameChunk::getDeviceCells(const int layer) +{ + return mCellsDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceCellsLookupTables(const int layer) +{ + return mCellsLookupTablesDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) +{ + return mNeighboursCellLookupTablesDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) +{ + return mNeighboursCellDevice[layer]; +} + +template +int* GpuTimeFrameChunk::getDeviceRoadsLookupTables(const int layer) +{ + return mRoadsLookupTablesDevice[layer]; +} + +// Load data +template +size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) +{ + RANGE("load_clusters_data", 5); + // auto nRofs = std::min(maxRof - startRof, mNRof); + // mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); + // for (int i = 0; i < maxLayers; ++i) { + // mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); + // mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); + // if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { + // LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); + // } + // GPUChkErrS(cudaMemcpyAsync(mClustersDevice[i], + // mHostClusters[i].data(), + // (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), + // cudaMemcpyHostToDevice, stream.get())); + // if (mHostIndexTables[i].data()) { + // GPUChkErrS(cudaMemcpyAsync(mIndexTablesDevice[i], + // mHostIndexTables[i].data(), + // mHostIndexTables[i].size() * sizeof(int), + // cudaMemcpyHostToDevice, stream.get())); + // } + // } + return mNPopulatedRof; // return the number of ROFs we loaded the data for. +} +template class GpuTimeFrameChunk<7>; +} // namespace gpu +} // namespace o2::its \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 1950bed9138f8..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -9,427 +9,711 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -#include -#include -#include "ITStracking/Constants.h" +#include + +#include +#include -#include "ITStrackingGPU/Utils.h" #include "ITStrackingGPU/TimeFrameGPU.h" -#include "ITStrackingGPU/TracerGPU.h" +#include "ITStracking/Constants.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStrackingGPU/Utils.h" -#include -#include - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2 -{ -namespace its -{ -using constants::GB; -using constants::MB; - -namespace gpu -{ -using utils::checkGPUError; -///////////////////////////////////////////////////////////////////////////////////////// -// GpuChunk -///////////////////////////////////////////////////////////////////////////////////////// -template -GpuTimeFrameChunk::~GpuTimeFrameChunk() -{ - if (mAllocated) { - for (int i = 0; i < nLayers; ++i) { - checkGPUError(cudaFree(mClustersDevice[i])); - checkGPUError(cudaFree(mTrackingFrameInfoDevice[i])); - checkGPUError(cudaFree(mClusterExternalIndicesDevice[i])); - checkGPUError(cudaFree(mIndexTablesDevice[i])); - if (i < nLayers - 1) { - checkGPUError(cudaFree(mTrackletsDevice[i])); - checkGPUError(cudaFree(mTrackletsLookupTablesDevice[i])); - if (i < nLayers - 2) { - checkGPUError(cudaFree(mCellsDevice[i])); - checkGPUError(cudaFree(mCellsLookupTablesDevice[i])); - if (i < nLayers - 3) { - checkGPUError(cudaFree(mNeighboursCellLookupTablesDevice[i])); - checkGPUError(cudaFree(mNeighboursCellDevice[i])); - } - } +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" +#include "GPUCommonLogger.h" +#include "GPUCommonHelpers.h" +#include "utils/strtag.h" + +namespace o2::its::gpu +{ + +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) +{ + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); + } +} + +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) +{ + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); + } +} + +template +void TimeFrameGPU::loadIndexTableUtils(const int iteration) +{ + GPUTimer timer("loading indextable utils"); + if (!iteration) { + GPULog("gpu-allocation: allocating IndexTableUtils buffer, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); + allocMem(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtilsN), this->hasFrameworkAllocator()); + } + GPULog("gpu-transfer: loading IndexTableUtils object, for {:.2f} MB.", sizeof(IndexTableUtilsN) / constants::MB); + GPUChkErrS(cudaMemcpy(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtilsN), cudaMemcpyHostToDevice)); +} + +template +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) +{ + if (!iteration) { + GPUTimer timer("creating unsorted clusters array"); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUnsortedClusters[iLayer].data(), this->mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(iLayer); } } - checkGPUError(cudaFree(mCUBTmpBufferDevice)); - checkGPUError(cudaFree(mFoundTrackletsDevice)); - checkGPUError(cudaFree(mNFoundCellsDevice)); - } -} - -template -void GpuTimeFrameChunk::allocate(const size_t nrof, Stream& stream) -{ - RANGE("device_partition_allocation", 2); - mNRof = nrof; - for (int i = 0; i < nLayers; ++i) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mClustersDevice[i])), sizeof(Cluster) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackingFrameInfoDevice[i])), sizeof(TrackingFrameInfo) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mClusterExternalIndicesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mIndexTablesDevice[i])), sizeof(int) * (256 * 128 + 1) * nrof, stream.get())); - if (i < nLayers - 1) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackletsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mTrackletsDevice[i])), sizeof(Tracklet) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - if (i < nLayers - 2) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mCellsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->validatedTrackletsCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mCellsDevice[i])), sizeof(Cell) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - if (i < nLayers - 3) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNeighboursCellLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNeighboursCellDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - } - if (i < 2) { - checkGPUError(cudaMallocAsync(reinterpret_cast(&(mNTrackletsPerClusterDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - } + } +} + +template +void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) +{ + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); + GPULog("gpu-transfer: loading {} unsorted clusters on layer {}, for {:.2f} MB.", this->mUnsortedClusters[layer].size(), layer, this->mUnsortedClusters[layer].size() * sizeof(Cluster) / constants::MB); + allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[layer]), this->mUnsortedClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mUnsortedClustersDevice[layer], this->mUnsortedClusters[layer].data(), this->mUnsortedClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mUnsortedClustersDeviceArray[layer], &mUnsortedClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} + +template +void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) +{ + if (!iteration) { + GPUTimer timer("creating sorted clusters array"); + allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mClusters[iLayer].data(), this->mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); + mPinnedClusters.set(iLayer); } } } - checkGPUError(cudaMallocAsync(reinterpret_cast(&mCUBTmpBufferDevice), mTFGPUParams->tmpCUBBufferSize * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mLinesDevice), sizeof(Line) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNExclusiveFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof + 1, stream.get())); // + 1 for cub::DeviceScan::ExclusiveSum, to cover cases where we have maximum number of clusters per ROF - checkGPUError(cudaMallocAsync(reinterpret_cast(&mUsedTrackletsDevice), sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mClusteredLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * nrof, stream.get())); - - /// Invariant allocations - checkGPUError(cudaMallocAsync(reinterpret_cast(&mFoundTrackletsDevice), (nLayers - 1) * sizeof(int) * nrof, stream.get())); // No need to reset, we always read it after writing - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNFoundCellsDevice), (nLayers - 2) * sizeof(int) * nrof, stream.get())); +} - mAllocated = true; +template +void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) +{ + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); + GPULog("gpu-transfer: loading {} clusters on layer {}, for {:.2f} MB.", this->mClusters[layer].size(), layer, this->mClusters[layer].size() * sizeof(Cluster) / constants::MB); + allocMemAsync(reinterpret_cast(&mClustersDevice[layer]), this->mClusters[layer].size() * sizeof(Cluster), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mClustersDevice[layer], this->mClusters[layer].data(), this->mClusters[layer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mClustersDeviceArray[layer], &mClustersDevice[layer], sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } template -void GpuTimeFrameChunk::reset(const Task task, Stream& stream) +void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { - RANGE("buffer_reset", 0); - if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) - for (int i = 0; i < 2; i++) { - auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - checkGPUError(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - } - checkGPUError(cudaMemsetAsync(mUsedTrackletsDevice, false, sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - checkGPUError(cudaMemsetAsync(mClusteredLinesDevice, -1, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * mNRof, stream.get())); - } else { - for (int i = 0; i < nLayers; ++i) { - if (i < nLayers - 1) { - checkGPUError(cudaMemsetAsync(mTrackletsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - if (i < nLayers - 2) { - checkGPUError(cudaMemsetAsync(mCellsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->cellsLUTsize * mNRof, stream.get())); - if (i < nLayers - 3) { - checkGPUError(cudaMemsetAsync(mNeighboursCellLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - checkGPUError(cudaMemsetAsync(mNeighboursCellDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - } - } + if (!iteration) { + GPUTimer timer("creating clustersindextable array"); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(iLayer); } } - checkGPUError(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); } } template -size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) +void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) { - size_t rofsize = nLayers * sizeof(int); // number of clusters per ROF - rofsize += nLayers * sizeof(Cluster) * config.clustersPerROfCapacity; // clusters - rofsize += nLayers * sizeof(TrackingFrameInfo) * config.clustersPerROfCapacity; // tracking frame info - rofsize += nLayers * sizeof(int) * config.clustersPerROfCapacity; // external cluster indices - rofsize += nLayers * sizeof(int) * (256 * 128 + 1); // index tables - rofsize += (nLayers - 1) * sizeof(int) * config.clustersPerROfCapacity; // tracklets lookup tables - rofsize += (nLayers - 1) * sizeof(Tracklet) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // tracklets - rofsize += 2 * sizeof(int) * config.clustersPerROfCapacity; // tracklets found per cluster (vertexer) - rofsize += sizeof(unsigned char) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // used tracklets (vertexer) - rofsize += (nLayers - 2) * sizeof(int) * config.validatedTrackletsCapacity; // cells lookup tables - rofsize += (nLayers - 2) * sizeof(Cell) * config.maxNeighboursSize; // cells - rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours lookup tables - rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours - rofsize += sizeof(Line) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // lines - rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines - rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines exclusive sum - rofsize += sizeof(int) * config.clustersPerROfCapacity * config.maxTrackletsPerCluster; // lines used in clusterlines + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); + GPULog("gpu-transfer: loading clusters indextable for layer {} with {} elements, for {:.2f} MB.", layer, this->mIndexTables[layer].size(), this->mIndexTables[layer].size() * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[layer]), this->mIndexTables[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mClustersIndexTablesDevice[layer], this->mIndexTables[layer].data(), this->mIndexTables[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mClustersIndexTablesDeviceArray[layer], &mClustersIndexTablesDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } +} - rofsize += (nLayers - 1) * sizeof(int); // total found tracklets - rofsize += (nLayers - 2) * sizeof(int); // total found cells +template +void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) +{ + if (!iteration) { + GPUTimer timer("creating used clusters flags"); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(iLayer); + } + } + } +} - return rofsize * nrof; +template +void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) +{ + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); + GPULog("gpu-transfer: creating {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[layer].size(), layer, this->mUsedClusters[layer].size() * sizeof(unsigned char) / constants::MB); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[layer]), this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemsetAsync(mUsedClustersDevice[layer], 0, this->mUsedClusters[layer].size() * sizeof(unsigned char), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mUsedClustersDeviceArray[layer], &mUsedClustersDevice[layer], sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } template -size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) +void TimeFrameGPU::loadUsedClustersDevice() { - size_t total = config.tmpCUBBufferSize; // CUB tmp buffers - total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once - return total; + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); + GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } } template -size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) +void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) { - return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); + if (!iteration) { + GPUTimer timer("creating ROFrame clusters array"); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(iLayer); + } + } + } } -/// Interface template -Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) +void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) { - return mClustersDevice[layer]; + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); + GPULog("gpu-transfer: loading {} ROframe clusters info on layer {}, for {:.2f} MB.", this->mROFramesClusters[layer].size(), layer, this->mROFramesClusters[layer].size() * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[layer]), this->mROFramesClusters[layer].size() * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mROFramesClustersDevice[layer], this->mROFramesClusters[layer].data(), this->mROFramesClusters[layer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mROFramesClustersDeviceArray[layer], &mROFramesClustersDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } template -TrackingFrameInfo* GpuTimeFrameChunk::getDeviceTrackingFrameInfo(const int layer) +void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) { - return mTrackingFrameInfoDevice[layer]; + if (!iteration) { + GPUTimer timer("creating trackingframeinfo array"); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(iLayer); + } + } + } } template -int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) { - return mClusterExternalIndicesDevice[layer]; + if (!iteration) { + GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); + GPULog("gpu-transfer: loading {} tfinfo on layer {}, for {:.2f} MB.", this->mTrackingFrameInfo[layer].size(), layer, this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[layer]), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(mTrackingFrameInfoDevice[layer], this->mTrackingFrameInfo[layer].data(), this->mTrackingFrameInfo[layer].size() * sizeof(TrackingFrameInfo), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mTrackingFrameInfoDeviceArray[layer], &mTrackingFrameInfoDevice[layer], sizeof(TrackingFrameInfo*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } } template -int* GpuTimeFrameChunk::getDeviceIndexTables(const int layer) +void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) { - return mIndexTablesDevice[layer]; + if (!iteration || iteration == 3) { // we need to re-load the swapped mult-mask in upc iteration + GPUTimer timer("loading multiplicity cut mask"); + GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", iteration, this->mMultiplicityCutMask.size(), this->mMultiplicityCutMask.size() * sizeof(uint8_t) / constants::MB); + if (!iteration) { // only allocate on first call + allocMem(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), this->hasFrameworkAllocator()); + } + GPUChkErrS(cudaMemcpy(mMultMaskDevice, this->mMultiplicityCutMask.data(), this->mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice)); + } } template -Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) +void TimeFrameGPU::loadVertices(const int iteration) { - return mTrackletsDevice[layer]; + if (!iteration) { + GPUTimer timer("loading seeding vertices"); + GPULog("gpu-transfer: loading {} ROframes vertices, for {:.2f} MB.", this->mROFramesPV.size(), this->mROFramesPV.size() * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mROFramesPVDevice), this->mROFramesPV.size() * sizeof(int), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mROFramesPVDevice, this->mROFramesPV.data(), this->mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice)); + GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); + allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); + } } template -int* GpuTimeFrameChunk::getDeviceTrackletsLookupTables(const int layer) +void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { - return mTrackletsLookupTablesDevice[layer]; + if (!iteration) { + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); + } } template -Cell* GpuTimeFrameChunk::getDeviceCells(const int layer) +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) { - return mCellsDevice[layer]; + GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); + const int ncls = this->mClusters[layer].size() + 1; + if (!iteration) { + GPULog("gpu-allocation: creating tracklets LUT for {} elements on layer {}, for {:.2f} MB.", ncls, layer, ncls * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[layer]), ncls * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpyAsync(&mTrackletsLUTDeviceArray[layer], &mTrackletsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); + } + GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); } template -int* GpuTimeFrameChunk::getDeviceCellsLookupTables(const int layer) +void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { - return mCellsLookupTablesDevice[layer]; + if (!iteration) { + GPUTimer timer("creating tracklet buffers array"); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); + } } template -int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) +void TimeFrameGPU::createTrackletsBuffers(const int layer) { - return mNeighboursCellLookupTablesDevice[layer]; + GPUTimer timer(mGpuStreams[layer], "creating tracklet buffers", layer); + mNTracklets[layer] = 0; + GPUChkErrS(cudaMemcpyAsync(&mNTracklets[layer], mTrackletsLUTDevice[layer] + this->mClusters[layer].size(), sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of tracklets is correct + GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpyAsync(&mTrackletsDeviceArray[layer], &mTrackletsDevice[layer], sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } template -int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) +void TimeFrameGPU::loadTrackletsDevice() { - return mNeighboursCellDevice[layer]; + GPUTimer timer(mGpuStreams, "loading tracklets", nLayers - 1); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + GPULog("gpu-transfer: loading {} tracklets on layer {}, for {:.2f} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / constants::MB); + GPUChkErrS(cudaHostRegister(this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } } -// Load data template -size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) +void TimeFrameGPU::loadTrackletsLUTDevice() { - RANGE("load_clusters_data", 5); - auto nRofs = std::min(maxRof - startRof, mNRof); - mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); - for (int i = 0; i < maxLayers; ++i) { - mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); - mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); - if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { - LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); - } - checkGPUError(cudaMemcpyAsync(mClustersDevice[i], - mHostClusters[i].data(), - (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), - cudaMemcpyHostToDevice, stream.get())); - if (mHostIndexTables[i].data()) { - checkGPUError(cudaMemcpyAsync(mIndexTablesDevice[i], - mHostIndexTables[i].data(), - mHostIndexTables[i].size() * sizeof(int), - cudaMemcpyHostToDevice, stream.get())); - } + GPUTimer timer("loading tracklets"); + for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPULog("gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {:.2f} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - return mNPopulatedRof; // return the number of ROFs we loaded the data for. + mGpuStreams.sync(); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } -///////////////////////////////////////////////////////////////////////////////////////// -// TimeFrameGPU -///////////////////////////////////////////////////////////////////////////////////////// template -TimeFrameGPU::TimeFrameGPU() +void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { - mIsGPU = true; - utils::getDeviceProp(0, true); + GPUTimer timer(mGpuStreams[layer], "creating cells neighbours", layer); + GPULog("gpu-transfer: reserving neighbours LUT for {} elements on layer {}, for {:.2f} MB.", mNCells[layer] + 1, layer, (mNCells[layer] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[layer]), (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); } template -TimeFrameGPU::~TimeFrameGPU() = default; +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +{ + GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); + GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursLUTDevice[layer]), (nCells + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); // We need one element more to move exc -> inc + GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); +} template -void TimeFrameGPU::registerHostMemory(const int maxLayers) +void TimeFrameGPU::loadCellsDevice() { - if (mHostRegistered) { - return; - } else { - mHostRegistered = true; + GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); + for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeedN) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->hasFrameworkAllocator()); + allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasFrameworkAllocator()); // accessory for the neigh. finding. + GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[iLayer], 0, (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer].get())); + GPUChkErrS(cudaMemcpyAsync(mCellsDevice[iLayer], this->mCells[iLayer].data(), this->mCells[iLayer].size() * sizeof(CellSeedN), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - for (auto iLayer{0}; iLayer < maxLayers; ++iLayer) { - checkGPUError(cudaHostRegister(mClusters[iLayer].data(), mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mNClustersPerROF[iLayer].data(), mNClustersPerROF[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mIndexTables[iLayer].data(), (mStaticTrackingParams.ZBins * mStaticTrackingParams.PhiBins + 1) * mNrof * sizeof(int), cudaHostRegisterPortable)); +} + +template +void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) +{ + if (!iteration) { + GPUTimer timer("creating cells LUTs array"); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); } - checkGPUError(cudaHostRegister(mHostNTracklets.data(), (nLayers - 1) * mGpuParams.nTimeFrameChunks * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mHostNCells.data(), (nLayers - 2) * mGpuParams.nTimeFrameChunks * sizeof(int), cudaHostRegisterPortable)); } template -void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +void TimeFrameGPU::createCellsLUTDevice(const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); + GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[layer]), (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsLUTDevice[layer], 0, (mNTracklets[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); + GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::createCellsBuffersArray(const int iteration) { - if (!mHostRegistered) { - return; + if (!iteration) { + GPUTimer timer("creating cells buffers array"); + allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeedN*), cudaMemcpyHostToDevice)); } - for (auto iLayer{0}; iLayer < maxLayers; ++iLayer) { - checkGPUError(cudaHostUnregister(mClusters[iLayer].data())); - checkGPUError(cudaHostUnregister(mNClustersPerROF[iLayer].data())); - checkGPUError(cudaHostUnregister(mIndexTables[iLayer].data())); +} + +template +void TimeFrameGPU::createCellsBuffers(const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); + mNCells[layer] = 0; + GPUChkErrS(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of cells is correct + GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeedN) / constants::MB); + allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeedN*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::loadCellsLUTDevice() +{ + GPUTimer timer(mGpuStreams, "loading cells LUTs", nLayers - 3); + for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPULog("gpu-transfer: loading cell LUT for {} elements on layer {}, for {:.2f} MB.", this->mCellsLookupTable[iLayer].size(), iLayer, this->mCellsLookupTable[iLayer].size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaHostRegister(this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(mCellsLUTDevice[iLayer + 1], this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } - checkGPUError(cudaHostUnregister(mHostNTracklets.data())); - checkGPUError(cudaHostUnregister(mHostNCells.data())); - mHostRegistered = false; } template -void TimeFrameGPU::initialise(const int iteration, - const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtils* utils, - const TimeFrameGPUParameters* gpuParam) +void TimeFrameGPU::loadRoadsDevice() { - mGpuStreams.resize(mGpuParams.nTimeFrameChunks); - mHostNTracklets.resize((nLayers - 1) * mGpuParams.nTimeFrameChunks, 0); - mHostNCells.resize((nLayers - 2) * mGpuParams.nTimeFrameChunks, 0); + GPUTimer timer("loading roads device"); + GPULog("gpu-transfer: loading {} roads, for {:.2f} MB.", this->mRoads.size(), this->mRoads.size() * sizeof(Road) / constants::MB); + allocMem(reinterpret_cast(&mRoadsDevice), this->mRoads.size() * sizeof(Road), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); +} - auto init = [&](int p) -> void { - this->initDevice(p, utils, trkParam, *gpuParam, maxLayers, iteration); - }; - std::thread t1{init, mGpuParams.nTimeFrameChunks}; - RANGE("tf_cpu_initialisation", 1); - o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); - registerHostMemory(maxLayers); - t1.join(); +template +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) +{ + GPUTimer timer("loading track seeds"); + GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeedN), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemcpy(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeedN), cudaMemcpyHostToDevice)); + GPULog("gpu-transfer: creating {} track seeds LUT, for {:.2f} MB.", seeds.size() + 1, (seeds.size() + 1) * sizeof(int) / constants::MB); + allocMem(reinterpret_cast(&mTrackSeedsLUTDevice), (seeds.size() + 1) * sizeof(int), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); } template -void TimeFrameGPU::wipe(const int maxLayers) +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) { - unregisterHostMemory(maxLayers); + GPUTimer timer(mGpuStreams[layer], "reserving neighbours", layer); + this->mNNeighbours[layer] = 0; + GPUChkErrS(cudaMemcpyAsync(&(this->mNNeighbours[layer]), &(mNeighboursLUTDevice[layer][this->mNCells[layer + 1] - 1]), sizeof(unsigned int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); + mGpuStreams[layer].sync(); // ensure number of neighbours is correct + GPULog("gpu-allocation: reserving {} neighbours (pairs), for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighbourPairsDevice[layer]), (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mNeighbourPairsDevice[layer], -1, (this->mNNeighbours[layer]) * sizeof(gpuPair), mGpuStreams[layer].get())); + GPULog("gpu-allocation: reserving {} neighbours, for {:.2f} MB.", this->mNNeighbours[layer], (this->mNNeighbours[layer]) * sizeof(gpuPair) / constants::MB); + allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), (this->mNNeighbours[layer]) * sizeof(int), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); } template -void TimeFrameGPU::initDevice(const int chunks, - IndexTableUtils* utils, - const TrackingParameters& trkParam, - const TimeFrameGPUParameters& gpuParam, - const int maxLayers, - const int iteration) -{ - mStaticTrackingParams.ZBins = trkParam.ZBins; - mStaticTrackingParams.PhiBins = trkParam.PhiBins; - if (mFirstInit) { - mGpuParams = gpuParam; - if (mGpuParams.maxGPUMemoryGB < 0) { - // Adaptive to available memory, hungry mode - size_t free; - checkGPUError(cudaMemGetInfo(&free, nullptr)); - mAvailMemGB = (double)free / GB; - LOGP(info, "Hungry memory mode requested, found {} free GB, going to use all of them", mAvailMemGB); - } else { - mAvailMemGB = mGpuParams.maxGPUMemoryGB; - LOGP(info, "Fixed memory mode requested, will try to use {} GB", mAvailMemGB); - } - checkGPUError(cudaMalloc(reinterpret_cast(&mTrackingParamsDevice), sizeof(gpu::StaticTrackingParameters))); - checkGPUError(cudaMemcpy(mTrackingParamsDevice, &mStaticTrackingParams, sizeof(gpu::StaticTrackingParameters), cudaMemcpyHostToDevice)); - if (utils) { // If utils is not nullptr, then its gpu vertexing - mIndexTableUtils = *utils; - checkGPUError(cudaMalloc(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtils))); - } else { // GPU tracking otherwise - mIndexTableUtils.setTrackingParameters(trkParam); - } +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) +{ + GPUTimer timer("reserving tracks"); + mNTracks = 0; + GPUChkErrS(cudaMemcpy(&mNTracks, mTrackSeedsLUTDevice + nSeeds, sizeof(int), cudaMemcpyDeviceToHost)); + GPULog("gpu-allocation: reserving {} tracks, for {:.2f} MB.", mNTracks, mNTracks * sizeof(o2::its::TrackITSExt) / constants::MB); + mTrackITSExt = bounded_vector(mNTracks, {}, this->getMemoryPool().get()); + allocMem(reinterpret_cast(&mTrackITSExtDevice), mNTracks * sizeof(o2::its::TrackITSExt), this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, mNTracks * sizeof(o2::its::TrackITSExt))); +} + +template +void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) +{ + GPUTimer timer("creating vertexer tracklet LUTs"); + const int32_t ncls = this->mClusters[1].size(); + for (int32_t iMode{0}; iMode < 2; ++iMode) { + if (!iteration) { + GPULog("gpu-transfer: creating vertexer tracklets per cluster for {} elements for mode {}, for {:.2f} MB.", ncls, iMode, ncls * sizeof(int32_t) / constants::MB); + allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterDevice[iMode]), ncls * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - mMemChunks.resize(chunks, GpuTimeFrameChunk{static_cast(this), mGpuParams}); - mVerticesInChunks.resize(chunks); - mNVerticesInChunks.resize(chunks); - mLabelsInChunks.resize(chunks); - LOGP(debug, "Size of fixed part is: {} MB", GpuTimeFrameChunk::computeFixedSizeBytes(mGpuParams) / MB); - LOGP(debug, "Size of scaling part is: {} MB", GpuTimeFrameChunk::computeScalingSizeBytes(GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB), mGpuParams) / MB); - LOGP(info, "Allocating {} chunks of {} rofs capacity each.", chunks, GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB)); + GPULog("gpu-transfer: creating vertexer tracklets per cluster sum for {} elements for mode {}, for {:.2f} MB.", ncls + 1, iMode, (ncls + 1) * sizeof(int32_t) / constants::MB); + allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterSumDevice[iMode]), (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - for (int iChunk{0}; iChunk < mMemChunks.size(); ++iChunk) { - mMemChunks[iChunk].allocate(GpuTimeFrameChunk::computeRofPerChunk(mGpuParams, mAvailMemGB), mGpuStreams[iChunk]); - } - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMalloc(reinterpret_cast(&mROframesClustersDevice[iLayer]), mROframesClusters[iLayer].size() * sizeof(int))); - checkGPUError(cudaMalloc(reinterpret_cast(&(mUsedClustersDevice[iLayer])), sizeof(unsigned char) * mGpuParams.clustersPerROfCapacity * mNrof)); + GPULog("gpu-transfer: creating vertexer tracklets per ROF for {} elements for mode {}, for {:.2f} MB.", this->mNrof + 1, iMode, (this->mNrof + 1) * sizeof(int32_t) / constants::MB); + allocMemAsync(reinterpret_cast(&mNTrackletsPerROFDevice[iMode]), (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); } - checkGPUError(cudaMalloc(reinterpret_cast(&mVerticesDevice), sizeof(Vertex) * mGpuParams.maxVerticesCapacity)); - checkGPUError(cudaMalloc(reinterpret_cast(&mROframesPVDevice), sizeof(int) * (mNrof + 1))); + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[iMode], 0, ncls * sizeof(int32_t), mGpuStreams[iMode].get())); + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterSumDevice[iMode], 0, (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); + GPUChkErrS(cudaMemsetAsync(mNTrackletsPerROFDevice[iMode], 0, (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); + } + mGpuStreams[0].sync(); + mGpuStreams[1].sync(); + if (!iteration) { + allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterDeviceArray, mNTrackletsPerClusterDevice.data(), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); + + allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterSumDeviceArray, mNTrackletsPerClusterSumDevice.data(), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - mFirstInit = false; + allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); } - if (maxLayers < nLayers) { // Vertexer - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemcpy(mROframesClustersDevice[iLayer], mROframesClusters[iLayer].data(), mROframesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); +} + +template +void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) +{ + GPUTimer timer("creating vertexer tracklet buffers"); + for (int32_t iMode{0}; iMode < 2; ++iMode) { + this->mTotalTracklets[iMode] = 0; + GPUChkErrS(cudaMemcpyAsync(&(this->mTotalTracklets[iMode]), mNTrackletsPerClusterSumDevice[iMode] + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost, mGpuStreams[iMode].get())); + GPULog("gpu-transfer: creating vertexer tracklets buffer for {} elements on layer {}, for {:.2f} MB.", this->mTotalTracklets[iMode], iMode, this->mTotalTracklets[iMode] * sizeof(Tracklet) / constants::MB); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[iMode]), this->mTotalTracklets[iMode] * sizeof(Tracklet), mGpuStreams[iMode], this->hasFrameworkAllocator()); + } + mGpuStreams[0].sync(); + mGpuStreams[1].sync(); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), 2 * sizeof(Tracklet*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpy(mTrackletsDeviceArray, mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaMemcpyHostToDevice)); +} + +template +void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) +{ + GPUTimer timer("creating vertexer lines LUT and used tracklets buffer"); + const int32_t ncls = this->mClusters[1].size(); + + GPULog("gpu-transfer: creating vertexer lines per cluster for {} elements , for {:.2f} MB.", ncls, ncls * sizeof(int32_t) / constants::MB); + allocMem(reinterpret_cast(&mNLinesPerClusterDevice), ncls * sizeof(int32_t), this->hasFrameworkAllocator()); + + GPULog("gpu-transfer: creating vertexer lines per cluster sum for {} elements , for {:.2f} MB.", ncls + 1, (ncls + 1) * sizeof(int32_t) / constants::MB); + allocMem(reinterpret_cast(&mNLinesPerClusterSumDevice), (ncls + 1) * sizeof(int32_t), this->hasFrameworkAllocator()); + + const int32_t ntrkls = this->mTotalTracklets[0]; + GPULog("gpu-transfer: creating vertexer used tracklets for {} elements , for {:.2f} MB.", ntrkls, ntrkls * sizeof(uint8_t) / constants::MB); + allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->hasFrameworkAllocator()); +} + +template +void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) +{ + GPUTimer timer("creating vertexer lines buffer and resetting used tracklets"); + int32_t nlines = 0; + GPUChkErrS(cudaMemcpy(&nlines, mNLinesPerClusterDevice + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost)); + this->mTotalLines = nlines; + GPULog("gpu-transfer: creating vertexer lines for {} elements , for {:.2f} MB.", nlines, nlines * sizeof(Line) / constants::MB); + allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->hasFrameworkAllocator()); + // reset used tracklets + GPUChkErrS(cudaMemset(mUsedTrackletsDevice, 0, this->mTotalTracklets[0] * sizeof(uint8_t))); +} + +template +void TimeFrameGPU::downloadCellsDevice() +{ + GPUTimer timer(mGpuStreams, "downloading cells", nLayers - 2); + for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPULog("gpu-transfer: downloading {} cells on layer: {}, for {:.2f} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeedN) / constants::MB); + this->mCells[iLayer].resize(mNCells[iLayer]); + GPUChkErrS(cudaMemcpyAsync(this->mCells[iLayer].data(), this->mCellsDevice[iLayer], mNCells[iLayer] * sizeof(CellSeedN), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); + } +} + +template +void TimeFrameGPU::downloadCellsLUTDevice() +{ + GPUTimer timer(mGpuStreams, "downloading cell luts", nLayers - 3); + for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPULog("gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); + this->mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); + GPUChkErrS(cudaMemcpyAsync(this->mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); + } +} + +template +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "downloading neighbours from layer", layer); + GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(std::pair) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) +{ + GPUTimer timer(mGpuStreams[layer], "downloading neighbours LUT from layer", layer); + GPULog("gpu-transfer: downloading neighbours LUT for {} elements on layer {}, for {:.2f} MB.", lut.size(), layer, lut.size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(lut.data(), mNeighboursLUTDevice[layer], lut.size() * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); +} + +template +void TimeFrameGPU::downloadTrackITSExtDevice() +{ + GPUTimer timer("downloading tracks"); + GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); + GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); +} + +template +void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +{ + GPUTimer timer("unregistering host memory"); + GPULog("unregistering host memory"); + + auto checkedUnregisterEntry = [](auto& bits, auto& vec, int layer) { + if (bits.test(layer)) { + GPUChkErrS(cudaHostUnregister(vec[layer].data())); + bits.reset(layer); } - } else { // Tracker - checkGPUError(cudaMemcpy(mVerticesDevice, mPrimaryVertices.data(), sizeof(Vertex) * mPrimaryVertices.size(), cudaMemcpyHostToDevice)); - checkGPUError(cudaMemcpy(mROframesPVDevice, mROframesPV.data(), sizeof(int) * mROframesPV.size(), cudaMemcpyHostToDevice)); - if (!iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemset(mUsedClustersDevice[iLayer], 0, sizeof(unsigned char) * mGpuParams.clustersPerROfCapacity * mNrof)); - } + }; + auto checkedUnregisterArray = [](auto& bits, auto& vec) { + if (bits.test(nLayers)) { + GPUChkErrS(cudaHostUnregister(vec.data())); + bits.reset(nLayers); } + }; + + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + checkedUnregisterEntry(mPinnedUsedClusters, this->mUsedClusters, iLayer); + checkedUnregisterEntry(mPinnedUnsortedClusters, this->mUnsortedClusters, iLayer); + checkedUnregisterEntry(mPinnedClusters, this->mClusters, iLayer); + checkedUnregisterEntry(mPinnedClustersIndexTables, this->mIndexTables, iLayer); + checkedUnregisterEntry(mPinnedTrackingFrameInfo, this->mTrackingFrameInfo, iLayer); + checkedUnregisterEntry(mPinnedROFramesClusters, this->mROFramesClusters, iLayer); } - checkGPUError(cudaMemcpy(mIndexTableUtilsDevice, &mIndexTableUtils, sizeof(IndexTableUtils), cudaMemcpyHostToDevice)); + checkedUnregisterArray(mPinnedUsedClusters, mUsedClustersDevice); + checkedUnregisterArray(mPinnedUnsortedClusters, mUnsortedClustersDevice); + checkedUnregisterArray(mPinnedClusters, mClustersDevice); + checkedUnregisterArray(mPinnedClustersIndexTables, mClustersIndexTablesDevice); + checkedUnregisterArray(mPinnedTrackingFrameInfo, mTrackingFrameInfoDevice); + checkedUnregisterArray(mPinnedROFramesClusters, mROFramesClustersDevice); } +namespace detail +{ +template +constexpr uint64_t makeIterTag() +{ + static_assert(I < 10); + constexpr char tag[] = {'I', 'T', 'S', 'I', 'T', 'E', 'R', char('0' + I), '\0'}; + return qStr2Tag(tag); +} +template +constexpr auto makeIterTags(std::index_sequence) +{ + return std::array{makeIterTag()...}; +} +// FIXME: we have to be careful that the MaxIter does not diverge from the 4 here! +constexpr auto kIterTags = makeIterTags(std::make_index_sequence<4>{}); +} // namespace detail + template -unsigned char* TimeFrameGPU::getDeviceUsedClusters(const int layer) +void TimeFrameGPU::pushMemoryStack(const int iteration) { - return mUsedClustersDevice[layer]; + // mark the beginning of memory marked with MEMORY_STACK that can be discarded + // after doing one iteration + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); } template -gsl::span TimeFrameGPU::getHostNTracklets(const int chunkId) +void TimeFrameGPU::popMemoryStack(const int iteration) { - return gsl::span(mHostNTracklets.data() + (nLayers - 1) * chunkId, nLayers - 1); + // pop all memory on the stack from this iteration + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); +} + +template +void TimeFrameGPU::initialise(const int iteration, + const TrackingParameters& trkParam, + const int maxLayers, + IndexTableUtilsN* utils, + const TimeFrameGPUParameters* gpuParam) +{ + mGpuStreams.resize(nLayers); + o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); +} + +template +void TimeFrameGPU::syncStream(const size_t stream) +{ + mGpuStreams[stream].sync(); +} + +template +void TimeFrameGPU::syncStreams(const bool device) +{ + mGpuStreams.sync(device); +} + +template +void TimeFrameGPU::waitEvent(const int stream, const int event) +{ + mGpuStreams.waitEvent(stream, event); +} + +template +void TimeFrameGPU::recordEvent(const int event) +{ + mGpuStreams[event].record(); +} + +template +void TimeFrameGPU::recordEvents(const int start, const int end) +{ + for (int i{start}; i < end; ++i) { + recordEvent(i); + } } template -gsl::span TimeFrameGPU::getHostNCells(const int chunkId) +void TimeFrameGPU::wipe() { - return gsl::span(mHostNCells.data() + (nLayers - 2) * chunkId, nLayers - 2); + unregisterHostMemory(0); + o2::its::TimeFrame::wipe(); } template class TimeFrameGPU<7>; -template class GpuTimeFrameChunk<7>; -} // namespace gpu -} // namespace its -} // namespace o2 \ No newline at end of file +} // namespace o2::its::gpu diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu index 7c09851eaa2fb..7c42658242231 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu @@ -9,10 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include #include "ITStrackingGPU/TracerGPU.h" #if !defined(__HIPCC__) && defined(__USE_GPU_TRACER__) -#include "nvToolsExt.h" +#include constexpr uint32_t colors[] = {0xff00ff00, 0xff0000ff, 0xffffff00, 0xffff00ff, 0xff00ffff, 0xffff0000, 0xffffffff}; constexpr int num_colors = sizeof(colors) / sizeof(uint32_t); @@ -44,4 +45,4 @@ Tracer::~Tracer() } // namespace gpu } // namespace its } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu deleted file mode 100644 index de24ffc947127..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cu +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "ITStracking/Constants.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/MathUtils.h" - -#include "ITStrackingGPU/TrackerTraitsGPU.h" -#include "ITStrackingGPU/TracerGPU.h" - -#include "GPUCommonLogger.h" -#include "GPUCommonAlgorithmThrust.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2 -{ -namespace its -{ -using gpu::utils::checkGPUError; -using namespace constants::its2; - -namespace gpu -{ - -GPUg() void printBufferLayerOnThread(const int layer, const int* v, size_t size, const int len = 150, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (int i{0}; i < size; ++i) { - if (!(i % len)) { - printf("\n layer %d: ===>%d/%d\t", layer, i, (int)size); - } - printf("%d\t", v[i]); - } - printf("\n"); - } -} - -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const o2::its::IndexTableUtils& utils, - const float z1, const float z2, float maxdeltaz, float maxdeltaphi) -{ - const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; - const float phiRangeMin = currentCluster.phi - maxdeltaphi; - const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; - const float phiRangeMax = currentCluster.phi + maxdeltaphi; - - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { - - return getEmptyBinsRect(); - } - - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; -} - -GPUhd() float Sq(float q) -{ - return q * q; -} - -template -struct trackletSortEmptyFunctor : public thrust::binary_function { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex > rhs.firstClusterIndex; - } -}; - -template -struct trackletSortIndexFunctor : public thrust::binary_function { - GPUhd() bool operator()(const T& lhs, const T& rhs) const - { - return lhs.firstClusterIndex < rhs.firstClusterIndex || (lhs.firstClusterIndex == rhs.firstClusterIndex && lhs.secondClusterIndex < rhs.secondClusterIndex); - } -}; - -// Dump vertices -GPUg() void printVertices(const Vertex* v, size_t size, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vertices: "); - for (int i{0}; i < size; ++i) { - printf("x=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); - } - } -} - -// Dump tracklets -GPUg() void printTracklets(const Tracklet* t, - const int offset, - const int startRof, - const int nrof, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int maxClustersPerRof = 5e2, - const int maxTrackletsPerCluster = 50, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - auto offsetChunk{(startRof - offset) * maxClustersPerRof * maxTrackletsPerCluster}; - for (int i{offsetChunk}; i < offsetChunk + nrof * maxClustersPerRof * maxTrackletsPerCluster; ++i) { - if (t[i].firstClusterIndex != -1) { - t[i].dump(offsetCurrent, offsetNext); - } - } - } -} - -GPUg() void printTrackletsNotStrided(const Tracklet* t, - const int offset, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int ntracklets, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - for (int i{0}; i < ntracklets; ++i) { - t[i].dump(offsetCurrent, offsetNext); - } - } -} - -// Compute the tracklets for a given layer -template -GPUg() void computeLayerTrackletsKernelSingleRof( - const int rof0, - const int maxRofs, - const int layerIndex, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxTrackletsPerCluster = 50) -{ - for (int currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < currentLayerClustersSize; currentClusterIndex += blockDim.x * gridDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{roFrameClusters[rof0] + currentClusterIndex}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; - const float inverseR0{1.f / currentCluster.radius}; - for (int iPrimaryVertex{0}; iPrimaryVertex < nVertices; iPrimaryVertex++) { - const auto& primaryVertex{vertices[iPrimaryVertex]}; - if (primaryVertex.getX() == 0.f && primaryVertex.getY() == 0.f && primaryVertex.getZ() == 0.f) { - continue; - } - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{std::sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - constexpr int tableSize{256 * 128 + 1}; // hardcoded for the time being - - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - if (!(roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTable[rof1 * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTable[rof1 * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { - break; - } - const Cluster& nextCluster{getPtrFromRuler(rof1, clustersNextLayer, roFrameClustersNext)[iNextCluster]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if (deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { - trackletsLookUpTable[currentSortedIndex]++; // Race-condition safe - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const size_t stride{currentClusterIndex * maxTrackletsPerCluster}; - new (tracklets + stride + storedTracklets) Tracklet{currentSortedIndex, roFrameClustersNext[rof1] + iNextCluster, tanL, phi, rof0, rof1}; - ++storedTracklets; - } - } - } - } - } - if (storedTracklets > maxTrackletsPerCluster) { - printf("its-gpu-tracklet finder: found more tracklets per clusters (%d) than maximum set (%d), check the configuration!\n", maxTrackletsPerCluster, storedTracklets); - } - } -} - -template -GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, - int* trackletsLookUpTable, - const int nTracklets) -{ - for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { - auto& tracklet{tracklets[currentTrackletIndex]}; - if (tracklet.firstClusterIndex >= 0) { - atomicAdd(trackletsLookUpTable + tracklet.firstClusterIndex, 1); - } - } -} - -template -GPUg() void computeLayerTrackletsKernelMultipleRof( - const int layerIndex, - const int iteration, - const unsigned int startRofId, - const unsigned int rofSize, - const int maxRofs, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxClustersPerRof = 5e2, - const unsigned int maxTrackletsPerCluster = 50) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof0 = iRof + startRofId; - auto nClustersCurrentLayerRof = roFrameClustersCurrentLayer[rof0 + 1] - roFrameClustersCurrentLayer[rof0]; - auto* clustersCurrentLayerRof = clustersCurrentLayer + (roFrameClustersCurrentLayer[rof0] - roFrameClustersCurrentLayer[startRofId]); - auto nVerticesRof0 = nVertices[rof0 + 1] - nVertices[rof0]; - auto trackletsRof0 = tracklets + maxTrackletsPerCluster * maxClustersPerRof * iRof; - for (int currentClusterIndex = threadIdx.x; currentClusterIndex < nClustersCurrentLayerRof; currentClusterIndex += blockDim.x) { - if (nClustersCurrentLayerRof > maxClustersPerRof) { - printf("its-gpu-tracklet finder: on layer %d found more clusters per ROF (%d) than maximum set (%d), check the configuration!\n", layerIndex, nClustersCurrentLayerRof, maxClustersPerRof); - } - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayerRof[currentClusterIndex]}; - const int currentSortedIndex{roFrameClustersCurrentLayer[rof0] + currentClusterIndex}; - const int currentSortedIndexChunk{currentSortedIndex - roFrameClustersCurrentLayer[startRofId]}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; // works with delta = {0, 1} - const float inverseR0{1.f / currentCluster.radius}; - - for (int iPrimaryVertex{0}; iPrimaryVertex < nVerticesRof0; iPrimaryVertex++) { - const auto& primaryVertex{vertices[nVertices[rof0] + iPrimaryVertex]}; - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{std::sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - const int tableSize{phiBins * zBins + 1}; - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - auto nClustersNext{roFrameClustersNextLayer[rof1 + 1] - roFrameClustersNextLayer[rof1]}; - if (!nClustersNext) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= nClustersNext) { - break; - } - auto nextClusterIndex{roFrameClustersNextLayer[rof1] - roFrameClustersNextLayer[startRofId] + iNextCluster}; - const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if ((deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut))) { - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const size_t stride{currentClusterIndex * maxTrackletsPerCluster}; - if (storedTracklets < maxTrackletsPerCluster) { - new (trackletsRof0 + stride + storedTracklets) Tracklet{currentSortedIndexChunk, nextClusterIndex, tanL, phi, static_cast(rof0), static_cast(rof1)}; - } - // else { - // printf("its-gpu-tracklet-finder: on rof %d layer: %d: found more tracklets (%d) than maximum allowed per cluster. This is lossy!\n", rof0, layerIndex, storedTracklets); - // } - ++storedTracklets; - } - } - } - } - } - } - } -} - -// Decrease LUT entries corresponding to duplicated tracklets. NB: duplicate tracklets are removed separately (see const Tracklets*). -GPUg() void removeDuplicateTrackletsEntriesLUTKernel( - int* trackletsLookUpTable, - const Tracklet* tracklets, - const int* nTracklets, - const int layerIndex) -{ - int id0{-1}, id1{-1}; - for (int iTracklet{0}; iTracklet < nTracklets[layerIndex]; ++iTracklet) { - auto& trk = tracklets[iTracklet]; - if (trk.firstClusterIndex == id0 && trk.secondClusterIndex == id1) { - trackletsLookUpTable[id0]--; - } else { - id0 = trk.firstClusterIndex; - id1 = trk.secondClusterIndex; - } - } -} - -// Compute cells kernel -template -GPUg() void computeLayerCellsKernel( - const Tracklet* trackletsCurrentLayer, - const Tracklet* trackletsNextLayer, - const int* trackletsCurrentLayerLUT, - const int nTrackletsCurrent, - Cell* cells, - int* cellsLUT, - const StaticTrackingParameters* trkPars) -{ - for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { - const Tracklet& currentTracklet = trackletsCurrentLayer[iCurrentTrackletIndex]; - const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{trackletsCurrentLayerLUT[nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{trackletsCurrentLayerLUT[nextLayerClusterIndex + 1]}; - if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { - continue; - } - int foundCells{0}; - for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { - if (trackletsNextLayer[iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { - break; - } - const Tracklet& nextTracklet = trackletsNextLayer[iNextTrackletIndex]; - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - const float tanLambda{(currentTracklet.tanLambda + nextTracklet.tanLambda) * 0.5f}; - - if (deltaTanLambda / trkPars->CellDeltaTanLambdaSigma < trkPars->NSigmaCut) { - if constexpr (!initRun) { - new (cells + cellsLUT[iCurrentTrackletIndex] + foundCells) Cell{currentTracklet.firstClusterIndex, nextTracklet.firstClusterIndex, - nextTracklet.secondClusterIndex, - iCurrentTrackletIndex, - iNextTrackletIndex, - tanLambda}; - } - ++foundCells; - } - } - if constexpr (initRun) { - // Fill cell Lookup table - cellsLUT[iCurrentTrackletIndex] = foundCells; - } - } -} - -template -GPUg() void computeLayerCellNeighboursKernel(Cell* cellsCurrentLayer, - Cell* cellsNextLayer, - const int layerIndex, - const int* cellsNextLayerLUT, - int* neighboursLUT, - int* cellNeighbours, - const int* nCells, - const int maxCellNeighbours = 1e2) -{ - for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells[layerIndex]; iCurrentCellIndex += blockDim.x * gridDim.x) { - const Cell& currentCell = cellsCurrentLayer[iCurrentCellIndex]; - const int nextLayerTrackletIndex{currentCell.getSecondTrackletIndex()}; - const int nextLayerFirstCellIndex{cellsNextLayerLUT[nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{cellsNextLayerLUT[nextLayerTrackletIndex + 1]}; - int foundNeighbours{0}; - for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - Cell& nextCell = cellsNextLayer[iNextCell]; - if (nextCell.getFirstTrackletIndex() != nextLayerTrackletIndex) { // Check if cells share the same tracklet - break; - } - if constexpr (initRun) { - atomicAdd(neighboursLUT + iNextCell, 1); - } else { - if (foundNeighbours >= maxCellNeighbours) { - printf("its-gpu-neighbours-finder: on layer: %d: found more neighbours (%d) than maximum allowed per cell, skipping writing. This is lossy!\n", layerIndex, neighboursLUT[iNextCell]); - continue; - } - cellNeighbours[neighboursLUT[iNextCell] + foundNeighbours++] = iCurrentCellIndex; - - const int currentCellLevel{currentCell.getLevel()}; - if (currentCellLevel >= nextCell.getLevel()) { - atomicExch(nextCell.getLevelPtr(), currentCellLevel + 1); - } - } - } - } -} - -} // namespace gpu - -template -void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) -{ - mTimeFrameGPU->initialise(iteration, mTrkParams[iteration], nLayers); -} - -template -void TrackerTraitsGPU::computeLayerTracklets(const int iteration) -{ - if (!mTimeFrameGPU->getClusters().size()) { - return; - } - const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); - gsl::span diamondSpan(&diamondVert, 1); - std::vector threads(mTimeFrameGPU->getNChunks()); - // std::array, nLayers - 1> totTrackletsChunk{std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}, std::array{0, 0, 0}}; - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int maxTracklets{static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity) * - static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster)}; - int maxRofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - // Define workload - auto doTrackReconstruction = [&, chunkId, maxRofPerChunk, iteration]() -> void { - auto offset = chunkId * maxRofPerChunk; - auto maxROF = offset + maxRofPerChunk; - while (offset < maxROF) { - auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - RANGE("chunk_gpu_tracking", 1); - for (int iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - auto nclus = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, iLayer); - const float meanDeltaR{mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]}; - gpu::computeLayerTrackletsKernelMultipleRof<<getStream(chunkId).get()>>>( - iLayer, // const int layerIndex, - iteration, // const int iteration, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - 0, // const unsigned int deltaRof, - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer), // const Cluster* clustersCurrentLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer + 1), // const Cluster* clustersNextLayer, - mTimeFrameGPU->getDeviceROframesClusters(iLayer), // const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(iLayer + 1), // const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(iLayer + 1), // const int* indexTableNextLayer, - mTimeFrameGPU->getDeviceUsedClusters(iLayer), // const int* usedClustersCurrentLayer, - mTimeFrameGPU->getDeviceUsedClusters(iLayer + 1), // const int* usedClustersNextLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), // Tracklet* tracklets, // output data - mTimeFrameGPU->getDeviceVertices(), // const Vertex* vertices, - mTimeFrameGPU->getDeviceROframesPV(), // const int* pvROFrame, - mTimeFrameGPU->getPhiCut(iLayer), // const float phiCut, - mTimeFrameGPU->getMinR(iLayer + 1), // const float minR, - mTimeFrameGPU->getMaxR(iLayer + 1), // const float maxR, - meanDeltaR, // const float meanDeltaR, - mTimeFrameGPU->getPositionResolution(iLayer), // const float positionResolution, - mTimeFrameGPU->getMSangle(iLayer), // const float mSAngle, - mTimeFrameGPU->getDeviceTrackingParameters(), // const StaticTrackingParameters* trkPars, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity, // const int clustersPerROfCapacity, - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster); // const int maxTrackletsPerCluster - - // Remove empty tracklets due to striding. - auto nulltracklet = o2::its::Tracklet{}; - auto thrustTrackletsBegin = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer)); - auto thrustTrackletsEnd = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer) + (int)rofs * maxTracklets); - auto thrustTrackletsAfterEraseEnd = thrust::remove(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - thrustTrackletsBegin, - thrustTrackletsEnd, - nulltracklet); - // Sort tracklets by first cluster index. - thrust::sort(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - thrustTrackletsBegin, - thrustTrackletsAfterEraseEnd, - gpu::trackletSortIndexFunctor()); - - // Remove duplicates. - auto thrustTrackletsAfterUniqueEnd = thrust::unique(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), thrustTrackletsBegin, thrustTrackletsAfterEraseEnd); - - discardResult(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer] = thrustTrackletsAfterUniqueEnd - thrustTrackletsBegin; - // Compute tracklet lookup table. - gpu::compileTrackletsLookupTableKernel<<getStream(chunkId).get()>>>(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_out - nclus, // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - // Create tracklets labels, at the moment on the host - if (mTimeFrameGPU->hasMCinformation()) { - std::vector tracklets(mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - checkGPUError(cudaHostRegister(tracklets.data(), tracklets.size() * sizeof(o2::its::Tracklet), cudaHostRegisterDefault)); - checkGPUError(cudaMemcpyAsync(tracklets.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), tracklets.size() * sizeof(o2::its::Tracklet), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - for (auto& trk : tracklets) { - MCCompLabel label; - int currentId{mTimeFrameGPU->mClusters[iLayer][trk.firstClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - int nextId{mTimeFrameGPU->mClusters[iLayer + 1][trk.secondClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - for (auto& lab1 : mTimeFrameGPU->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrameGPU->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - // TODO: implment label merging. - // mTimeFrameGPU->getTrackletsLabel(iLayer).emplace_back(label); - } - checkGPUError(cudaHostUnregister(tracklets.data())); - } - } - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // Compute layer cells. - gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - nullptr, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - mTimeFrameGPU->getDeviceTrackingParameters()); - - // Compute number of found Cells - checkGPUError(cub::DeviceReduce::Sum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells() + iLayer, // d_out - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - // Compute LUT - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_out - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - mTimeFrameGPU->getDeviceTrackingParameters()); - } - checkGPUError(cudaMemcpyAsync(mTimeFrameGPU->getHostNCells(chunkId).data(), - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - (nLayers - 2) * sizeof(int), - cudaMemcpyDeviceToHost, - mTimeFrameGPU->getStream(chunkId).get())); - // Create cells labels TODO: make it work after fixing the tracklets labels - if (mTimeFrameGPU->hasMCinformation()) { - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - std::vector cells(mTimeFrameGPU->getHostNCells(chunkId)[iLayer]); - // Async with not registered memory? - checkGPUError(cudaMemcpyAsync(cells.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), mTimeFrameGPU->getHostNCells(chunkId)[iLayer] * sizeof(o2::its::Cell), cudaMemcpyDeviceToHost)); - for (auto& cell : cells) { - MCCompLabel currentLab{mTimeFrameGPU->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - MCCompLabel nextLab{mTimeFrameGPU->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; - mTimeFrameGPU->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); - } - } - } - - for (int iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - iLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - nullptr, - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // Compute Cell Neighbours LUT - checkGPUError(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_in - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_out - mTimeFrameGPU->getHostNCells(chunkId)[iLayer + 1], // num_items - mTimeFrameGPU->getStream(chunkId).get())); - - gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - iLayer, - mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - // if (!chunkId) { - // gpu::printBufferLayerOnThread<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize * rofs); - // } - } - - // End of tracking for this chunk - offset += rofs; - } - }; - threads[chunkId] = std::thread(doTrackReconstruction); - } - for (auto& thread : threads) { - thread.join(); - } - - mTimeFrameGPU->wipe(nLayers); -} - -template -void TrackerTraitsGPU::computeLayerCells(const int iteration) -{ -} - -// void TrackerTraitsGPU::refitTracks(const std::vector>& tf, std::vector& tracks) -// { -// PrimaryVertexContextNV* pvctx = static_cast(nullptr); //TODO: FIX THIS with Time Frames -// std::array cells; -// for (int iLayer = 0; iLayer < 5; iLayer++) { -// cells[iLayer] = pvctx->getDeviceCells()[iLayer].get(); -// } -// std::array clusters; -// for (int iLayer = 0; iLayer < 7; iLayer++) { -// clusters[iLayer] = pvctx->getDeviceClusters()[iLayer].get(); -// } -// //TODO: restore this -// // mChainRunITSTrackFit(*mChain, mPrimaryVertexContext->getRoads(), clusters, cells, tf, tracks); -// } - -template -void TrackerTraitsGPU::findCellsNeighbours(const int iteration){}; - -template -void TrackerTraitsGPU::findRoads(const int iteration){}; - -template -void TrackerTraitsGPU::findTracks(){}; - -template -void TrackerTraitsGPU::extendTracks(const int iteration){}; - -template -void TrackerTraitsGPU::setBz(float bz) -{ - mBz = bz; - mTimeFrameGPU->setBz(bz); -} - -template -int TrackerTraitsGPU::getTFNumberOfClusters() const -{ - return mTimeFrameGPU->getNumberOfClusters(); -} - -template -int TrackerTraitsGPU::getTFNumberOfTracklets() const -{ - return mTimeFrameGPU->getNumberOfTracklets(); -} - -template -int TrackerTraitsGPU::getTFNumberOfCells() const -{ - return mTimeFrameGPU->getNumberOfCells(); -} - -template class TrackerTraitsGPU<7>; -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx new file mode 100644 index 0000000000000..42d2227de60f8 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -0,0 +1,448 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" + +#include "ITStrackingGPU/TrackerTraitsGPU.h" +#include "ITStrackingGPU/TrackingKernels.h" +#include "ITStracking/TrackingConfigParam.h" +#include "ITStracking/Constants.h" + +namespace o2::its +{ + +template +void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) +{ + mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], nLayers); + // on default stream + mTimeFrameGPU->loadVertices(iteration); + mTimeFrameGPU->loadIndexTableUtils(iteration); + mTimeFrameGPU->loadMultiplicityCutMask(iteration); + // pinned on host + mTimeFrameGPU->createUsedClustersDeviceArray(iteration); + mTimeFrameGPU->createClustersDeviceArray(iteration); + mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration); + mTimeFrameGPU->createClustersIndexTablesArray(iteration); + mTimeFrameGPU->createTrackingFrameInfoDeviceArray(iteration); + mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); + // device array + mTimeFrameGPU->createTrackletsLUTDeviceArray(iteration); + mTimeFrameGPU->createTrackletsBuffersArray(iteration); + mTimeFrameGPU->createCellsBuffersArray(iteration); + mTimeFrameGPU->createCellsLUTDeviceArray(iteration); + // push every create artefact on the stack + mTimeFrameGPU->pushMemoryStack(iteration); +} + +template +void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) +{ + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); +} + +template +void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) +{ + const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + + int startROF{0}; + int endROF{mTimeFrameGPU->getNrof()}; + + // start by queuing loading needed of two last layers + for (int iLayer{nLayers}; iLayer-- > nLayers - 2;) { + mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); + mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer); + mTimeFrameGPU->recordEvent(iLayer); + } + + for (int iLayer{this->mTrkParams[iteration].TrackletsPerRoad()}; iLayer--;) { + if (iLayer) { // queue loading data of next layer in parallel, this the copies are overlapping with computation kernels + mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->loadClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer - 1); + mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->recordEvent(iLayer - 1); + } + mTimeFrameGPU->createTrackletsLUTDevice(iteration, iLayer); + mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + iLayer, + startROF, + endROF, + mTimeFrameGPU->getNrof(), + this->mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + iteration, + this->mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + this->mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + this->mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerTracklets[iteration], + conf.nThreadsLayerTracklets[iteration], + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createTrackletsBuffers(iLayer); + if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { + continue; + } + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + iLayer, + startROF, + endROF, + mTimeFrameGPU->getNrof(), + this->mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceTracklets(), + mTimeFrameGPU->getNTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + iteration, + this->mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + this->mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + this->mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerTracklets[iteration], + conf.nThreadsLayerTracklets[iteration], + mTimeFrameGPU->getStreams()); + } +} + +template +void TrackerTraitsGPU::computeLayerCells(const int iteration) +{ + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + + // start by queuing loading needed of three last layers + for (int iLayer{nLayers}; iLayer-- > nLayers - 3;) { + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer); + mTimeFrameGPU->recordEvent(iLayer); + } + + for (int iLayer{this->mTrkParams[iteration].CellsPerRoad()}; iLayer--;) { + if (iLayer) { + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer - 1); + mTimeFrameGPU->recordEvent(iLayer - 1); + } + + // if there are no tracklets skip entirely + const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[iLayer])}; + if (!currentLayerTrackletsNum || !mTimeFrameGPU->getNTracklets()[iLayer + 1]) { + mTimeFrameGPU->getNCells()[iLayer] = 0; + continue; + } + + mTimeFrameGPU->createCellsLUTDevice(iLayer); + mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available + mTimeFrameGPU->waitEvent(iLayer, iLayer + 2); // wait stream until all data is available + countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + iLayer, + nullptr, + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + this->mTrkParams[iteration].DeltaROF, + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksLayerCells[iteration], + conf.nThreadsLayerCells[iteration], + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createCellsBuffers(iLayer); + if (mTimeFrameGPU->getNCells()[iLayer] == 0) { + continue; + } + computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + currentLayerTrackletsNum, + iLayer, + mTimeFrameGPU->getDeviceCells()[iLayer], + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceCellLUTs()[iLayer], + this->mTrkParams[iteration].DeltaROF, + this->mBz, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].CellDeltaTanLambdaSigma, + this->mTrkParams[iteration].NSigmaCut, + conf.nBlocksLayerCells[iteration], + conf.nThreadsLayerCells[iteration], + mTimeFrameGPU->getStreams()); + } +} + +template +void TrackerTraitsGPU::findCellsNeighbours(const int iteration) +{ + const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + + for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { + const int currentLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer])}; + const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; + if (!nextLayerCellsNum || !currentLayerCellsNum) { + mTimeFrameGPU->getNNeighbours()[iLayer] = 0; + continue; + } + mTimeFrameGPU->createNeighboursIndexTablesDevice(iLayer); + mTimeFrameGPU->createNeighboursLUTDevice(iLayer, nextLayerCellsNum); + countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), + mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), + (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), + this->mTrkParams[0].DeltaROF, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mBz, + iLayer, + currentLayerCellsNum, + nextLayerCellsNum, + 1e2, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksFindNeighbours[iteration], + conf.nThreadsFindNeighbours[iteration], + mTimeFrameGPU->getStream(iLayer)); + mTimeFrameGPU->createNeighboursDevice(iLayer); + if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { + continue; + } + computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), + mTimeFrameGPU->getDeviceArrayCellsLUT(), + mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), + mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), + (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), + this->mTrkParams[0].DeltaROF, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mBz, + iLayer, + currentLayerCellsNum, + nextLayerCellsNum, + 1e2, + conf.nBlocksFindNeighbours[iteration], + conf.nThreadsFindNeighbours[iteration], + mTimeFrameGPU->getStream(iLayer)); + mTimeFrameGPU->getArrayNNeighbours()[iLayer] = filterCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), + mTimeFrameGPU->getDeviceNeighbours(iLayer), + mTimeFrameGPU->getArrayNNeighbours()[iLayer], + mTimeFrameGPU->getStream(iLayer), + mTimeFrameGPU->getFrameworkAllocator()); + } + mTimeFrameGPU->syncStreams(false); +} + +template +void TrackerTraitsGPU::findRoads(const int iteration) +{ + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { + const int minimumLayer{startLevel - 1}; + bounded_vector> trackSeeds(this->getMemoryPool().get()); + for (int startLayer{this->mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { + if ((this->mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { + continue; + } + processNeighboursHandler(startLayer, + startLevel, + mTimeFrameGPU->getDeviceArrayCells(), + mTimeFrameGPU->getDeviceCells()[startLayer], + mTimeFrameGPU->getArrayNCells(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceNeighboursAll(), + mTimeFrameGPU->getDeviceNeighboursLUTs(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + trackSeeds, + this->mBz, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[0].MaxChi2NDF, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksProcessNeighbours[iteration], + conf.nThreadsProcessNeighbours[iteration]); + } + // fixme: I don't want to move tracks back and forth, but I need a way to use a thrust::allocator that is aware of our managed memory. + if (trackSeeds.empty()) { + LOGP(debug, "No track seeds found, skipping track finding"); + continue; + } + mTimeFrameGPU->loadTrackSeedsDevice(trackSeeds); + + // Since TrackITSExt is an enourmous class it is better to first count how many + // successfull fits we do and only then allocate + countTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + trackSeeds.size(), + this->mBz, + startLevel, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[0].MaxChi2NDF, + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksTracksSeeds[iteration], + conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->createTrackITSExtDevice(trackSeeds.size()); + computeTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), + mTimeFrameGPU->getDeviceTrackITSExt(), + mTimeFrameGPU->getDeviceTrackSeedsLUT(), + this->mTrkParams[iteration].LayerRadii, + this->mTrkParams[iteration].MinPt, + trackSeeds.size(), + mTimeFrameGPU->getNTrackSeeds(), + this->mBz, + startLevel, + this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[0].MaxChi2NDF, + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksTracksSeeds[iteration], + conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->downloadTrackITSExtDevice(); + + auto& tracks = mTimeFrameGPU->getTrackITSExt(); + + for (auto& track : tracks) { + if (!track.getChi2()) { + continue; // this is to skip the unset tracks that are put at the beginning of the vector by the sorting. To see if this can be optimised. + } + int nShared = 0; + bool isFirstShared{false}; + for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + nShared += int(mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); + isFirstShared |= !iLayer && mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + } + + if (nShared > this->mTrkParams[0].ClusterSharing) { + continue; + } + + std::array rofs{INT_MAX, INT_MAX, INT_MAX}; + for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + mTimeFrameGPU->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); + int currentROF = mTimeFrameGPU->getClusterROF(iLayer, track.getClusterIndex(iLayer)); + for (int iR{0}; iR < 3; ++iR) { + if (rofs[iR] == INT_MAX) { + rofs[iR] = currentROF; + } + if (rofs[iR] == currentROF) { + break; + } + } + } + if (rofs[2] != INT_MAX) { + continue; + } + if (rofs[1] != INT_MAX) { + track.setNextROFbit(); + } + mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + } + mTimeFrameGPU->loadUsedClustersDevice(); + } + // wipe the artefact memory + mTimeFrameGPU->popMemoryStack(iteration); +}; + +template +int TrackerTraitsGPU::getTFNumberOfClusters() const +{ + return mTimeFrameGPU->getNumberOfClusters(); +} + +template +int TrackerTraitsGPU::getTFNumberOfTracklets() const +{ + return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); +} + +template +int TrackerTraitsGPU::getTFNumberOfCells() const +{ + return mTimeFrameGPU->getNumberOfCells(); +} + +template +void TrackerTraitsGPU::setBz(float bz) +{ + this->mBz = bz; + mTimeFrameGPU->setBz(bz); +} + +template class TrackerTraitsGPU<7>; +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu new file mode 100644 index 0000000000000..eacf514c7a91d --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -0,0 +1,1523 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ITStracking/Constants.h" +#include "ITStracking/Definitions.h" +#include "ITStracking/IndexTableUtils.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/Tracklet.h" +#include "ITStracking/Cluster.h" +#include "ITStracking/Cell.h" +#include "DataFormatsITS/TrackITS.h" +#include "ITStrackingGPU/TrackingKernels.h" +#include "ITStrackingGPU/Utils.h" +#include "utils/strtag.h" + +// O2 track model +#include "ReconstructionDataFormats/Track.h" +#include "DetectorsBase/Propagator.h" +using namespace o2::track; + +namespace o2::its +{ +namespace gpu +{ + +GPUdii() bool fitTrack(TrackITSExt& track, + int start, + int end, + int step, + float chi2clcut, + float chi2ndfcut, + float maxQoverPt, + int nCl, + float bz, + const TrackingFrameInfo** tfInfos, + const o2::base::Propagator* prop, + o2::base::PropagatorF::MatCorrType matCorrType, + o2::track::TrackPar* linRef, + const bool shiftRefToCluster) +{ + for (int iLayer{start}; iLayer != end; iLayer += step) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { + continue; + } + const TrackingFrameInfo& trackingHit = tfInfos[iLayer][track.getClusterIndex(iLayer)]; + if (linRef) { + if (!track.o2::track::TrackParCovF::rotate(trackingHit.alphaTrackingFrame, *linRef, bz)) { + return false; + } + if (!prop->propagateToX(track, + *linRef, + trackingHit.xTrackingFrame, + bz, + o2::base::PropagatorImpl::MAX_SIN_PHI, + o2::base::PropagatorImpl::MAX_STEP, + matCorrType)) { + + return false; + } + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + const float xx0 = (iLayer > 2) ? 1.e-2f : 5.e-3f; // Rough layer thickness + if (!track.correctForMaterial(*linRef, xx0, xx0 * constants::Radl * constants::Rho, true)) { + return false; + } + } + } else { + if (!track.o2::track::TrackParCovF::rotate(trackingHit.alphaTrackingFrame)) { + return false; + } + if (!prop->propagateToX(track, + trackingHit.xTrackingFrame, + bz, + o2::base::PropagatorImpl::MAX_SIN_PHI, + o2::base::PropagatorImpl::MAX_STEP, + matCorrType)) { + return false; + } + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + const float xx0 = (iLayer > 2) ? 1.e-2f : 5.e-3f; // Rough layer thickness + if (!track.correctForMaterial(xx0, xx0 * constants::Radl * constants::Rho, true)) { + return false; + } + } + } + + auto predChi2{track.getPredictedChi2(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if ((nCl >= 3 && predChi2 > chi2clcut) || predChi2 < 0.f) { + return false; + } + track.setChi2(track.getChi2() + predChi2); + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + return false; + } + if (linRef && shiftRefToCluster) { // displace the reference to the last updated cluster + linRef->setY(trackingHit.positionTrackingFrame[0]); + linRef->setZ(trackingHit.positionTrackingFrame[1]); + } + nCl++; + } + return o2::gpu::CAMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); +} + +GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, + const Cluster& cluster2, + const TrackingFrameInfo& tf3, + const float bz, + const bool reverse = false) +{ + const float sign = reverse ? -1.f : 1.f; + + float ca, sa; + o2::gpu::CAMath::SinCos(tf3.alphaTrackingFrame, sa, ca); + + const float x1 = cluster1.xCoordinate * ca + cluster1.yCoordinate * sa; + const float y1 = -cluster1.xCoordinate * sa + cluster1.yCoordinate * ca; + const float x2 = cluster2.xCoordinate * ca + cluster2.yCoordinate * sa; + const float y2 = -cluster2.xCoordinate * sa + cluster2.yCoordinate * ca; + const float x3 = tf3.xTrackingFrame; + const float y3 = tf3.positionTrackingFrame[0]; + + float snp, q2pt, q2pt2; + if (o2::gpu::CAMath::Abs(bz) < 0.01f) { + const float tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); + snp = sign * tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + q2pt = sign / track::kMostProbablePt; + q2pt2 = 1.f; + } else { + const float crv = math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); + snp = sign * crv * (x3 - math_utils::computeCurvatureCentreX(x3, y3, x2, y2, x1, y1)); + q2pt = sign * crv / (bz * o2::constants::math::B2C); + q2pt2 = crv * crv; + } + + const float tgl = 0.5f * (math_utils::computeTanDipAngle(x1, y1, x2, y2, cluster1.zCoordinate, cluster2.zCoordinate) + + math_utils::computeTanDipAngle(x2, y2, x3, y3, cluster2.zCoordinate, tf3.positionTrackingFrame[1])); + const float sg2q2pt = track::kC1Pt2max * (q2pt2 > 0.0005f ? (q2pt2 < 1.f ? q2pt2 : 1.f) : 0.0005f); + + return {x3, tf3.alphaTrackingFrame, {y3, tf3.positionTrackingFrame[1], snp, tgl, q2pt}, {tf3.covarianceTrackingFrame[0], tf3.covarianceTrackingFrame[1], tf3.covarianceTrackingFrame[2], 0.f, 0.f, track::kCSnp2max, 0.f, 0.f, 0.f, track::kCTgl2max, 0.f, 0.f, 0.f, 0.f, sg2q2pt}}; +} + +template +GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const float* layerRadii, + const float bz, + const int reseedIfShorter) +{ + TrackITSExt temporaryTrack(seed); + int lrMin = nLayers, lrMax = 0, lrMid = 0; + for (int iL{0}; iL < nLayers; ++iL) { + const int idx = seed.getCluster(iL); + temporaryTrack.setExternalClusterIndex(iL, idx, idx != constants::UnusedIndex); + if (idx != constants::UnusedIndex) { + // TODO only works if does not have holes + lrMin = o2::gpu::CAMath::Min(lrMin, iL); + lrMax = o2::gpu::CAMath::Max(lrMax, iL); + } + } + const int ncl = temporaryTrack.getNClusters(); + if (ncl < reseedIfShorter && ncl > 0) { // need to check if there are any clusters since we keep invalidate seeeds around + if (ncl == nLayers) { + lrMin = 0; + lrMax = nLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + lrMid = lrMin + 1; + float midR = 0.5f * (layerRadii[lrMax] + layerRadii[lrMin]), dstMidR = o2::gpu::CAMath::Abs(midR - layerRadii[lrMid]); + for (int iL = lrMid + 1; iL < lrMax; ++iL) { // find the midpoint as closest to the midR + auto dst = o2::gpu::GPUCommonMath::Abs(midR - layerRadii[iL]); + if (dst < dstMidR) { + lrMid = iL; + dstMidR = dst; + } + } + } + const auto& cluster0_tf = foundTrackingFrameInfo[lrMin][seed.getCluster(lrMin)]; + const auto& cluster1_gl = unsortedClusters[lrMid][seed.getCluster(lrMid)]; + const auto& cluster2_gl = unsortedClusters[lrMax][seed.getCluster(lrMax)]; + temporaryTrack.getParamIn() = buildTrackSeed(cluster2_gl, cluster1_gl, cluster0_tf, bz, true); + } + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + return temporaryTrack; +} + +struct sort_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) + { + if (a.firstClusterIndex != b.firstClusterIndex) { + return a.firstClusterIndex < b.firstClusterIndex; + } + return a.secondClusterIndex < b.secondClusterIndex; + } +}; + +struct equal_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; } +}; + +template +struct sort_by_second { + GPUhd() bool operator()(const gpuPair& a, const gpuPair& b) const { return a.second < b.second; } +}; + +template +struct pair_to_first { + GPUhd() int operator()(const gpuPair& a) const + { + return a.first; + } +}; + +template +struct pair_to_second { + GPUhd() int operator()(const gpuPair& a) const + { + return a.second; + } +}; + +template +struct is_invalid_pair { + GPUhd() bool operator()(const gpuPair& p) const + { + return p.first == -1 && p.second == -1; + } +}; + +template +struct is_valid_pair { + GPUhd() bool operator()(const gpuPair& p) const + { + return !(p.first == -1 && p.second == -1); + } +}; + +template +struct seed_selector { + float maxQ2Pt; + float maxChi2; + + GPUhd() seed_selector(float maxQ2Pt, float maxChi2) : maxQ2Pt(maxQ2Pt), maxChi2(maxChi2) {} + GPUhd() bool operator()(const CellSeed& seed) const + { + return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2); + } +}; + +struct compare_track_chi2 { + GPUhd() bool operator()(const TrackITSExt& a, const TrackITSExt& b) const + { + return a.getChi2() < b.getChi2(); + } +}; + +template +GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( + CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + maybe_const* seedLUT, + const float* layerRadii, + const float* minPts, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shifRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) +{ + for (int iCurrentTrackSeedIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackSeedIndex < nSeeds; iCurrentTrackSeedIndex += blockDim.x * gridDim.x) { + + if constexpr (!initRun) { + if (seedLUT[iCurrentTrackSeedIndex] == seedLUT[iCurrentTrackSeedIndex + 1]) { + continue; + } + } + + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); + o2::track::TrackPar linRef{temporaryTrack}; + bool fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, + 0, // int lastLayer, + nLayers, // int firstLayer, + 1, // int firstCluster, + maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, + maxChi2NDF, // float maxChi2NDF, + o2::constants::math::VeryBig, // float maxQoverPt, + 0, // nCl, + bz, // float bz, + foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, + propagator, // const o2::base::Propagator* propagator, + matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType + &linRef, + shifRefToCluster); + if (!fitSuccess) { + continue; + } + temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); + linRef = temporaryTrack.getParamOut(); // use refitted track as lin.reference + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + temporaryTrack.setChi2(0); + fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, + nLayers - 1, // int lastLayer, + -1, // int firstLayer, + -1, // int firstCluster, + maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, + maxChi2NDF, // float maxChi2NDF, + 50.f, // float maxQoverPt, + 0, // nCl, + bz, // float bz, + foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, + propagator, // const o2::base::Propagator* propagator, + matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType + &linRef, + shifRefToCluster); + if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { + continue; + } + if (repeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result + o2::track::TrackParCov saveInw{temporaryTrack}; + linRef = saveInw; // use refitted track as lin.reference + float saveChi2 = temporaryTrack.getChi2(); + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + temporaryTrack.setChi2(0); + fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, + 0, // int lastLayer, + nLayers, // int firstLayer, + 1, // int firstCluster, + maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, + maxChi2NDF, // float maxChi2NDF, + o2::constants::math::VeryBig, // float maxQoverPt, + 0, // nCl, + bz, // float bz, + foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, + propagator, // const o2::base::Propagator* propagator, + matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType + &linRef, + shifRefToCluster); + if (!fitSuccess) { + continue; + } + temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); + temporaryTrack.getParamIn() = saveInw; + temporaryTrack.setChi2(saveChi2); + } + + if constexpr (initRun) { + seedLUT[iCurrentTrackSeedIndex] = 1; + } else { + tracks[seedLUT[iCurrentTrackSeedIndex]] = temporaryTrack; + } + } +} + +template +GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( + CellSeed** cellSeedArray, + int* neighboursLUT, + int* neighboursIndexTable, + int** cellsLUTs, + gpuPair* cellNeighbours, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const int maxCellNeighbours = 1e2) +{ + for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells; iCurrentCellIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (neighboursIndexTable[iCurrentCellIndex] == neighboursIndexTable[iCurrentCellIndex + 1]) { + continue; + } + } + const auto& currentCellSeed{cellSeedArray[layerIndex][iCurrentCellIndex]}; + const int nextLayerTrackletIndex{currentCellSeed.getSecondTrackletIndex()}; + const int nextLayerFirstCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex]}; + const int nextLayerLastCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex + 1]}; + int foundNeighbours{0}; + for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { + auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { // Check if cells share the same tracklet + break; + } + + if (deltaROF) { + const auto& trkl00 = tracklets[layerIndex][currentCellSeed.getFirstTrackletIndex()]; + const auto& trkl01 = tracklets[layerIndex + 1][currentCellSeed.getSecondTrackletIndex()]; + const auto& trkl10 = tracklets[layerIndex + 1][nextCellSeed.getFirstTrackletIndex()]; + const auto& trkl11 = tracklets[layerIndex + 2][nextCellSeed.getSecondTrackletIndex()]; + if ((o2::gpu::CAMath::Max(trkl00.getMaxRof(), o2::gpu::CAMath::Max(trkl01.getMaxRof(), o2::gpu::CAMath::Max(trkl10.getMaxRof(), trkl11.getMaxRof()))) - + o2::gpu::CAMath::Min(trkl00.getMinRof(), o2::gpu::CAMath::Min(trkl01.getMinRof(), o2::gpu::CAMath::Min(trkl10.getMinRof(), trkl11.getMinRof())))) > deltaROF) { + continue; + } + } + + if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || + !nextCellSeed.propagateTo(currentCellSeed.getX(), bz)) { + continue; + } + + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); + if (chi2 > maxChi2ClusterAttachment) /// TODO: switch to the chi2 wrt cluster to avoid correlation + { + continue; + } + + if constexpr (initRun) { + atomicAdd(neighboursLUT + iNextCell, 1); + neighboursIndexTable[iCurrentCellIndex]++; + } else { + cellNeighbours[neighboursIndexTable[iCurrentCellIndex] + foundNeighbours] = {iCurrentCellIndex, iNextCell}; + foundNeighbours++; + const int currentCellLevel{currentCellSeed.getLevel()}; + if (currentCellLevel >= nextCellSeed.getLevel()) { + atomicMax(cellSeedArray[layerIndex + 1][iNextCell].getLevelPtr(), currentCellLevel + 1); + } + } + } + } +} + +template +GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTrackletsCurrent, + const int layer, + CellSeed* cells, + int** cellsLUTs, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut) +{ + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // FIXME: Hardcoded here for the moment. + for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { + if constexpr (!initRun) { + if (cellsLUTs[layer][iCurrentTrackletIndex] == cellsLUTs[layer][iCurrentTrackletIndex + 1]) { + continue; + } + } + const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; + const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; + const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex + 1]}; + if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { + continue; + } + int foundCells{0}; + for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { + if (tracklets[layer + 1][iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { + break; + } + const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; + if (deltaROF && currentTracklet.getSpanRof(nextTracklet) > deltaROF) { + continue; + } + const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + + if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { + const int clusId[3]{ + sortedClusters[layer][currentTracklet.firstClusterIndex].clusterId, + sortedClusters[layer + 1][nextTracklet.firstClusterIndex].clusterId, + sortedClusters[layer + 2][nextTracklet.secondClusterIndex].clusterId}; + + const auto& cluster1_glo = unsortedClusters[layer][clusId[0]]; + const auto& cluster2_glo = unsortedClusters[layer + 1][clusId[1]]; + const auto& cluster3_tf = tfInfo[layer + 2][clusId[2]]; + auto track{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf, bz)}; + float chi2{0.f}; + bool good{false}; + for (int iC{2}; iC--;) { + const TrackingFrameInfo& trackingHit = tfInfo[layer + iC][clusId[iC]]; + if (!track.rotate(trackingHit.alphaTrackingFrame)) { + break; + } + if (!track.propagateTo(trackingHit.xTrackingFrame, bz)) { + break; + } + + if (!track.correctForMaterial(layerxX0[layer + iC], layerxX0[layer + iC] * constants::Radl * constants::Rho, true)) { + break; + } + + const auto predChi2{track.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; + if (!track.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { + break; + } + if (!iC && predChi2 > maxChi2ClusterAttachment) { + break; + } + good = !iC; + chi2 += predChi2; + } + if (!good) { + continue; + } + if constexpr (!initRun) { + new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; + } + ++foundCells; + } + } + if constexpr (initRun) { + cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; + } + } +} + +template +GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, + const uint8_t* multMask, + const int layerIndex, + const int startROF, + const int endROF, + const int totalROFs, + const int deltaROF, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const int vertexId, + const Cluster** clusters, // Input data rof0 + const int** ROFClusters, // Number of clusters on layers per ROF + const unsigned char** usedClusters, // Used clusters + const int** indexTables, // Input data rof0-delta getNphiBins()}; + const int zBins{utils->getNzBins()}; + const int tableSize{phiBins * zBins + 1}; + for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { + const short pivotROF = iROF + startROF; + const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(pivotROF - deltaROF)); + const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(pivotROF + deltaROF)); + auto primaryVertices = getPrimaryVertices(minROF, maxROF, rofPV, totalROFs, vertices); + if (primaryVertices.empty()) { + continue; + } + const auto startVtx{vertexId >= 0 ? vertexId : 0}; + const auto endVtx{vertexId >= 0 ? o2::gpu::CAMath::Min(vertexId + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; + if ((endVtx - startVtx) <= 0) { + continue; + } + + auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs, layerIndex, ROFClusters, clusters); + if (clustersCurrentLayer.empty()) { + continue; + } + + for (int currentClusterIndex = threadIdx.x; currentClusterIndex < clustersCurrentLayer.size(); currentClusterIndex += blockDim.x) { + + unsigned int storedTracklets{0}; + const auto& currentCluster{clustersCurrentLayer[currentClusterIndex]}; + const int currentSortedIndex{ROFClusters[layerIndex][pivotROF] + currentClusterIndex}; + if (usedClusters[layerIndex][currentCluster.clusterId]) { + continue; + } + if constexpr (!initRun) { + if (trackletsLUT[layerIndex][currentSortedIndex] == trackletsLUT[layerIndex][currentSortedIndex + 1]) { + continue; + } + } + + const float inverseR0{1.f / currentCluster.radius}; + for (int iV{startVtx}; iV < endVtx; ++iV) { + auto& primaryVertex{primaryVertices[iV]}; + if ((primaryVertex.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !primaryVertex.isFlagSet(Vertex::Flags::UPCMode))) { + continue; + } + + const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(resolutionPV) / primaryVertex.getNContributors() + math_utils::Sq(positionResolution)); + const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; + const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; + const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + constants::Tolerance)}; /// protecting from overflows adding the detector resolution + const float sigmaZ{o2::gpu::CAMath::Sqrt(math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInverseDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * MSAngle))}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex + 1, utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; + if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { + continue; + } + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; + + if (phiBinsNum < 0) { + phiBinsNum += phiBins; + } + + for (short targetROF{minROF}; targetROF <= maxROF; ++targetROF) { + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs, layerIndex + 1, ROFClusters, clusters); + if (clustersNextLayer.empty()) { + continue; + } + for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { + int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; + const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; + const int firstRowClusterIndex = indexTables[layerIndex + 1][(targetROF)*tableSize + firstBinIndex]; + const int maxRowClusterIndex = indexTables[layerIndex + 1][(targetROF)*tableSize + maxBinIndex]; + for (int nextClusterIndex{firstRowClusterIndex}; nextClusterIndex < maxRowClusterIndex; ++nextClusterIndex) { + if (nextClusterIndex >= clustersNextLayer.size()) { + break; + } + const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; + if (usedClusters[layerIndex + 1][nextCluster.clusterId]) { + continue; + } + const float deltaPhi{o2::gpu::CAMath::Abs(currentCluster.phi - nextCluster.phi)}; + const float deltaZ{o2::gpu::CAMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + if (deltaZ / sigmaZ < NSigmaCut && (deltaPhi < phiCut || o2::gpu::CAMath::Abs(deltaPhi - o2::constants::math::TwoPI) < phiCut)) { + if constexpr (initRun) { + trackletsLUT[layerIndex][currentSortedIndex]++; // we need l0 as well for usual exclusive sums. + } else { + const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; + const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; + const int nextSortedIndex{ROFClusters[layerIndex + 1][targetROF] + nextClusterIndex}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, pivotROF, targetROF}; + } + ++storedTracklets; + } + } + } + } + } + } + } +} + +GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( + const Tracklet* tracklets, + int* trackletsLookUpTable, + const int nTracklets) +{ + for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { + atomicAdd(&trackletsLookUpTable[tracklets[currentTrackletIndex].firstClusterIndex], 1); + } +} + +template +GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( + const int layer, + const int level, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + const int* currentCellIds, + const unsigned int nCurrentCells, + CellSeed* updatedCellSeeds, + int* updatedCellsIds, + int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration + const unsigned char** usedClusters, // Used clusters + int* neighbours, + int* neighboursLUT, + const TrackingFrameInfo** foundTrackingFrameInfo, + const float bz, + const float maxChi2ClusterAttachment, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType) +{ + constexpr float layerxX0[7] = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; // FIXME: Hardcoded here for the moment. + for (unsigned int iCurrentCell = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCell < nCurrentCells; iCurrentCell += blockDim.x * gridDim.x) { + if constexpr (!dryRun) { + if (foundSeedsTable[iCurrentCell] == foundSeedsTable[iCurrentCell + 1]) { + continue; + } + } + int foundSeeds{0}; + const auto& currentCell{currentCellSeeds[iCurrentCell]}; + if (currentCell.getLevel() != level) { + continue; + } + if (currentCellIds == nullptr && (usedClusters[layer][currentCell.getFirstClusterIndex()] || + usedClusters[layer + 1][currentCell.getSecondClusterIndex()] || + usedClusters[layer + 2][currentCell.getThirdClusterIndex()])) { + continue; + } + const int cellId = currentCellIds == nullptr ? iCurrentCell : currentCellIds[iCurrentCell]; + + const int startNeighbourId{cellId ? neighboursLUT[cellId - 1] : 0}; + const int endNeighbourId{neighboursLUT[cellId]}; + + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const int neighbourCellId = neighbours[iNeighbourCell]; + const auto& neighbourCell = allCellSeeds[layer - 1][neighbourCellId]; + + if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { + continue; + } + if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + continue; + } + if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { + continue; + } + auto seed{currentCell}; + auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; + + if (!seed.rotate(trHit.alphaTrackingFrame)) { + continue; + } + + if (!propagator->propagateToX(seed, trHit.xTrackingFrame, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorrType)) { + continue; + } + + if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { + if (!seed.correctForMaterial(layerxX0[layer - 1], layerxX0[layer - 1] * constants::Radl * constants::Rho, true)) { + continue; + } + } + + auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; + if ((predChi2 > maxChi2ClusterAttachment) || predChi2 < 0.f) { + continue; + } + seed.setChi2(seed.getChi2() + predChi2); + if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { + continue; + } + if constexpr (dryRun) { + foundSeedsTable[iCurrentCell]++; + } else { + seed.getClusters()[layer - 1] = neighbourCell.getFirstClusterIndex(); + seed.setLevel(neighbourCell.getLevel()); + seed.setFirstTrackletIndex(neighbourCell.getFirstTrackletIndex()); + seed.setSecondTrackletIndex(neighbourCell.getSecondTrackletIndex()); + updatedCellsIds[foundSeedsTable[iCurrentCell] + foundSeeds] = neighbourCellId; + updatedCellSeeds[foundSeedsTable[iCurrentCell] + foundSeeds] = seed; + } + foundSeeds++; + } + } +} + +} // namespace gpu + +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams) +{ + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + multMask, + layer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + nullptr, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[layer], + resolutionPV, + minRs[layer + 1], + maxRs[layer + 1], + resolutions[layer], + radii[layer + 1] - radii[layer], + mulScatAng[layer]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); +} + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams) +{ + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + multMask, + layer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + tracklets, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[layer], + resolutionPV, + minRs[layer + 1], + maxRs[layer + 1], + resolutions[layer], + radii[layer + 1] - radii[layer], + mulScatAng[layer]); + thrust::device_ptr tracklets_ptr(spanTracklets[layer]); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); + thrust::sort(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[layer], gpu::sort_tracklets()); + auto unique_end = thrust::unique(nosync_policy, tracklets_ptr, tracklets_ptr + nTracklets[layer], gpu::equal_tracklets()); + nTracklets[layer] = unique_end - tracklets_ptr; + if (layer) { + GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[layer], 0, (nClusters[layer] + 1) * sizeof(int), streams[layer].get())); + gpu::compileTrackletsLookupTableKernel<<>>( + spanTracklets[layer], + trackletsLUTsHost[layer], + nTracklets[layer]); + thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); + } +} + +template +void countCellsHandler( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams) +{ + gpu::computeLayerCellsKernel<<>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + layer, // const int + cells, // CellSeed* + cellsLUTsArrayDevice, // int** + deltaROF, // const int + bz, // const float + maxChi2ClusterAttachment, // const float + cellDeltaTanLambdaSigma, // const float + nSigmaCut); // const float + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(streams[layer].get()); + thrust::exclusive_scan(nosync_policy, cellsLUTsHost, cellsLUTsHost + nTracklets + 1, cellsLUTsHost); +} + +template +void computeCellsHandler( + const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const int nBlocks, + const int nThreads, + gpu::Streams& streams) +{ + gpu::computeLayerCellsKernel<<>>( + sortedClusters, // const Cluster** + unsortedClusters, // const Cluster** + tfInfo, // const TrackingFrameInfo** + tracklets, // const Tracklets** + trackletsLUT, // const int** + nTracklets, // const int + layer, // const int + cells, // CellSeed* + cellsLUTsArrayDevice, // int** + deltaROF, // const int + bz, // const float + maxChi2ClusterAttachment, // const float + cellDeltaTanLambdaSigma, // const float + nSigmaCut); // const float +} + +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Stream& stream) +{ + gpu::computeLayerCellNeighboursKernel<<>>( + cellsLayersDevice, + neighboursLUT, + neighboursIndexTable, + cellsLUTs, + cellNeighbours, + tracklets, + deltaROF, + maxChi2ClusterAttachment, + bz, + layerIndex, + nCells, + maxCellNeighbours); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(stream.get()); + thrust::inclusive_scan(nosync_policy, neighboursLUT, neighboursLUT + nCellsNext, neighboursLUT); + thrust::exclusive_scan(nosync_policy, neighboursIndexTable, neighboursIndexTable + nCells + 1, neighboursIndexTable); +} + +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + const int nBlocks, + const int nThreads, + gpu::Stream& stream) +{ + gpu::computeLayerCellNeighboursKernel<<>>( + cellsLayersDevice, + neighboursLUT, + neighboursIndexTable, + cellsLUTs, + cellNeighbours, + tracklets, + deltaROF, + maxChi2ClusterAttachment, + bz, + layerIndex, + nCells, + maxCellNeighbours); +} + +int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, + int* cellNeighbours, + unsigned int nNeigh, + gpu::Stream& stream, + o2::its::ExternalAllocator* allocator) +{ + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(allocator)).on(stream.get()); + thrust::device_ptr> neighVectorPairs(cellNeighbourPairs); + thrust::device_ptr validNeighs(cellNeighbours); + auto updatedEnd = thrust::remove_if(nosync_policy, neighVectorPairs, neighVectorPairs + nNeigh, gpu::is_invalid_pair()); + size_t newSize = updatedEnd - neighVectorPairs; + thrust::stable_sort(nosync_policy, neighVectorPairs, neighVectorPairs + newSize, gpu::sort_by_second()); + thrust::transform(nosync_policy, neighVectorPairs, neighVectorPairs + newSize, validNeighs, gpu::pair_to_first()); + return newSize; +} + +template +void processNeighboursHandler(const int startLayer, + const int startLevel, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, + const unsigned char** usedClusters, + std::array& neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + alloc->pushTagOnStack(Tag); + auto allocInt = gpu::TypedAllocator(alloc); + auto allocCellSeed = gpu::TypedAllocator>(alloc); + thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + + gpu::processNeighboursKernel<<>>( + startLayer, + startLevel, + allCellSeeds, + currentCellSeeds, + nullptr, + nCells[startLayer], + nullptr, + nullptr, + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours[startLayer - 1], + neighboursDeviceLUTs[startLayer - 1], + foundTrackingFrameInfo, + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + gpu::processNeighboursKernel<<>>( + startLayer, + startLevel, + allCellSeeds, + currentCellSeeds, + nullptr, + nCells[startLayer], + thrust::raw_pointer_cast(&updatedCellSeed[0]), + thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours[startLayer - 1], + neighboursDeviceLUTs[startLayer - 1], + foundTrackingFrameInfo, + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + + int level = startLevel; + thrust::device_vector> lastCellId(allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); + for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { + lastCellSeed.swap(updatedCellSeed); + lastCellId.swap(updatedCellId); + thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); + thrust::device_vector>(allocInt).swap(updatedCellId); + auto lastCellSeedSize{lastCellSeed.size()}; + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + + gpu::processNeighboursKernel<<>>( + iLayer, + --level, + allCellSeeds, + thrust::raw_pointer_cast(&lastCellSeed[0]), + thrust::raw_pointer_cast(&lastCellId[0]), + lastCellSeedSize, + nullptr, + nullptr, + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours[iLayer - 1], + neighboursDeviceLUTs[iLayer - 1], + foundTrackingFrameInfo, + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + auto foundSeeds{foundSeedsTable.back()}; + updatedCellId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); + updatedCellSeed.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + + gpu::processNeighboursKernel<<>>( + iLayer, + level, + allCellSeeds, + thrust::raw_pointer_cast(&lastCellSeed[0]), + thrust::raw_pointer_cast(&lastCellId[0]), + lastCellSeedSize, + thrust::raw_pointer_cast(&updatedCellSeed[0]), + thrust::raw_pointer_cast(&updatedCellId[0]), + thrust::raw_pointer_cast(&foundSeedsTable[0]), + usedClusters, + neighbours[iLayer - 1], + neighboursDeviceLUTs[iLayer - 1], + foundTrackingFrameInfo, + bz, + maxChi2ClusterAttachment, + propagator, + matCorrType); + } + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + auto s{end - outSeeds.begin()}; + seedsHost.reserve(seedsHost.size() + s); + thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); +} + +template +void countTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + // TODO: the minPts&layerRadii is transfered twice + // we should allocate this in constant memory and stop these + // small transferes! + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + nullptr, // TrackITSExt* + seedLUT, // int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); +} + +template +void computeTrackSeedHandler(CellSeed* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads) +{ + thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); + gpu::fitTrackSeedsKernel<<>>( + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + tracks, // TrackITSExt* + seedLUT, // const int* + thrust::raw_pointer_cast(&layerRadii[0]), // const float* + thrust::raw_pointer_cast(&minPts[0]), // const float* + nSeeds, // const unsigned int + bz, // const float + startLevel, // const int + maxChi2ClusterAttachment, // float + maxChi2NDF, // float + reseedIfShorter, // int + repeatRefitOut, // bool + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + thrust::device_ptr tr_ptr(tracks); + thrust::sort(sync_policy, tr_ptr, tr_ptr + nTracks, gpu::compare_track_chi2()); +} + +/// Explicit instantiation of ITS2 handlers +template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, + const uint8_t* multMask, + const int layer, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + bounded_vector& phiCuts, + const float resolutionPV, + std::array& minRs, + std::array& maxRs, + bounded_vector& resolutions, + std::vector& radii, + bounded_vector& mulScatAng, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template void countCellsHandler<7>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed<7>* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template void computeCellsHandler<7>(const Cluster** sortedClusters, + const Cluster** unsortedClusters, + const TrackingFrameInfo** tfInfo, + Tracklet** tracklets, + int** trackletsLUT, + const int nTracklets, + const int layer, + CellSeed<7>* cells, + int** cellsLUTsArrayDevice, + int* cellsLUTsHost, + const int deltaROF, + const float bz, + const float maxChi2ClusterAttachment, + const float cellDeltaTanLambdaSigma, + const float nSigmaCut, + const int nBlocks, + const int nThreads, + gpu::Streams& streams); + +template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads, + gpu::Stream& stream); + +template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, + int* neighboursLUT, + int** cellsLUTs, + gpuPair* cellNeighbours, + int* neighboursIndexTable, + const Tracklet** tracklets, + const int deltaROF, + const float maxChi2ClusterAttachment, + const float bz, + const int layerIndex, + const unsigned int nCells, + const unsigned int nCellsNext, + const int maxCellNeighbours, + const int nBlocks, + const int nThreads, + gpu::Stream& stream); + +template void processNeighboursHandler<7>(const int startLayer, + const int startLevel, + CellSeed<7>** allCellSeeds, + CellSeed<7>* currentCellSeeds, + std::array& nCells, + const unsigned char** usedClusters, + std::array& neighbours, + gsl::span neighboursDeviceLUTs, + const TrackingFrameInfo** foundTrackingFrameInfo, + bounded_vector>& seedsHost, + const float bz, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template void countTrackSeedHandler(CellSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + o2::its::TrackITSExt* tracks, + const int* seedLUT, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, + const unsigned int nSeeds, + const unsigned int nTracks, + const float bz, + const int startLevel, + const float maxChi2ClusterAttachment, + const float maxChi2NDF, + const int reseedIfShorter, + const bool repeatRefitOut, + const bool shiftRefToCluster, + const o2::base::Propagator* propagator, + const o2::base::PropagatorF::MatCorrType matCorrType, + o2::its::ExternalAllocator* alloc, + const int nBlocks, + const int nThreads); + +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu deleted file mode 100644 index 75b17c815b9a4..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/Context.h" -#include "ITStracking/Constants.h" - -#include -#include -#include -#include -#include -#include - -namespace -{ - -int roundUp(const int numToRound, const int multiple) -{ - if (multiple == 0) { - return numToRound; - } - - int remainder{numToRound % multiple}; - if (remainder == 0) { - return numToRound; - } - return numToRound + multiple - remainder; -} - -int findNearestDivisor(const int numToRound, const int divisor) -{ - - if (numToRound > divisor) { - return divisor; - } - - int result = numToRound; - while (divisor % result != 0) { - ++result; - } - return result; -} - -} // namespace - -namespace o2 -{ -namespace its -{ -using constants::GB; -namespace gpu -{ - -GPUh() void gpuThrowOnError() -{ - cudaError_t error = cudaGetLastError(); - - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -double bytesToconfig(size_t s) { return (double)s / (1024.0); } -double bytesToGB(size_t s) { return (double)s / GB; } - -void utils::checkGPUError(const cudaError_t error, const char* file, const int line) -{ - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << file << ":" << line << std::endl - << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " - << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -void utils::getDeviceProp(int deviceId, bool print) -{ - const int w1 = 34; - std::cout << std::left; - std::cout << std::setw(w1) - << "--------------------------------------------------------------------------------" - << std::endl; - std::cout << std::setw(w1) << "device#" << deviceId << std::endl; - - cudaDeviceProp props; - checkGPUError(cudaGetDeviceProperties(&props, deviceId)); - if (print) { - std::cout << std::setw(w1) << "Name: " << props.name << std::endl; - std::cout << std::setw(w1) << "pciBusID: " << props.pciBusID << std::endl; - std::cout << std::setw(w1) << "pciDeviceID: " << props.pciDeviceID << std::endl; - std::cout << std::setw(w1) << "pciDomainID: " << props.pciDomainID << std::endl; - std::cout << std::setw(w1) << "multiProcessorCount: " << props.multiProcessorCount << std::endl; - std::cout << std::setw(w1) << "maxThreadsPerMultiProcessor: " << props.maxThreadsPerMultiProcessor - << std::endl; - std::cout << std::setw(w1) << "isMultiGpuBoard: " << props.isMultiGpuBoard << std::endl; - std::cout << std::setw(w1) << "clockRate: " << (float)props.clockRate / 1000.0 << " Mhz" << std::endl; - std::cout << std::setw(w1) << "memoryClockRate: " << (float)props.memoryClockRate / 1000.0 << " Mhz" - << std::endl; - std::cout << std::setw(w1) << "memoryBusWidth: " << props.memoryBusWidth << std::endl; - std::cout << std::setw(w1) << "clockInstructionRate: " << (float)props.clockRate / 1000.0 - << " Mhz" << std::endl; - std::cout << std::setw(w1) << "totalGlobalMem: " << std::fixed << std::setprecision(2) - << bytesToGB(props.totalGlobalMem) << " GB" << std::endl; -#if !defined(__CUDACC__) - std::cout << std::setw(w1) << "maxSharedMemoryPerMultiProcessor: " << std::fixed << std::setprecision(2) - << bytesToconfig(props.sharedMemPerMultiprocessor) << " config" << std::endl; -#endif -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "maxSharedMemoryPerMultiProcessor: " << std::fixed << std::setprecision(2) - << bytesToconfig(props.maxSharedMemoryPerMultiProcessor) << " config" << std::endl; -#endif - std::cout << std::setw(w1) << "totalConstMem: " << props.totalConstMem << std::endl; - std::cout << std::setw(w1) << "sharedMemPerBlock: " << (float)props.sharedMemPerBlock / 1024.0 << " config" - << std::endl; - std::cout << std::setw(w1) << "canMapHostMemory: " << props.canMapHostMemory << std::endl; - std::cout << std::setw(w1) << "regsPerBlock: " << props.regsPerBlock << std::endl; - std::cout << std::setw(w1) << "warpSize: " << props.warpSize << std::endl; - std::cout << std::setw(w1) << "l2CacheSize: " << props.l2CacheSize << std::endl; - std::cout << std::setw(w1) << "computeMode: " << props.computeMode << std::endl; - std::cout << std::setw(w1) << "maxThreadsPerBlock: " << props.maxThreadsPerBlock << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.x: " << props.maxThreadsDim[0] << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.y: " << props.maxThreadsDim[1] << std::endl; - std::cout << std::setw(w1) << "maxThreadsDim.z: " << props.maxThreadsDim[2] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.x: " << props.maxGridSize[0] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.y: " << props.maxGridSize[1] << std::endl; - std::cout << std::setw(w1) << "maxGridSize.z: " << props.maxGridSize[2] << std::endl; - std::cout << std::setw(w1) << "major: " << props.major << std::endl; - std::cout << std::setw(w1) << "minor: " << props.minor << std::endl; - std::cout << std::setw(w1) << "concurrentKernels: " << props.concurrentKernels << std::endl; - std::cout << std::setw(w1) << "cooperativeLaunch: " << props.cooperativeLaunch << std::endl; - std::cout << std::setw(w1) << "cooperativeMultiDeviceLaunch: " << props.cooperativeMultiDeviceLaunch << std::endl; -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "arch.hasGlobalInt32Atomics: " << props.arch.hasGlobalInt32Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasGlobalFloatAtomicExch: " << props.arch.hasGlobalFloatAtomicExch - << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedInt32Atomics: " << props.arch.hasSharedInt32Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedFloatAtomicExch: " << props.arch.hasSharedFloatAtomicExch - << std::endl; - std::cout << std::setw(w1) << "arch.hasFloatAtomicAdd: " << props.arch.hasFloatAtomicAdd << std::endl; - std::cout << std::setw(w1) << "arch.hasGlobalInt64Atomics: " << props.arch.hasGlobalInt64Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasSharedInt64Atomics: " << props.arch.hasSharedInt64Atomics << std::endl; - std::cout << std::setw(w1) << "arch.hasDoubles: " << props.arch.hasDoubles << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpVote: " << props.arch.hasWarpVote << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpBallot: " << props.arch.hasWarpBallot << std::endl; - std::cout << std::setw(w1) << "arch.hasWarpShuffle: " << props.arch.hasWarpShuffle << std::endl; - std::cout << std::setw(w1) << "arch.hasFunnelShift: " << props.arch.hasFunnelShift << std::endl; - std::cout << std::setw(w1) << "arch.hasThreadFenceSystem: " << props.arch.hasThreadFenceSystem << std::endl; - std::cout << std::setw(w1) << "arch.hasSyncThreadsExt: " << props.arch.hasSyncThreadsExt << std::endl; - std::cout << std::setw(w1) << "arch.hasSurfaceFuncs: " << props.arch.hasSurfaceFuncs << std::endl; - std::cout << std::setw(w1) << "arch.has3dGrid: " << props.arch.has3dGrid << std::endl; - std::cout << std::setw(w1) << "arch.hasDynamicParallelism: " << props.arch.hasDynamicParallelism << std::endl; - std::cout << std::setw(w1) << "gcnArchName: " << props.gcnArchName << std::endl; -#endif - std::cout << std::setw(w1) << "isIntegrated: " << props.integrated << std::endl; - std::cout << std::setw(w1) << "maxTexture1D: " << props.maxTexture1D << std::endl; - std::cout << std::setw(w1) << "maxTexture2D.width: " << props.maxTexture2D[0] << std::endl; - std::cout << std::setw(w1) << "maxTexture2D.height: " << props.maxTexture2D[1] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.width: " << props.maxTexture3D[0] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.height: " << props.maxTexture3D[1] << std::endl; - std::cout << std::setw(w1) << "maxTexture3D.depth: " << props.maxTexture3D[2] << std::endl; -#if defined(__HIPCC__) - std::cout << std::setw(w1) << "isLargeBar: " << props.isLargeBar << std::endl; - std::cout << std::setw(w1) << "asicRevision: " << props.asicRevision << std::endl; -#endif - - int deviceCnt; - checkGPUError(cudaGetDeviceCount(&deviceCnt)); - std::cout << std::setw(w1) << "peers: "; - for (int i = 0; i < deviceCnt; i++) { - int isPeer; - checkGPUError(cudaDeviceCanAccessPeer(&isPeer, i, deviceId)); - if (isPeer) { - std::cout << "device#" << i << " "; - } - } - std::cout << std::endl; - std::cout << std::setw(w1) << "non-peers: "; - for (int i = 0; i < deviceCnt; i++) { - int isPeer; - checkGPUError(cudaDeviceCanAccessPeer(&isPeer, i, deviceId)); - if (!isPeer) { - std::cout << "device#" << i << " "; - } - } - std::cout << std::endl; - - size_t free, total; - checkGPUError(cudaMemGetInfo(&free, &total)); - - std::cout << std::fixed << std::setprecision(2); - std::cout << std::setw(w1) << "memInfo.total: " << bytesToGB(total) << " GB" << std::endl; - std::cout << std::setw(w1) << "memInfo.free: " << bytesToGB(free) << " GB (" << std::setprecision(0) - << (float)free / total * 100.0 << "%)" << std::endl; - } -} - -dim3 utils::getBlockSize(const int colsNum) -{ - return getBlockSize(colsNum, 1); -} - -dim3 utils::getBlockSize(const int colsNum, const int rowsNum) -{ - const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); - return getBlockSize(colsNum, rowsNum, deviceProperties.gpuCores / deviceProperties.maxBlocksPerSM); -} - -dim3 utils::getBlockSize(const int colsNum, const int rowsNum, const int maxThreadsPerBlock) -{ - const DeviceProperties& deviceProperties = Context::getInstance().getDeviceProperties(); - int xThreads = max(min(colsNum, deviceProperties.maxThreadsDim.x), 1); - int yThreads = max(min(rowsNum, deviceProperties.maxThreadsDim.y), 1); - const int totalThreads = roundUp(min(xThreads * yThreads, maxThreadsPerBlock), - deviceProperties.warpSize); - - if (xThreads > yThreads) { - - xThreads = findNearestDivisor(xThreads, totalThreads); - yThreads = totalThreads / xThreads; - - } else { - - yThreads = findNearestDivisor(yThreads, totalThreads); - xThreads = totalThreads / yThreads; - } - - return dim3{static_cast(xThreads), static_cast(yThreads)}; -} - -dim3 utils::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum) -{ - return getBlocksGrid(threadsPerBlock, rowsNum, 1); -} - -dim3 utils::getBlocksGrid(const dim3& threadsPerBlock, const int rowsNum, const int colsNum) -{ - - return dim3{1 + (rowsNum - 1) / threadsPerBlock.x, 1 + (colsNum - 1) / threadsPerBlock.y}; -} - -void utils::gpuMalloc(void** p, const int size) -{ - checkGPUError(cudaMalloc(p, size), __FILE__, __LINE__); -} - -void utils::gpuFree(void* p) -{ - checkGPUError(cudaFree(p), __FILE__, __LINE__); -} - -void utils::gpuMemset(void* p, int value, int size) -{ - checkGPUError(cudaMemset(p, value, size), __FILE__, __LINE__); -} - -void utils::gpuMemcpyHostToDevice(void* dst, const void* src, int size) -{ - checkGPUError(cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice), __FILE__, __LINE__); -} - -void utils::gpuMemcpyDeviceToHost(void* dst, const void* src, int size) -{ - checkGPUError(cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost), __FILE__, __LINE__); -} - -void utils::gpuMemcpyToSymbol(const void* symbol, const void* src, int size) -{ - checkGPUError(cudaMemcpyToSymbol(symbol, src, size, 0, cudaMemcpyHostToDevice), __FILE__, __LINE__); -} - -void utils::gpuMemcpyFromSymbol(void* dst, const void* symbol, int size) -{ - checkGPUError(cudaMemcpyFromSymbol(dst, symbol, size, 0, cudaMemcpyDeviceToHost), __FILE__, __LINE__); -} - -GPUd() int utils::getLaneIndex() -{ - uint32_t laneIndex; - asm volatile("mov.u32 %0, %%laneid;" - : "=r"(laneIndex)); - return static_cast(laneIndex); -} -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu deleted file mode 100644 index cefad5117ff9c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu +++ /dev/null @@ -1,781 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \author matteo.concas@cern.ch - -#include -#include -#include -#include -#include -#include - -#ifndef GPUCA_GPUCODE_GENRTC -#include -#endif - -#include "ITStracking/MathUtils.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" - -#include "GPUCommonArray.h" - -#ifdef VTX_DEBUG -#include "TTree.h" -#include "TFile.h" -#endif - -#include "ITStrackingGPU/TracerGPU.h" - -namespace o2 -{ -namespace its -{ -using constants::its::VertexerHistogramVolume; -using constants::math::TwoPi; -using gpu::utils::checkGPUError; -using math_utils::getNormalizedPhi; - -using namespace constants::its2; -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float z1, float maxdeltaz, float maxdeltaphi) -{ - const float zRangeMin = z1 - maxdeltaz; - const float phiRangeMin = currentCluster.phi - maxdeltaphi; - const float zRangeMax = z1 + maxdeltaz; - const float phiRangeMax = currentCluster.phi + maxdeltaphi; - - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { - - return getEmptyBinsRect(); - } - - return int4{o2::gpu::GPUCommonMath::Max(0, getZBinIndex(layerIndex + 1, zRangeMin)), - getPhiBinIndex(phiRangeMin), - o2::gpu::GPUCommonMath::Min(ZBins - 1, getZBinIndex(layerIndex + 1, zRangeMax)), - getPhiBinIndex(phiRangeMax)}; -} - -GPUh() void gpuThrowOnError() -{ - cudaError_t error = cudaGetLastError(); - - if (error != cudaSuccess) { - std::ostringstream errorString{}; - errorString << GPU_ARCH << " API returned error [" << cudaGetErrorString(error) << "] (code " << error << ")" << std::endl; - throw std::runtime_error{errorString.str()}; - } -} - -VertexerTraitsGPU::VertexerTraitsGPU() -{ - setIsGPU(true); -} - -VertexerTraitsGPU::~VertexerTraitsGPU() -{ - gpu::utils::gpuFree(mDeviceIndexTableUtils); -} - -void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams) -{ - mTimeFrameGPU->initialise(0, trackingParams, 3, &mIndexTableUtils, &mTfGPUParams); -} - -namespace gpu -{ - -template -GPUd() void printOnThread(const unsigned int tId, const char* str, Args... args) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf(str, args...); - } -} - -template -GPUd() void printOnBlock(const unsigned int bId, const char* str, Args... args) -{ - if (blockIdx.x == bId && threadIdx.x == 0) { - printf(str, args...); - } -} - -GPUg() void printBufferOnThread(const int* v, size_t size, const int len = 150, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - for (int i{0}; i < size; ++i) { - if (!(i % len)) { - printf("\n start: ===>%d/%d\t", i, (int)size); - } - printf("%d\t", v[i]); - } - printf("\n"); - } -} - -GPUg() void printBufferOnThreadF(const float* v, size_t size, const unsigned int tId = 0) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vector :"); - for (int i{0}; i < size; ++i) { - printf("%.9f\t", v[i]); - } - printf("\n"); - } -} - -GPUg() void resetTrackletsKernel(Tracklet* tracklets, const int nTracklets) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nTracklets; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - new (tracklets + iCurrentLayerClusterIndex) Tracklet{}; - } -} - -GPUg() void dumpFoundTrackletsKernel(const Tracklet* tracklets, const int* nTracklet, const size_t nClustersMiddleLayer, const int maxTrackletsPerCluster) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nClustersMiddleLayer; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - const int stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - for (int iTracklet{0}; iTracklet < nTracklet[iCurrentLayerClusterIndex]; ++iTracklet) { - auto& t = tracklets[stride + iTracklet]; - t.dump(); - } - } -} - -GPUg() void dumpMaximaKernel(const cub::KeyValuePair* tmpVertexBins, const int threadId) -{ - if (blockIdx.x * blockDim.x + threadIdx.x == threadId) { - printf("XmaxBin: %d at index: %d | YmaxBin: %d at index: %d | ZmaxBin: %d at index: %d\n", - tmpVertexBins[0].value, tmpVertexBins[0].key, - tmpVertexBins[1].value, tmpVertexBins[1].key, - tmpVertexBins[2].value, tmpVertexBins[2].key); - } -} - -template -GPUg() void trackleterKernelSingleRof( - const Cluster* clustersNextLayer, // 0 2 - const Cluster* clustersCurrentLayer, // 1 1 - const int sizeNextLClusters, - const int sizeCurrentLClusters, - const int* indexTableNext, - const float phiCut, - Tracklet* Tracklets, - int* foundTracklets, - const IndexTableUtils* utils, - const int rofId, - const size_t maxTrackletsPerCluster = 1e2) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - // loop on layer1 clusters - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < sizeCurrentLClusters; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - if (iCurrentLayerClusterIndex < sizeCurrentLClusters) { - unsigned int storedTracklets{0}; - const size_t stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - const Cluster& currentCluster = clustersCurrentLayer[iCurrentLayerClusterIndex]; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, *utils)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - // loop on phi bins next layer - for (unsigned int iPhiBin{(unsigned int)selectedBinsRect.y}, iPhiCount{0}; iPhiCount < (unsigned int)phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int firstRowClusterIndex{indexTableNext[firstBinIndex]}; - const int maxRowClusterIndex{indexTableNext[firstBinIndex + zBins]}; - // loop on clusters next layer - for (int iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < sizeNextLClusters; ++iNextLayerClusterIndex) { - const Cluster& nextCluster = clustersNextLayer[iNextLayerClusterIndex]; - if (o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - new (Tracklets + stride + storedTracklets) Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, rofId, rofId}; - } else { - new (Tracklets + stride + storedTracklets) Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, rofId, rofId}; - } - ++storedTracklets; - } - } - } - } - } - foundTracklets[iCurrentLayerClusterIndex] = storedTracklets; - if (storedTracklets >= maxTrackletsPerCluster) { - printf("gpu tracklet finder: some lines will be left behind for cluster %d. valid: %u max: %zu\n", iCurrentLayerClusterIndex, storedTracklets, maxTrackletsPerCluster); - } - } - } -} - -template -GPUg() void trackleterKernelMultipleRof( - const Cluster* clustersNextLayer, // 0 2 - const Cluster* clustersCurrentLayer, // 1 1 - const int* sizeNextLClusters, - const int* sizeCurrentLClusters, - const int* nextIndexTables, - Tracklet* Tracklets, - int* foundTracklets, - const IndexTableUtils* utils, - const unsigned int startRofId, - const unsigned int rofSize, - const float phiCut, - const size_t maxTrackletsPerCluster = 1e2) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof = iRof + startRofId; - auto* clustersNextLayerRof = clustersNextLayer + (sizeNextLClusters[rof] - sizeNextLClusters[startRofId]); - auto* clustersCurrentLayerRof = clustersCurrentLayer + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]); - auto nClustersNextLayerRof = sizeNextLClusters[rof + 1] - sizeNextLClusters[rof]; - auto nClustersCurrentLayerRof = sizeCurrentLClusters[rof + 1] - sizeCurrentLClusters[rof]; - auto* indexTableNextRof = nextIndexTables + iRof * (phiBins * zBins + 1); - auto* TrackletsRof = Tracklets + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]) * maxTrackletsPerCluster; - auto* foundTrackletsRof = foundTracklets + (sizeCurrentLClusters[rof] - sizeCurrentLClusters[startRofId]); - - // single rof loop on layer1 clusters - for (int iCurrentLayerClusterIndex = threadIdx.x; iCurrentLayerClusterIndex < nClustersCurrentLayerRof; iCurrentLayerClusterIndex += blockDim.x) { - unsigned int storedTracklets{0}; - const size_t stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - const Cluster& currentCluster = clustersCurrentLayerRof[iCurrentLayerClusterIndex]; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, *utils)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - // loop on phi bins next layer - for (unsigned int iPhiBin{(unsigned int)selectedBinsRect.y}, iPhiCount{0}; iPhiCount < (unsigned int)phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int firstRowClusterIndex{indexTableNextRof[firstBinIndex]}; - const int maxRowClusterIndex{indexTableNextRof[firstBinIndex + zBins]}; - // loop on clusters next layer - for (int iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < nClustersNextLayerRof; ++iNextLayerClusterIndex) { - const Cluster& nextCluster = clustersNextLayerRof[iNextLayerClusterIndex]; - if (o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - new (TrackletsRof + stride + storedTracklets) Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, static_cast(rof), static_cast(rof)}; - } else { - new (TrackletsRof + stride + storedTracklets) Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, static_cast(rof), static_cast(rof)}; - } - ++storedTracklets; - } - } - } - } - } - foundTrackletsRof[iCurrentLayerClusterIndex] = storedTracklets; - // if (storedTracklets >= maxTrackletsPerCluster && storedTracklets - maxTrackletsPerCluster < 5) { - // printf("gpu tracklet finder: some lines will be left behind for cluster %d in rof: %d. valid: %u max: %lu (suppressing after 5 msgs)\n", iCurrentLayerClusterIndex, rof, storedTracklets, maxTrackletsPerCluster); - // } - } - } -} - -template -GPUg() void trackletSelectionKernelSingleRof( - const Cluster* clusters0, - const Cluster* clusters1, - const size_t nClustersMiddleLayer, - Tracklet* tracklets01, - Tracklet* tracklets12, - const int* nFoundTracklet01, - const int* nFoundTracklet12, - unsigned char* usedTracklets, - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const int maxTrackletsPerCluster = 1e2, - const float tanLambdaCut = 0.025f, - const float phiCut = 0.002f) -{ - for (int iCurrentLayerClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentLayerClusterIndex < nClustersMiddleLayer; iCurrentLayerClusterIndex += blockDim.x * gridDim.x) { - const int stride{iCurrentLayerClusterIndex * maxTrackletsPerCluster}; - int validTracklets{0}; - for (int iTracklet12{0}; iTracklet12 < nFoundTracklet12[iCurrentLayerClusterIndex]; ++iTracklet12) { - for (int iTracklet01{0}; iTracklet01 < nFoundTracklet01[iCurrentLayerClusterIndex] && validTracklets < maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklets01[stride + iTracklet01].tanLambda - tracklets12[stride + iTracklet12].tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(tracklets01[stride + iTracklet01].phi - tracklets12[stride + iTracklet12].phi)}; - if (!usedTracklets[stride + iTracklet01] && deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTrackletsPerCluster) { - usedTracklets[stride + iTracklet01] = true; - if constexpr (!initRun) { - new (lines + nExclusiveFoundLines[iCurrentLayerClusterIndex] + validTracklets) Line{tracklets01[stride + iTracklet01], clusters0, clusters1}; - } - ++validTracklets; - } - } - } - if constexpr (initRun) { - nFoundLines[iCurrentLayerClusterIndex] = validTracklets; - if (validTracklets >= maxTrackletsPerCluster) { - printf("gpu tracklet selection: some lines will be left behind for cluster %d. valid: %d max: %d\n", iCurrentLayerClusterIndex, validTracklets, maxTrackletsPerCluster); - } - } - } -} - -template -GPUg() void trackletSelectionKernelMultipleRof( - const Cluster* clusters0, // Clusters on layer 0 - const Cluster* clusters1, // Clusters on layer 1 - const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Tracklet* tracklets01, // Tracklets on layer 0-1 - Tracklet* tracklets12, // Tracklets on layer 1-2 - const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - const int* nFoundTracklets12, // Number of tracklets found on layers 1-2 - unsigned char* usedTracklets, // Used tracklets - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider - const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - const float tanLambdaCut = 0.025f, // Cut on tan lambda - const float phiCut = 0.002f) // Cut on phi -{ - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof = iRof + startRofId; - auto* clustersL0Rof = clusters0 + (sizeClustersL0[rof] - sizeClustersL0[startRofId]); - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; - auto* clustersL1Rof = clusters1 + clustersL1offsetRof; - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; - auto* tracklets01Rof = tracklets01 + clustersL1offsetRof * maxTrackletsPerCluster; - auto* tracklets12Rof = tracklets12 + clustersL1offsetRof * maxTrackletsPerCluster; - auto* foundTracklets01Rof = nFoundTracklets01 + clustersL1offsetRof; - auto* foundTracklets12Rof = nFoundTracklets12 + clustersL1offsetRof; - auto* usedTrackletsRof = usedTracklets + clustersL1offsetRof * maxTrackletsPerCluster; - auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - int* nExclusiveFoundLinesRof = nullptr; - if constexpr (!initRun) { - nExclusiveFoundLinesRof = nExclusiveFoundLines + clustersL1offsetRof; - } - for (int iClusterIndexLayer1 = threadIdx.x; iClusterIndexLayer1 < nClustersL1Rof; iClusterIndexLayer1 += blockDim.x) { - const int stride{iClusterIndexLayer1 * maxTrackletsPerCluster}; - int validTracklets{0}; - for (int iTracklet12{0}; iTracklet12 < foundTracklets12Rof[iClusterIndexLayer1]; ++iTracklet12) { - for (int iTracklet01{0}; iTracklet01 < foundTracklets01Rof[iClusterIndexLayer1] && validTracklets < maxTrackletsPerCluster; ++iTracklet01) { - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklets01Rof[stride + iTracklet01].tanLambda - tracklets12Rof[stride + iTracklet12].tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(tracklets01Rof[stride + iTracklet01].phi - tracklets12Rof[stride + iTracklet12].phi)}; - if (!usedTrackletsRof[stride + iTracklet01] && deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTrackletsPerCluster) { - usedTrackletsRof[stride + iTracklet01] = true; - if constexpr (!initRun) { - new (lines + nExclusiveFoundLinesRof[iClusterIndexLayer1] + validTracklets) Line{tracklets01Rof[stride + iTracklet01], clustersL0Rof, clustersL1Rof}; - } - ++validTracklets; - } - } - } - if constexpr (initRun) { - foundLinesRof[iClusterIndexLayer1] = validTracklets; - // if (validTracklets >= maxTrackletsPerCluster) { - // printf("gpu tracklet selection: some lines will be left behind for cluster %d. valid: %d max: %d\n", iClusterIndexLayer1, validTracklets, maxTrackletsPerCluster); - // } - } - } - } // rof loop -} - -GPUg() void lineClustererMultipleRof( - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - int* clusteredLines, // Clustered lines - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider // Number of found lines exclusive scan - const float pairCut) // Selection on line pairs -{ - for (unsigned int iRof{threadIdx.x}; iRof < rofSize; iRof += blockDim.x) { - auto rof = iRof + startRofId; - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; // starting cluster offset for this ROF - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; // number of clusters for this ROF - auto linesOffsetRof = nExclusiveFoundLines[clustersL1offsetRof]; // starting line offset for this ROF - // auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - auto nLinesRof = nExclusiveFoundLines[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - // printf("rof: %d -> %d lines.\n", rof, nLinesRof); - for (int iLine1 = 0; iLine1 < nLinesRof; ++iLine1) { - auto absLine1Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine1; - if (clusteredLines[absLine1Index] > -1) { - continue; - } - for (int iLine2 = iLine1 + 1; iLine2 < nLinesRof; ++iLine2) { - auto absLine2Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine2; - if (clusteredLines[absLine2Index] > -1) { - continue; - } - - if (Line::getDCA(lines[absLine1Index], lines[absLine2Index]) < pairCut) { - ClusterLinesGPU tmpClus{lines[absLine1Index], lines[absLine2Index]}; - float tmpVertex[3]; - tmpVertex[0] = tmpClus.getVertex()[0]; - tmpVertex[1] = tmpClus.getVertex()[1]; - tmpVertex[2] = tmpClus.getVertex()[2]; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { // outside the beampipe, skip it - break; - } - clusteredLines[absLine1Index] = iLine1; // We set local index of first line to contribute, so we can retrieve the cluster later - clusteredLines[absLine2Index] = iLine1; - for (int iLine3 = 0; iLine3 < nLinesRof; ++iLine3) { - auto absLine3Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine3; - if (clusteredLines[absLine3Index] > -1) { - continue; - } - if (Line::getDistanceFromPoint(lines[absLine3Index], tmpVertex) < pairCut) { - clusteredLines[absLine3Index] = iLine1; - } - } - break; - } - } - } - } // rof loop -} - -GPUg() void computeCentroidsKernel( - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const size_t nClustersMiddleLayer, - float* centroids, - const float lowHistX, - const float highHistX, - const float lowHistY, - const float highHistY, - const float pairCut) -{ - const int nLines = nExclusiveFoundLines[nClustersMiddleLayer - 1] + nFoundLines[nClustersMiddleLayer - 1]; - const int maxIterations{nLines * (nLines - 1) / 2}; - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { - int iFirstLine = currentThreadIndex / nLines; - int iSecondLine = currentThreadIndex % nLines; - // All unique pairs - if (iSecondLine <= iFirstLine) { - iFirstLine = nLines - iFirstLine - 2; - iSecondLine = nLines - iSecondLine - 1; - } - if (Line::getDCA(lines[iFirstLine], lines[iSecondLine]) < pairCut) { - ClusterLinesGPU cluster{lines[iFirstLine], lines[iSecondLine]}; - if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { - // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); - centroids[2 * currentThreadIndex] = cluster.getVertex()[0]; - centroids[2 * currentThreadIndex + 1] = cluster.getVertex()[1]; - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * lowHistX; - centroids[2 * currentThreadIndex + 1] = 2 * lowHistY; - } - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * highHistX; - centroids[2 * currentThreadIndex + 1] = 2 * highHistY; - } - } -} - -GPUg() void computeZCentroidsKernel( - const int nLines, - const cub::KeyValuePair* tmpVtX, - float* beamPosition, - Line* lines, - float* centroids, - const int* histX, // X - const float lowHistX, - const float binSizeHistX, - const int nBinsHistX, - const int* histY, // Y - const float lowHistY, - const float binSizeHistY, - const int nBinsHistY, - const float lowHistZ, // Z - const float pairCut, - const int binOpeningX, - const int binOpeningY) -{ - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { - if (tmpVtX[0].value || tmpVtX[1].value) { - float tmpX{lowHistX + tmpVtX[0].key * binSizeHistX + binSizeHistX / 2}; - int sumWX{tmpVtX[0].value}; - float wX{tmpX * tmpVtX[0].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[0].key + binOpeningX + 1, nBinsHistX - 1); ++iBin) { - if (iBin != tmpVtX[0].key) { - wX += (lowHistX + iBin * binSizeHistX + binSizeHistX / 2) * histX[iBin]; - sumWX += histX[iBin]; - } - } - float tmpY{lowHistY + tmpVtX[1].key * binSizeHistY + binSizeHistY / 2}; - int sumWY{tmpVtX[1].value}; - float wY{tmpY * tmpVtX[1].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[1].key + binOpeningY + 1, nBinsHistY - 1); ++iBin) { - if (iBin != tmpVtX[1].key) { - wY += (lowHistY + iBin * binSizeHistY + binSizeHistY / 2) * histY[iBin]; - sumWY += histY[iBin]; - } - } - beamPosition[0] = wX / sumWX; - beamPosition[1] = wY / sumWY; - float mockBeamPoint1[3] = {beamPosition[0], beamPosition[1], -1}; // get two points laying at different z, to create line object - float mockBeamPoint2[3] = {beamPosition[0], beamPosition[1], 1}; - Line pseudoBeam = {mockBeamPoint1, mockBeamPoint2}; - if (Line::getDCA(lines[currentThreadIndex], pseudoBeam) < pairCut) { - ClusterLinesGPU cluster{lines[currentThreadIndex], pseudoBeam}; - centroids[currentThreadIndex] = cluster.getVertex()[2]; - } else { - centroids[currentThreadIndex] = 2 * lowHistZ; - } - } - } -} - -GPUg() void computeVertexKernel( - cub::KeyValuePair* tmpVertexBins, - int* histZ, // Z - const float lowHistZ, - const float binSizeHistZ, - const int nBinsHistZ, - Vertex* vertices, - float* beamPosition, - const int vertIndex, - const int minContributors, - const int binOpeningZ) -{ - for (size_t currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { - if (currentThreadIndex == 0) { - if (tmpVertexBins[2].value > 1 && (tmpVertexBins[0].value || tmpVertexBins[1].value)) { - float z{lowHistZ + tmpVertexBins[2].key * binSizeHistZ + binSizeHistZ / 2}; - float ex{0.f}; - float ey{0.f}; - float ez{0.f}; - int sumWZ{tmpVertexBins[2].value}; - float wZ{z * tmpVertexBins[2].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVertexBins[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVertexBins[2].key + binOpeningZ + 1, nBinsHistZ - 1); ++iBin) { - if (iBin != tmpVertexBins[2].key) { - wZ += (lowHistZ + iBin * binSizeHistZ + binSizeHistZ / 2) * histZ[iBin]; - sumWZ += histZ[iBin]; - } - histZ[iBin] = 0; - } - if (sumWZ > minContributors || vertIndex == 0) { - new (vertices + vertIndex) Vertex{o2::math_utils::Point3D(beamPosition[0], beamPosition[1], wZ / sumWZ), o2::gpu::gpustd::array{ex, 0, ey, 0, 0, ez}, static_cast(sumWZ), 0}; - } else { - new (vertices + vertIndex) Vertex{}; - } - } else { - new (vertices + vertIndex) Vertex{}; - } - } - } -} -} // namespace gpu - -void VertexerTraitsGPU::updateVertexingParameters(const VertexingParameters& vrtPar, const TimeFrameGPUParameters& tfPar) -{ - mVrtParams = vrtPar; - mTfGPUParams = tfPar; - mIndexTableUtils.setTrackingParameters(vrtPar); - mVrtParams.phiSpan = static_cast(std::ceil(mIndexTableUtils.getNphiBins() * mVrtParams.phiCut / - constants::math::TwoPi)); - mVrtParams.zSpan = static_cast(std::ceil(mVrtParams.zCut * mIndexTableUtils.getInverseZCoordinate(0))); -} - -void VertexerTraitsGPU::computeTracklets() -{ - if (!mTimeFrameGPU->getClusters().size()) { - return; - } - std::vector threads(mTimeFrameGPU->getNChunks()); - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int rofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - mTimeFrameGPU->getVerticesInChunks()[chunkId].clear(); - mTimeFrameGPU->getNVerticesInChunks()[chunkId].clear(); - mTimeFrameGPU->getLabelsInChunks()[chunkId].clear(); - auto doVertexReconstruction = [&, chunkId, rofPerChunk]() -> void { - auto offset = chunkId * rofPerChunk; - auto maxROF = offset + rofPerChunk; - while (offset < maxROF) { - auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - RANGE("chunk_gpu_vertexing", 1); - gpu::trackleterKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clustersNextLayer, // 0 2 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clustersCurrentLayer, // 1 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeNextLClusters, - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeCurrentLClusters, - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(0), // const int* nextIndexTables, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* Tracklets, - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // int* foundTracklets, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - mVrtParams.phiCut, // const float phiCut, - mVrtParams.maxTrackletsPerCluster); // const size_t maxTrackletsPerCluster = 1e2 - gpu::trackleterKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(2), // const Cluster* clustersNextLayer, // 0 2 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clustersCurrentLayer, // 1 1 - mTimeFrameGPU->getDeviceROframesClusters(2), // const int* sizeNextLClusters, - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeCurrentLClusters, - mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(2), // const int* nextIndexTables, - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* Tracklets, - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // int* foundTracklets, - mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils, - offset, // const unsigned int startRofId, - rofs, // const unsigned int rofSize, - mVrtParams.phiCut, // const float phiCut, - mVrtParams.maxTrackletsPerCluster); // const size_t maxTrackletsPerCluster = 1e2 - - // Tracklet selection - gpu::trackletSelectionKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clusters0, // Clusters on layer 0 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clusters1, // Clusters on layer 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* tracklets01, // Tracklets on layer 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* tracklets12, // Tracklets on layer 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // const int* nFoundTracklet12, // Number of tracklets found on layers 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), // unsigned char* usedTracklets, // Used tracklets - mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), // Line* lines, // Lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), // int* nFoundLines, // Number of found lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), // int* nExclusiveFoundLines, // Number of found lines exclusive scan - offset, // const unsigned int startRofId, // Starting ROF ID - rofs, // const unsigned int rofSize, // Number of ROFs to consider - mVrtParams.maxTrackletsPerCluster, // const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - mVrtParams.tanLambdaCut, // const float tanLambdaCut = 0.025f, // Cut on tan lambda - mVrtParams.phiCut); // const float phiCut = 0.002f) // Cut on phi - - discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), - mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), - mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1), - mTimeFrameGPU->getStream(chunkId).get())); - - // Reset used tracklets - checkGPUError(cudaMemsetAsync(mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), - false, - sizeof(unsigned char) * mVrtParams.maxTrackletsPerCluster * mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1), - mTimeFrameGPU->getStream(chunkId).get()), - __FILE__, __LINE__); - - gpu::trackletSelectionKernelMultipleRof<<getStream(chunkId).get()>>>( - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(0), // const Cluster* clusters0, // Clusters on layer 0 - mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(1), // const Cluster* clusters1, // Clusters on layer 1 - mTimeFrameGPU->getDeviceROframesClusters(0), // const int* sizeClustersL0, // Number of clusters on layer 0 per ROF - mTimeFrameGPU->getDeviceROframesClusters(1), // const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(0), // Tracklet* tracklets01, // Tracklets on layer 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(1), // Tracklet* tracklets12, // Tracklets on layer 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(0), // const int* nFoundTracklets01, // Number of tracklets found on layers 0-1 - mTimeFrameGPU->getChunk(chunkId).getDeviceNTrackletCluster(1), // const int* nFoundTracklet12, // Number of tracklets found on layers 1-2 - mTimeFrameGPU->getChunk(chunkId).getDeviceUsedTracklets(), // unsigned char* usedTracklets, // Used tracklets - mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), // Line* lines, // Lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines(), // int* nFoundLines, // Number of found lines - mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), // int* nExclusiveFoundLines, // Number of found lines exclusive scan - offset, // const unsigned int startRofId, // Starting ROF ID - rofs, // const unsigned int rofSize, // Number of ROFs to consider - mVrtParams.maxTrackletsPerCluster, // const int maxTrackletsPerCluster = 1e2, // Maximum number of tracklets per cluster - mVrtParams.tanLambdaCut, // const float tanLambdaCut = 0.025f, // Cut on tan lambda - mVrtParams.phiCut); // const float phiCut = 0.002f) // Cut on phi - - int nClusters = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, 1); - int lastFoundLines; - std::vector exclusiveFoundLinesHost(nClusters + 1); - - // Obtain whole exclusive sum including nCluster+1 element (nCluster+1)th element is the total number of found lines. - checkGPUError(cudaMemcpyAsync(exclusiveFoundLinesHost.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceNExclusiveFoundLines(), (nClusters) * sizeof(int), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - checkGPUError(cudaMemcpyAsync(&lastFoundLines, mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundLines() + nClusters - 1, sizeof(int), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - exclusiveFoundLinesHost[nClusters] = exclusiveFoundLinesHost[nClusters - 1] + lastFoundLines; - - std::vector lines(exclusiveFoundLinesHost[nClusters]); - - checkGPUError(cudaMemcpyAsync(lines.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceLines(), sizeof(Line) * lines.size(), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - checkGPUError(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - - // Compute vertices - std::vector clusterLines; - std::vector usedLines; - for (int rofId{0}; rofId < rofs; ++rofId) { - auto rof = offset + rofId; - auto clustersL1offsetRof = mTimeFrameGPU->getROframeClusters(1)[rof] - mTimeFrameGPU->getROframeClusters(1)[offset]; // starting cluster offset for this ROF - auto nClustersL1Rof = mTimeFrameGPU->getROframeClusters(1)[rof + 1] - mTimeFrameGPU->getROframeClusters(1)[rof]; // number of clusters for this ROF - auto linesOffsetRof = exclusiveFoundLinesHost[clustersL1offsetRof]; // starting line offset for this ROF - auto nLinesRof = exclusiveFoundLinesHost[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - gsl::span linesInRof(lines.data() + linesOffsetRof, static_cast::size_type>(nLinesRof)); - - usedLines.resize(linesInRof.size(), false); - usedLines.assign(linesInRof.size(), false); - clusterLines.clear(); - clusterLines.reserve(nClustersL1Rof); - computeVerticesInRof(rof, - linesInRof, - usedLines, - clusterLines, - mTimeFrameGPU->getBeamXY(), - mTimeFrameGPU->getVerticesInChunks()[chunkId], - mTimeFrameGPU->getNVerticesInChunks()[chunkId], - mTimeFrameGPU->hasMCinformation() ? mTimeFrameGPU : nullptr, - mTimeFrameGPU->hasMCinformation() ? &mTimeFrameGPU->getLabelsInChunks()[chunkId] : nullptr); - } - offset += rofs; - } - }; - // Do work - threads[chunkId] = std::thread(doVertexReconstruction); - } - for (auto& thread : threads) { - thread.join(); - } - for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - int start{0}; - for (int rofId{0}; rofId < mTimeFrameGPU->getNVerticesInChunks()[chunkId].size(); ++rofId) { - gsl::span rofVerts{mTimeFrameGPU->getVerticesInChunks()[chunkId].data() + start, static_cast::size_type>(mTimeFrameGPU->getNVerticesInChunks()[chunkId][rofId])}; - mTimeFrameGPU->addPrimaryVertices(rofVerts); - if (mTimeFrameGPU->hasMCinformation()) { - mTimeFrameGPU->getVerticesLabels().emplace_back(); - // TODO: add MC labels - } - start += mTimeFrameGPU->getNVerticesInChunks()[chunkId][rofId]; - } - } - mTimeFrameGPU->wipe(3); -} - -void VertexerTraitsGPU::computeTrackletMatching() -{ -} - -void VertexerTraitsGPU::computeVertices() -{ -} - -void VertexerTraitsGPU::computeVerticesHist() -{ -} - -VertexerTraits* createVertexerTraitsGPU() -{ - return new VertexerTraitsGPU; -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx new file mode 100644 index 0000000000000..658d3cf0dfb91 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx @@ -0,0 +1,179 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \author matteo.concas@cern.ch + +#include + +#include "ITStracking/TrackingConfigParam.h" +#include "ITStrackingGPU/VertexingKernels.h" +#include "ITStrackingGPU/VertexerTraitsGPU.h" + +namespace o2::its +{ + +template +void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams, const int iteration) +{ + // FIXME + // Two things to fix here: + // This loads all necessary data for this step at once, can be overlayed with computation + // Also if running with the tracker some data is loaded twice! + mTimeFrameGPU->initialise(0, trackingParams, 3, &this->mIndexTableUtils, &mTfGPUParams); + + // FIXME some of these only need to be created once! + mTimeFrameGPU->loadIndexTableUtils(iteration); + mTimeFrameGPU->createUsedClustersDeviceArray(iteration, 3); + mTimeFrameGPU->createClustersDeviceArray(iteration, 3); + mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration, 3); + mTimeFrameGPU->createClustersIndexTablesArray(iteration); + mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); + for (int iLayer{0}; iLayer < 3; ++iLayer) { + mTimeFrameGPU->loadClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); + mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); + mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer); + } +} + +template +void VertexerTraitsGPU::adoptTimeFrame(TimeFrame* tf) noexcept +{ + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); +} + +template +void VertexerTraitsGPU::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) +{ + this->mVrtParams = vrtPar; + mTfGPUParams = tfPar; + this->mIndexTableUtils.setTrackingParameters(vrtPar[0]); + for (auto& par : this->mVrtParams) { + par.phiSpan = static_cast(std::ceil(this->mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI)); + par.zSpan = static_cast(std::ceil(par.zCut * this->mIndexTableUtils.getInverseZCoordinate(0))); + } +} + +template +void VertexerTraitsGPU::computeTracklets(const int iteration) +{ + if (mTimeFrameGPU->getClusters().empty()) { + return; + } + const auto& conf = ITSGpuTrackingParamConfig::Instance(); + + mTimeFrameGPU->createVtxTrackletsLUTDevice(iteration); + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + mTimeFrameGPU->getNrof(), + this->mVrtParams[iteration].deltaRof, + mTimeFrameGPU->getDeviceROFramesPV(), + this->mVrtParams[iteration].vertPerRofThreshold, + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes()[1], + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), + mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), + mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), + mTimeFrameGPU->getDeviceNTrackletsPerCluster(), + mTimeFrameGPU->getDeviceNTrackletsPerClusterSum(), + iteration, + this->mVrtParams[iteration].phiCut, + this->mVrtParams[iteration].maxTrackletsPerCluster, + conf.nBlocksVtxComputeTracklets[iteration], + conf.nThreadsVtxComputeTracklets[iteration], + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createVtxTrackletsBuffers(iteration); + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + mTimeFrameGPU->getNrof(), + this->mVrtParams[iteration].deltaRof, + mTimeFrameGPU->getDeviceROFramesPV(), + this->mVrtParams[iteration].vertPerRofThreshold, + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes()[1], + mTimeFrameGPU->getDeviceROFrameClusters(), + (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), + iteration, + this->mVrtParams[iteration].phiCut, + this->mVrtParams[iteration].maxTrackletsPerCluster, + conf.nBlocksVtxComputeTracklets[iteration], + conf.nThreadsVtxComputeTracklets[iteration], + mTimeFrameGPU->getStreams()); +} + +template +void VertexerTraitsGPU::computeTrackletMatching(const int iteration) +{ + if (!mTimeFrameGPU->getTotalTrackletsTF(0) || !mTimeFrameGPU->getTotalTrackletsTF(1)) { + return; + } + + const auto& conf = ITSGpuTrackingParamConfig::Instance(); + mTimeFrameGPU->createVtxLinesLUTDevice(iteration); + countTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), + this->mVrtParams[iteration].deltaRof, + mTimeFrameGPU->getClusterSizes()[1], + mTimeFrameGPU->getDeviceROFrameClusters(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getDeviceArrayUsedClusters(), + (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceUsedTracklets(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), + mTimeFrameGPU->getDeviceNLinesPerCluster(), + mTimeFrameGPU->getDeviceNLinesPerClusterSum(), + iteration, + this->mVrtParams[iteration].phiCut, + this->mVrtParams[iteration].tanLambdaCut, + conf.nBlocksVtxComputeMatching[iteration], + conf.nThreadsVtxComputeMatching[iteration], + mTimeFrameGPU->getStreams()); + mTimeFrameGPU->createVtxLinesBuffer(iteration); + computeTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), + this->mVrtParams[iteration].deltaRof, + mTimeFrameGPU->getClusterSizes()[1], + mTimeFrameGPU->getDeviceROFrameClusters(), + mTimeFrameGPU->getDeviceArrayClusters(), + nullptr, + (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceUsedTracklets(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), + (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), + (const int32_t*)mTimeFrameGPU->getDeviceNLinesPerClusterSum(), + mTimeFrameGPU->getDeviceLines(), + iteration, + this->mVrtParams[iteration].phiCut, + this->mVrtParams[iteration].tanLambdaCut, + conf.nBlocksVtxComputeMatching[iteration], + conf.nThreadsVtxComputeMatching[iteration], + mTimeFrameGPU->getStreams()); +} + +template +void VertexerTraitsGPU::computeVertices(const int iteration) +{ + LOGP(fatal, "This step is not implemented yet!"); + mTimeFrameGPU->loadUsedClustersDevice(); +} + +template class VertexerTraitsGPU<7>; + +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu new file mode 100644 index 0000000000000..a2787bb13598d --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu @@ -0,0 +1,660 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// + +#include +#include + +#include "ITStrackingGPU/VertexingKernels.h" +#include "ITStracking/Tracklet.h" +#include "ITStracking/IndexTableUtils.h" +#include "ITStracking/ClusterLines.h" + +#include "GPUCommonMath.h" +#include "GPUCommonHelpers.h" +#include "GPUCommonDef.h" + +namespace o2::its +{ + +namespace gpu +{ + +template +GPUg() void computeLayerTrackletMutliROFKernel(const Cluster** GPUrestrict() clusters, + const int32_t** GPUrestrict() rofClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clusterIndexTables, + const float phiCut, + maybe_const** GPUrestrict() tracklets, + maybe_const** GPUrestrict() trackletOffsets, + const IndexTableUtils* GPUrestrict() utils, + const int32_t nRofs, + const int32_t deltaRof, + const int32_t* GPUrestrict() rofPV, + const int32_t iteration, + const int32_t verPerRofThreshold, + const int32_t maxTrackletsPerCluster) +{ + constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; + const int32_t phiBins(utils->getNphiBins()); + const int32_t zBins(utils->getNzBins()); + const int32_t tableSize{phiBins * zBins + 1}; + extern __shared__ uint16_t storedTrackletsShared[]; // each deltaROF needs its own counters + uint16_t* storedTrackletsLocal = storedTrackletsShared + threadIdx.x * (2 * deltaRof + 1); + for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < (uint32_t)nRofs; pivotRofId += gridDim.x) { + if (iteration && rofPV[pivotRofId] > verPerRofThreshold) { + continue; + } + const uint16_t startROF = o2::gpu::CAMath::Max(0, (int)pivotRofId - deltaRof); + const uint16_t endROF = o2::gpu::CAMath::Min(nRofs, (int)pivotRofId + deltaRof + 1); + const auto clustersCurrentLayer = getClustersOnLayer((int32_t)pivotRofId, nRofs, 1, rofClusters, clusters); + if (clustersCurrentLayer.empty()) { + continue; + } + auto trackletsPerCluster = getNTrackletsPerCluster(pivotRofId, nRofs, iMode, rofClusters, trackletOffsets); + for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < (uint32_t)clustersCurrentLayer.size(); iCurrentLayerClusterIndex += blockDim.x) { + for (int16_t i{0}; i < (int16_t)((2 * deltaRof) + 1); ++i) { + storedTrackletsLocal[i] = 0; + } + const Cluster& GPUrestrict() currentCluster { clustersCurrentLayer[iCurrentLayerClusterIndex] }; + const int4 selectedBinsRect{getBinsRect(currentCluster, (int)Mode, utils, 0.f, 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; + } + for (int32_t iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { + for (uint16_t targetRofId{startROF}; targetRofId < endROF; ++targetRofId) { + uint16_t& storedTracklets = storedTrackletsLocal[pivotRofId - targetRofId + deltaRof]; + const int32_t firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int32_t maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; + const int32_t firstRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + firstBinIndex]}; + const int32_t maxRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + maxBinIndex]}; + auto clustersNextLayer = getClustersOnLayer((int32_t)targetRofId, nRofs, (int32_t)Mode, rofClusters, clusters); + if (clustersNextLayer.empty()) { + continue; + } + for (int32_t iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < (int32_t)clustersNextLayer.size(); ++iNextLayerClusterIndex) { + if (iteration && usedClusters[(int32_t)Mode][iNextLayerClusterIndex]) { + continue; + } + const Cluster& GPUrestrict() nextCluster { clustersNextLayer[iNextLayerClusterIndex] }; + if (o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(currentCluster.phi, nextCluster.phi)) < phiCut) { + if (storedTracklets < maxTrackletsPerCluster) { + if constexpr (!dryRun) { + if constexpr (Mode == TrackletMode::Layer0Layer1) { + tracklets[0][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iNextLayerClusterIndex, (int)iCurrentLayerClusterIndex, nextCluster, currentCluster, (short)targetRofId, (short)pivotRofId}; + } else { + tracklets[1][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{(int)iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, (short)pivotRofId, (short)targetRofId}; + } + } + ++storedTracklets; + } + } + } + } + } + } + if constexpr (dryRun) { + for (int32_t i{0}; i < (int32_t)((2 * deltaRof) + 1); ++i) { + trackletsPerCluster[iCurrentLayerClusterIndex] += storedTrackletsLocal[i]; + } + } + } + } +} + +template +GPUg() void computeTrackletSelectionMutliROFKernel(const Cluster** GPUrestrict() clusters, + maybe_const** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() rofClusters, + const float phiCut, + const float tanLambdaCut, + const Tracklet** GPUrestrict() tracklets, + uint8_t* GPUrestrict() usedTracklets, + const int32_t** GPUrestrict() trackletOffsets, + const int32_t** GPUrestrict() trackletLUTs, + maybe_const* lineOffsets, + maybe_const* GPUrestrict() lines, + const int32_t nRofs, + const int32_t deltaRof, + const int32_t maxTracklets) +{ + for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < nRofs; pivotRofId += gridDim.x) { + const int16_t startROF = o2::gpu::CAMath::Max(0, (int32_t)pivotRofId - deltaRof); + const int16_t endROF = o2::gpu::CAMath::Min(nRofs, (int32_t)pivotRofId + deltaRof + 1); + + const uint32_t clusterOffset = rofClusters[1][pivotRofId]; + const uint32_t nClustersCurrentLayer = rofClusters[1][pivotRofId + 1] - clusterOffset; + if (nClustersCurrentLayer <= 0) { + continue; + } + + auto linesPerCluster = getNLinesPerCluster(pivotRofId, nRofs, rofClusters, lineOffsets); + auto nTrackletsPerCluster01 = getNTrackletsPerCluster(pivotRofId, nRofs, 0, rofClusters, trackletOffsets); + auto nTrackletsPerCluster12 = getNTrackletsPerCluster(pivotRofId, nRofs, 1, rofClusters, trackletOffsets); + + for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < nClustersCurrentLayer; iCurrentLayerClusterIndex += blockDim.x) { + int32_t validTracklets{0}; + const int32_t nTracklets01 = nTrackletsPerCluster01[iCurrentLayerClusterIndex]; + const int32_t nTracklets12 = nTrackletsPerCluster12[iCurrentLayerClusterIndex]; + for (int32_t iTracklet12{0}; iTracklet12 < nTracklets12; ++iTracklet12) { + for (int32_t iTracklet01{0}; iTracklet01 < nTracklets01; ++iTracklet01) { + + if (usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01]) { + continue; + } + + const auto& GPUrestrict() tracklet01 { tracklets[0][trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] }; + const auto& GPUrestrict() tracklet12 { tracklets[1][trackletLUTs[1][clusterOffset + iCurrentLayerClusterIndex] + iTracklet12] }; + const int16_t rof0 = tracklet01.rof[0]; + const int16_t rof2 = tracklet12.rof[1]; + if (deltaRof > 0 && ((rof0 < startROF) || (rof0 >= endROF) || (rof2 < startROF) || (rof2 >= endROF) || (o2::gpu::CAMath::Abs(rof0 - rof2) > deltaRof))) { + continue; + } + + const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)}; + const float deltaPhi{o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(tracklet01.phi, tracklet12.phi))}; + // + if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets < maxTracklets) { + // TODO use atomics to avoid race conditions for torn writes but is it needed here? + usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] = 1; + if constexpr (dryRun) { + usedClusters[0][rofClusters[0][rof0] + tracklet01.firstClusterIndex] = 1; + usedClusters[2][rofClusters[2][rof2] + tracklet12.secondClusterIndex] = 1; + } else { + const Cluster* clusters0 = clusters[0] + rofClusters[0][tracklet01.rof[0]]; + const Cluster* clusters1 = clusters[1] + rofClusters[1][tracklet01.rof[1]]; + lines[lineOffsets[iCurrentLayerClusterIndex] + validTracklets] = Line(tracklet01, clusters0, clusters1); + } + ++validTracklets; + } + } + } + + if constexpr (dryRun) { + linesPerCluster[iCurrentLayerClusterIndex] = validTracklets; + } + } + } +} + +template +GPUg() void compileTrackletsPerROFKernel(const int32_t nRofs, + int** GPUrestrict() nTrackletsPerROF, + const int32_t** GPUrestrict() rofClusters, + const int32_t** GPUrestrict() nTrackletsPerCluster) +{ + // TODO is this the best reduction kernel? + constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; + extern __shared__ int32_t ssum[]; + for (uint32_t rof = blockIdx.x; rof < (uint32_t)nRofs; rof += gridDim.x) { + const auto& GPUrestrict() currentNTracklets = getNTrackletsPerCluster(rof, nRofs, iMode, rofClusters, nTrackletsPerCluster); + int32_t localSum = 0; + for (uint32_t ci = threadIdx.x; ci < (uint32_t)currentNTracklets.size(); ci += blockDim.x) { + localSum += currentNTracklets[ci]; + } + ssum[threadIdx.x] = localSum; + __syncthreads(); + for (uint32_t stride = blockDim.x / 2; stride > 0; stride >>= 1) { + if (threadIdx.x < stride) { + ssum[threadIdx.x] += ssum[threadIdx.x + stride]; + } + __syncthreads(); + } + if (threadIdx.x == 0) { + nTrackletsPerROF[iMode][rof] = ssum[0]; + } + } +} + +template +GPUhi() void cubExclusiveScan(const T* GPUrestrict() in, T* GPUrestrict() out, int32_t num_items, cudaStream_t stream) +{ + void* d_temp_storage = nullptr; + size_t temp_storage_bytes = 0; + GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); + GPUChkErrS(cudaMallocAsync(&d_temp_storage, temp_storage_bytes, stream)); + GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); + GPUChkErrS(cudaFreeAsync(d_temp_storage, stream)); +} + +} // namespace gpu + +template +void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int32_t vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + int32_t** GPUrestrict() trackletsPerClusterLUTs, + int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + int32_t** GPUrestrict() trackletsPerROF, + const std::array& trackletsPerClusterLUTsHost, + const std::array& trackletsPerClusterSumLUTsHost, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams) +{ + const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); + gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + phiCut, + nullptr, + trackletsPerClusterLUTs, + utils, + nRofs, + deltaROF, + rofPV, + iteration, + vertPerRofThreshold, + maxTrackletsPerCluster); + gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int32_t**)trackletsPerClusterLUTs); + gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[0], trackletsPerClusterSumLUTsHost[0], nClusters, streams[0].get()); + + gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + phiCut, + nullptr, + trackletsPerClusterLUTs, + utils, + nRofs, + deltaROF, + rofPV, + iteration, + vertPerRofThreshold, + maxTrackletsPerCluster); + gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int**)trackletsPerClusterLUTs); + gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[1], trackletsPerClusterSumLUTsHost[1], nClusters, streams[1].get()); +} + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + Tracklet** GPUrestrict() foundTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + const int32_t** GPUrestrict() trackletsPerROF, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams) +{ + const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); + gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + phiCut, + foundTracklets, + trackletsPerClusterSumLUTs, + utils, + nRofs, + deltaROF, + rofPV, + iteration, + vertPerRofThreshold, + maxTrackletsPerCluster); + gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + phiCut, + foundTracklets, + trackletsPerClusterSumLUTs, + utils, + nRofs, + deltaROF, + rofPV, + iteration, + vertPerRofThreshold, + maxTrackletsPerCluster); +} + +void countTrackletsMatchingInROFsHandler(const int32_t nRofs, + const int32_t deltaROF, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const Cluster** GPUrestrict() clusters, + uint8_t** GPUrestrict() usedClusters, + const Tracklet** GPUrestrict() foundTracklets, + uint8_t* GPUrestrict() usedTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + int32_t* GPUrestrict() linesPerClusterLUT, + int32_t* GPUrestrict() linesPerClusterSumLUT, + const int32_t iteration, + const float phiCut, + const float tanLambdaCut, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams) +{ + streams[1].sync(); // need to make sure that all tracklets are done, since this placed in 0 tracklet01 will be done but tracklet12 needs to be guaranteed + gpu::computeTrackletSelectionMutliROFKernel<<>>(nullptr, + usedClusters, + ROFClusters, + phiCut, + tanLambdaCut, + foundTracklets, + usedTracklets, + trackletsPerClusterLUTs, + trackletsPerClusterSumLUTs, + linesPerClusterLUT, + nullptr, + nRofs, + deltaROF, + 100); + gpu::cubExclusiveScan(linesPerClusterLUT, linesPerClusterSumLUT, nClusters, streams[0].get()); +} + +void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, + const int32_t deltaROF, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const Cluster** GPUrestrict() clusters, + const uint8_t** GPUrestrict() usedClusters, + const Tracklet** GPUrestrict() foundTracklets, + uint8_t* GPUrestrict() usedTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + const int32_t* GPUrestrict() linesPerClusterSumLUT, + Line* GPUrestrict() lines, + const int32_t iteration, + const float phiCut, + const float tanLambdaCut, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams) +{ + gpu::computeTrackletSelectionMutliROFKernel<<>>(clusters, + nullptr, + ROFClusters, + phiCut, + tanLambdaCut, + foundTracklets, + usedTracklets, + trackletsPerClusterLUTs, + trackletsPerClusterSumLUTs, + linesPerClusterSumLUT, + lines, + nRofs, + deltaROF, + 100); +} + +/// Explicit instantiation of ITS2 handlers +template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int32_t vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + int32_t** trackletsPerClusterLUTs, + int32_t** trackletsPerClusterSumLUTs, + int32_t** trackletsPerROF, + const std::array& trackletsPerClusterLUTsHost, + const std::array& trackletsPerClusterSumLUTsHost, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); + +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, + const uint8_t* GPUrestrict() multMask, + const int32_t nRofs, + const int32_t deltaROF, + const int32_t* GPUrestrict() rofPV, + const int vertPerRofThreshold, + const Cluster** GPUrestrict() clusters, + const uint32_t nClusters, + const int32_t** GPUrestrict() ROFClusters, + const uint8_t** GPUrestrict() usedClusters, + const int32_t** GPUrestrict() clustersIndexTables, + Tracklet** GPUrestrict() foundTracklets, + const int32_t** GPUrestrict() trackletsPerClusterLUTs, + const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, + const int32_t** GPUrestrict() trackletsPerROF, + const int32_t iteration, + const float phiCut, + const int32_t maxTrackletsPerCluster, + const int32_t nBlocks, + const int32_t nThreads, + gpu::Streams& streams); +/* +GPUg() void lineClustererMultipleRof( + const int* sizeClustersL1, // Number of clusters on layer 1 per ROF + Line* lines, // Lines + int* nFoundLines, // Number of found lines + int* nExclusiveFoundLines, // Number of found lines exclusive scan + int* clusteredLines, // Clustered lines + const unsigned int startRofId, // Starting ROF ID + const unsigned int rofSize, // Number of ROFs to consider // Number of found lines exclusive scan + const float pairCut) // Selection on line pairs +{ + for (unsigned int iRof{threadIdx.x}; iRof < rofSize; iRof += blockDim.x) { + auto rof = iRof + startRofId; + auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; // starting cluster offset for this ROF + auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; // number of clusters for this ROF + auto linesOffsetRof = nExclusiveFoundLines[clustersL1offsetRof]; // starting line offset for this ROF + // auto* foundLinesRof = nFoundLines + clustersL1offsetRof; + auto nLinesRof = nExclusiveFoundLines[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; + // printf("rof: %d -> %d lines.\n", rof, nLinesRof); + for (int iLine1 = 0; iLine1 < nLinesRof; ++iLine1) { + auto absLine1Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine1; + if (clusteredLines[absLine1Index] > -1) { + continue; + } + for (int iLine2 = iLine1 + 1; iLine2 < nLinesRof; ++iLine2) { + auto absLine2Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine2; + if (clusteredLines[absLine2Index] > -1) { + continue; + } + + if (Line::getDCA(lines[absLine1Index], lines[absLine2Index]) < pairCut) { + ClusterLinesGPU tmpClus{lines[absLine1Index], lines[absLine2Index]}; + float tmpVertex[3]; + tmpVertex[0] = tmpClus.getVertex()[0]; + tmpVertex[1] = tmpClus.getVertex()[1]; + tmpVertex[2] = tmpClus.getVertex()[2]; + if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { // outside the beampipe, skip it + break; + } + clusteredLines[absLine1Index] = iLine1; // We set local index of first line to contribute, so we can retrieve the cluster later + clusteredLines[absLine2Index] = iLine1; + for (int iLine3 = 0; iLine3 < nLinesRof; ++iLine3) { + auto absLine3Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine3; + if (clusteredLines[absLine3Index] > -1) { + continue; + } + if (Line::getDistanceFromPoint(lines[absLine3Index], tmpVertex) < pairCut) { + clusteredLines[absLine3Index] = iLine1; + } + } + break; + } + } + } + } // rof loop +} + +GPUg() void computeCentroidsKernel( + Line* lines, + int* nFoundLines, + int* nExclusiveFoundLines, + const unsigned int nClustersMiddleLayer, + float* centroids, + const float lowHistX, + const float highHistX, + const float lowHistY, + const float highHistY, + const float pairCut) +{ + const int nLines = nExclusiveFoundLines[nClustersMiddleLayer - 1] + nFoundLines[nClustersMiddleLayer - 1]; + const int maxIterations{nLines * (nLines - 1) / 2}; + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { + int iFirstLine = currentThreadIndex / nLines; + int iSecondLine = currentThreadIndex % nLines; + // All unique pairs + if (iSecondLine <= iFirstLine) { + iFirstLine = nLines - iFirstLine - 2; + iSecondLine = nLines - iSecondLine - 1; + } + if (Line::getDCA(lines[iFirstLine], lines[iSecondLine]) < pairCut) { + ClusterLinesGPU cluster{lines[iFirstLine], lines[iSecondLine]}; + if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { + // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); + centroids[2 * currentThreadIndex] = cluster.getVertex()[0]; + centroids[2 * currentThreadIndex + 1] = cluster.getVertex()[1]; + } else { + // write values outside the histogram boundaries, + // default behaviour is not to have them added to histogram later + // (writing zeroes would be problematic) + centroids[2 * currentThreadIndex] = 2 * lowHistX; + centroids[2 * currentThreadIndex + 1] = 2 * lowHistY; + } + } else { + // write values outside the histogram boundaries, + // default behaviour is not to have them added to histogram later + // (writing zeroes would be problematic) + centroids[2 * currentThreadIndex] = 2 * highHistX; + centroids[2 * currentThreadIndex + 1] = 2 * highHistY; + } + } +} + +GPUg() void computeZCentroidsKernel( + const int nLines, + const cub::KeyValuePair* tmpVtX, + float* beamPosition, + Line* lines, + float* centroids, + const int* histX, // X + const float lowHistX, + const float binSizeHistX, + const int nBinsHistX, + const int* histY, // Y + const float lowHistY, + const float binSizeHistY, + const int nBinsHistY, + const float lowHistZ, // Z + const float pairCut, + const int binOpeningX, + const int binOpeningY) +{ + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { + if (tmpVtX[0].value || tmpVtX[1].value) { + float tmpX{lowHistX + tmpVtX[0].key * binSizeHistX + binSizeHistX / 2}; + int sumWX{tmpVtX[0].value}; + float wX{tmpX * tmpVtX[0].value}; + for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[0].key + binOpeningX + 1, nBinsHistX - 1); ++iBin) { + if (iBin != tmpVtX[0].key) { + wX += (lowHistX + iBin * binSizeHistX + binSizeHistX / 2) * histX[iBin]; + sumWX += histX[iBin]; + } + } + float tmpY{lowHistY + tmpVtX[1].key * binSizeHistY + binSizeHistY / 2}; + int sumWY{tmpVtX[1].value}; + float wY{tmpY * tmpVtX[1].value}; + for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[1].key + binOpeningY + 1, nBinsHistY - 1); ++iBin) { + if (iBin != tmpVtX[1].key) { + wY += (lowHistY + iBin * binSizeHistY + binSizeHistY / 2) * histY[iBin]; + sumWY += histY[iBin]; + } + } + beamPosition[0] = wX / sumWX; + beamPosition[1] = wY / sumWY; + float mockBeamPoint1[3] = {beamPosition[0], beamPosition[1], -1}; // get two points laying at different z, to create line object + float mockBeamPoint2[3] = {beamPosition[0], beamPosition[1], 1}; + Line pseudoBeam = {mockBeamPoint1, mockBeamPoint2}; + if (Line::getDCA(lines[currentThreadIndex], pseudoBeam) < pairCut) { + ClusterLinesGPU cluster{lines[currentThreadIndex], pseudoBeam}; + centroids[currentThreadIndex] = cluster.getVertex()[2]; + } else { + centroids[currentThreadIndex] = 2 * lowHistZ; + } + } + } +} + +GPUg() void computeVertexKernel( + cub::KeyValuePair* tmpVertexBins, + int* histZ, // Z + const float lowHistZ, + const float binSizeHistZ, + const int nBinsHistZ, + Vertex* vertices, + float* beamPosition, + const int vertIndex, + const int minContributors, + const int binOpeningZ) +{ + for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { + if (currentThreadIndex == 0) { + if (tmpVertexBins[2].value > 1 && (tmpVertexBins[0].value || tmpVertexBins[1].value)) { + float z{lowHistZ + tmpVertexBins[2].key * binSizeHistZ + binSizeHistZ / 2}; + float ex{0.f}; + float ey{0.f}; + float ez{0.f}; + int sumWZ{tmpVertexBins[2].value}; + float wZ{z * tmpVertexBins[2].value}; + for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVertexBins[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVertexBins[2].key + binOpeningZ + 1, nBinsHistZ - 1); ++iBin) { + if (iBin != tmpVertexBins[2].key) { + wZ += (lowHistZ + iBin * binSizeHistZ + binSizeHistZ / 2) * histZ[iBin]; + sumWZ += histZ[iBin]; + } + histZ[iBin] = 0; + } + if (sumWZ > minContributors || vertIndex == 0) { + new (vertices + vertIndex) Vertex{o2::math_utils::Point3D(beamPosition[0], beamPosition[1], wZ / sumWZ), std::array{ex, 0, ey, 0, 0, ez}, static_cast(sumWZ), 0}; + } else { + new (vertices + vertIndex) Vertex{}; + } + } else { + new (vertices + vertIndex) Vertex{}; + } + } + } +} +*/ +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore b/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore deleted file mode 100644 index 5854f8830a79b..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Don't track generated hip sources -*.hip.cxx \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index cba23d5119e41..a40aac491a386 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -10,49 +10,25 @@ # or submit itself to any jurisdiction. if(HIP_ENABLED) - # Hipify-perl to generate HIP sources - set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") - file(GLOB CUDA_SOURCES_FULL_PATH "../cuda/*.cu") - foreach(file ${CUDA_SOURCES_FULL_PATH}) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) - get_filename_component(CUDA_SOURCE ${file} NAME) - string(REPLACE ".cu" "" CUDA_SOURCE_NAME ${CUDA_SOURCE}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} - ) - endforeach() - - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${baseTargetName} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) - set(CMAKE_CXX_EXTENSIONS OFF) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") - message(STATUS "Building ITS HIP tracker") - o2_add_library(ITStrackingHIP - SOURCES ClusterLinesGPU.hip.cxx - Context.hip.cxx - TimeFrameGPU.hip.cxx - Stream.hip.cxx - TrackerTraitsGPU.hip.cxx - TracerGPU.hip.cxx - VertexerTraitsGPU.hip.cxx - Utils.hip.cxx + set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -fgpu-rdc") + # set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} -O0 -g -ggdb -fno-inline -fno-omit-frame-pointer -D__HIP_ENABLE_DEVICE_ASSERT__") + # add_compile_definitions(ITS_MEASURE_GPU_TIME) + # add_compile_definitions(ITS_GPU_LOG) + o2_add_hipified_library(ITStrackingHIP + SOURCES ../cuda/ClusterLinesGPU.cu + ../cuda/TimeFrameGPU.cu + ../cuda/TrackerTraitsGPU.cxx + ../cuda/TracerGPU.cu + ../cuda/TrackingKernels.cu + ../cuda/VertexingKernels.cu + ../cuda/VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::GPUTracking + O2::SimulationDataFormat + O2::ReconstructionDataFormats hip::host - hip::device - hip::hipcub + PRIVATE_LINK_LIBRARIES O2::GPUTrackingHIPExternalProvider TARGETVARNAME targetName) - - target_compile_definitions( - ${targetName} PRIVATE $) - - if(O2_HIP_CMAKE_LINK_FLAGS) - # Need to add gpu target also to link flags due to gpu-rdc option - target_link_options(${targetName} PUBLIC ${O2_HIP_CMAKE_LINK_FLAGS}) - endif() endif() diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h deleted file mode 100644 index 971ae6a7fe83a..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ArrayUtils.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ArrayUtils.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ -#define TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ - -#include -#include -#include - -namespace o2 -{ -namespace its -{ -namespace CA -{ - -namespace ArrayUtils -{ -template -constexpr std::array fillArray(Initializer, std::index_sequence); -template -constexpr std::array fillArray(Initializer); -} // namespace ArrayUtils - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer, std::index_sequence) -{ - return std::array{{initializer(Is)...}}; -} - -template -constexpr std::array ArrayUtils::fillArray(Initializer initializer) -{ - return ArrayUtils::fillArray(initializer, std::make_index_sequence{}); -} -} // namespace CA -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ARRAYUTILS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h new file mode 100644 index 0000000000000..66634c1a07eea --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -0,0 +1,196 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file BoundedAllocator.h +/// \brief +/// + +#ifndef TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ +#define TRACKINGITSU_INCLUDE_BOUNDEDALLOCATOR_H_ + +#include +#include +#include +#include +#include + +#include "ITStracking/ExternalAllocator.h" + +#include "GPUCommonLogger.h" + +namespace o2::its +{ + +class BoundedMemoryResource final : public std::pmr::memory_resource +{ + public: + class MemoryLimitExceeded final : public std::bad_alloc + { + public: + MemoryLimitExceeded(size_t attempted, size_t used, size_t max) + : mAttempted(attempted), mUsed(used), mMax(max) {} + const char* what() const noexcept final + { + static thread_local char msg[256]; + if (mAttempted != 0) { + snprintf(msg, sizeof(msg), + "Reached set memory limit (attempted: %zu, used: %zu, max: %zu)", + mAttempted, mUsed, mMax); + } else { + snprintf(msg, sizeof(msg), + "New set maximum below current used (newMax: %zu, used: %zu)", + mMax, mUsed); + } + return msg; + } + + private: + size_t mAttempted{0}, mUsed{0}, mMax{0}; + }; + + BoundedMemoryResource(size_t maxBytes = std::numeric_limits::max(), std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) + : mMaxMemory(maxBytes), mUpstream(upstream) {} + BoundedMemoryResource(ExternalAllocator* alloc) : mAdaptor(std::make_unique(alloc)), mUpstream(mAdaptor.get()) {} + + void* do_allocate(size_t bytes, size_t alignment) final + { + size_t new_used{0}, current_used{mUsedMemory.load(std::memory_order_relaxed)}; + do { + new_used = current_used + bytes; + if (new_used > mMaxMemory) { + ++mCountThrow; + throw MemoryLimitExceeded(new_used, current_used, mMaxMemory); + } + } while (!mUsedMemory.compare_exchange_weak(current_used, new_used, + std::memory_order_acq_rel, + std::memory_order_relaxed)); + void* p{nullptr}; + try { + p = mUpstream->allocate(bytes, alignment); + } catch (...) { + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); + throw; + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t alignment) final + { + mUpstream->deallocate(p, bytes, alignment); + mUsedMemory.fetch_sub(bytes, std::memory_order_relaxed); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final + { + return this == &other; + } + + size_t getUsedMemory() const noexcept { return mUsedMemory.load(); } + size_t getMaxMemory() const noexcept { return mMaxMemory; } + void setMaxMemory(size_t max) + { + size_t used = mUsedMemory.load(std::memory_order_acquire); + if (used > max) { + ++mCountThrow; + throw MemoryLimitExceeded(0, used, max); + } + mMaxMemory.store(max, std::memory_order_release); + } + + void print() const + { +#if !defined(GPUCA_GPUCODE_DEVICE) + constexpr double GB{1024 * 1024 * 1024}; + auto throw_ = mCountThrow.load(std::memory_order_relaxed); + auto used = static_cast(mUsedMemory.load(std::memory_order_relaxed)); + LOGP(info, "maxthrow={} maxmem={:.2f} GB used={:.2f} ({:.2f}%)", + throw_, (double)mMaxMemory / GB, used / GB, 100. * used / (double)mMaxMemory); +#endif + } + + private: + std::atomic mMaxMemory{std::numeric_limits::max()}; + std::atomic mCountThrow{0}; + std::atomic mUsedMemory{0}; + std::unique_ptr mAdaptor{nullptr}; + std::pmr::memory_resource* mUpstream{nullptr}; +}; + +template +using bounded_vector = std::pmr::vector; + +template +inline void deepVectorClear(std::vector& vec) +{ + std::vector().swap(vec); +} + +template +inline void deepVectorClear(bounded_vector& vec, std::pmr::memory_resource* mr = nullptr) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void deepVectorClear(std::vector>& vec, std::pmr::memory_resource* mr = nullptr) +{ + for (auto& v : vec) { + deepVectorClear(v, mr); + } +} + +template +inline void deepVectorClear(std::array, S>& arr, std::pmr::memory_resource* mr = nullptr) +{ + for (size_t i{0}; i < S; ++i) { + deepVectorClear(arr[i], mr); + } +} + +template +inline void clearResizeBoundedVector(bounded_vector& vec, size_t sz, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + std::pmr::memory_resource* tmr = (mr != nullptr) ? mr : vec.get_allocator().resource(); + vec.~bounded_vector(); + new (&vec) bounded_vector(sz, def, std::pmr::polymorphic_allocator{tmr}); +} + +template +inline void clearResizeBoundedVector(std::vector>& vec, size_t size, std::pmr::memory_resource* mr) +{ + vec.clear(); + vec.reserve(size); + for (size_t i = 0; i < size; ++i) { + vec.emplace_back(std::pmr::polymorphic_allocator>{mr}); + } +} + +template +inline void clearResizeBoundedArray(std::array, S>& arr, size_t size, std::pmr::memory_resource* mr = nullptr, T def = T()) +{ + for (size_t i{0}; i < S; ++i) { + clearResizeBoundedVector(arr[i], size, mr, def); + } +} + +template +inline std::vector toSTDVector(const bounded_vector& b) +{ + std::vector t(b.size()); + std::copy(b.cbegin(), b.cend(), t.begin()); + return t; +} + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index 49dd34577ac65..902092a510eb0 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -16,69 +16,87 @@ #ifndef TRACKINGITSU_INCLUDE_CACELL_H_ #define TRACKINGITSU_INCLUDE_CACELL_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif - +#include "ITStracking/Constants.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { class Cell final { public: - GPUhd() Cell(); - GPUd() Cell(const int, const int, const int, const int, const int, const float); - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; GPUhd() int getFirstTrackletIndex() const { return mFirstTrackletIndex; }; GPUhd() int getSecondTrackletIndex() const { return mSecondTrackletIndex; }; - GPUhd() float getTanLambda() const { return mTanLambda; }; GPUhd() int getLevel() const { return mLevel; }; GPUhd() void setLevel(const int level) { mLevel = level; }; GPUhd() int* getLevelPtr() { return &mLevel; } private: - const int mFirstClusterIndex; - const int mSecondClusterIndex; - const int mThirdClusterIndex; - const int mFirstTrackletIndex; - const int mSecondTrackletIndex; - const float mTanLambda; - int mLevel; + int mFirstClusterIndex{constants::UnusedIndex}; + int mSecondClusterIndex{constants::UnusedIndex}; + int mThirdClusterIndex{constants::UnusedIndex}; + int mFirstTrackletIndex{constants::UnusedIndex}; + int mSecondTrackletIndex{constants::UnusedIndex}; + int mLevel{constants::UnusedIndex}; }; -GPUhdi() Cell::Cell() - : mFirstClusterIndex{0}, - mSecondClusterIndex{0}, - mThirdClusterIndex{0}, - mFirstTrackletIndex{0}, - mSecondTrackletIndex{0}, - mTanLambda{0}, - mLevel{0} +template +class CellSeed final : public o2::track::TrackParCovF { - // Nothing to do -} + public: + GPUhdDefault() CellSeed() = default; + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1) + { + mClusters.fill(constants::UnusedIndex); + setUserField(innerL); + mClusters[innerL + 0] = cl0; + mClusters[innerL + 1] = cl1; + mClusters[innerL + 2] = cl2; + mTracklets[0] = trkl0; + mTracklets[1] = trkl1; + } + GPUhdDefault() CellSeed(const CellSeed&) = default; + GPUhdDefault() ~CellSeed() = default; + // GPUhdDefault() CellSeed(CellSeed&&) = default; TODO cannot use this yet since TrackPar only has device + GPUhdDefault() CellSeed& operator=(const CellSeed&) = default; + GPUhdDefault() CellSeed& operator=(CellSeed&&) = default; -GPUdi() Cell::Cell(const int firstClusterIndex, const int secondClusterIndex, const int thirdClusterIndex, - const int firstTrackletIndex, const int secondTrackletIndex, const float tanL) - : mFirstClusterIndex{firstClusterIndex}, - mSecondClusterIndex{secondClusterIndex}, - mThirdClusterIndex{thirdClusterIndex}, - mFirstTrackletIndex{firstTrackletIndex}, - mSecondTrackletIndex{secondTrackletIndex}, - mTanLambda{tanL}, - mLevel{1} -{ - // Nothing to do -} + GPUhd() int getFirstClusterIndex() const { return mClusters[getUserField()]; }; + GPUhd() int getSecondClusterIndex() const { return mClusters[getUserField() + 1]; }; + GPUhd() int getThirdClusterIndex() const { return mClusters[getUserField() + 2]; }; + GPUhd() int getFirstTrackletIndex() const { return mTracklets[0]; }; + GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; + GPUhd() int getSecondTrackletIndex() const { return mTracklets[1]; }; + GPUhd() void setSecondTrackletIndex(int trkl) { mTracklets[1] = trkl; }; + GPUhd() float getChi2() const { return mChi2; }; + GPUhd() void setChi2(float chi2) { mChi2 = chi2; }; + GPUhd() int getLevel() const { return mLevel; }; + GPUhd() void setLevel(int level) { mLevel = level; }; + GPUhd() int* getLevelPtr() { return &mLevel; } + GPUhd() auto& getClusters() { return mClusters; } + GPUhd() int getCluster(int i) const { return mClusters[i]; } + GPUhd() void printCell() const + { + printf("cell: %d, %d\t lvl: %d\t chi2: %f\tcls: [", mTracklets[0], mTracklets[1], mLevel, mChi2); + for (int i = 0; i < nLayers; ++i) { + printf("%d", mClusters[i]); + if (i < nLayers - 1) { + printf(" | "); + } + } + printf("]\n"); + } + + private: + float mChi2 = -999.f; + int mLevel = constants::UnusedIndex; + std::array mTracklets = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); +}; + +} // namespace o2::its -} // namespace its -} // namespace o2 #endif /* TRACKINGITSU_INCLUDE_CACELL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index 4004fad2f7de2..b96f0558943a6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -16,61 +16,67 @@ #ifndef TRACKINGITSU_INCLUDE_CACLUSTER_H_ #define TRACKINGITSU_INCLUDE_CACLUSTER_H_ -#ifndef GPUCA_GPUCODE_DEVICE #include -#endif - +#include "ITStracking/Constants.h" #include "GPUCommonRtypes.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/MathUtils.h" -namespace o2 -{ -namespace its +namespace o2::its { +template class IndexTableUtils; struct Cluster final { - Cluster() = default; - Cluster(const float x, const float y, const float z, const int idx); - Cluster(const int, const IndexTableUtils& utils, const Cluster&); - Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); - void Init(const int, const float3&, const IndexTableUtils& utils, const Cluster&); - bool operator==(const Cluster&) const; + GPUhdDefault() Cluster() = default; + GPUhd() Cluster(const float x, const float y, const float z, const int idx); + template + GPUhd() Cluster(const int, const IndexTableUtils& utils, const Cluster&); + template + GPUhd() Cluster(const int, const float3&, const IndexTableUtils& utils, const Cluster&); + GPUhdDefault() Cluster(const Cluster&) = default; + GPUhdDefault() Cluster(Cluster&&) noexcept = default; + GPUhdDefault() ~Cluster() = default; + + GPUhdDefault() Cluster& operator=(const Cluster&) = default; + GPUhdDefault() Cluster& operator=(Cluster&&) noexcept = default; + GPUhdDefault() bool operator==(const Cluster&) const = default; + GPUhd() void print() const; - float xCoordinate; // = -999.f; - float yCoordinate; // = -999.f; - float zCoordinate; // = -999.f; - float phi; // = -999.f; - float radius; // = -999.f; - int clusterId; // = -1; - int indexTableBinIndex; // = -1; + float xCoordinate{-999.f}; + float yCoordinate{-999.f}; + float zCoordinate{-999.f}; + float phi{-999.f}; + float radius{-999.f}; + int clusterId{constants::UnusedIndex}; + int indexTableBinIndex{constants::UnusedIndex}; ClassDefNV(Cluster, 1); }; -GPUhdi() void Cluster::print() const -{ - printf("Cluster: %f %f %f %f %f %d %d\n", xCoordinate, yCoordinate, zCoordinate, phi, radius, clusterId, indexTableBinIndex); -} +struct TrackingFrameInfo final { + GPUhdDefault() TrackingFrameInfo() = default; + GPUhd() TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, std::array&& posTF, std::array&& covTF); + GPUhdDefault() TrackingFrameInfo(const TrackingFrameInfo&) = default; + GPUhdDefault() TrackingFrameInfo(TrackingFrameInfo&&) noexcept = default; + GPUhdDefault() ~TrackingFrameInfo() = default; + + GPUhdDefault() TrackingFrameInfo& operator=(const TrackingFrameInfo&) = default; + GPUhdDefault() TrackingFrameInfo& operator=(TrackingFrameInfo&&) = default; -struct TrackingFrameInfo { - TrackingFrameInfo() = default; - TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, GPUArray&& posTF, GPUArray&& covTF); + GPUhd() void print() const; - float xCoordinate; - float yCoordinate; - float zCoordinate; - float xTrackingFrame; - float alphaTrackingFrame; - GPUArray positionTrackingFrame = {-1., -1.}; - GPUArray covarianceTrackingFrame = {999., 999., 999.}; + float xCoordinate{-999.f}; + float yCoordinate{-999.f}; + float zCoordinate{-999.f}; + float xTrackingFrame{-999.f}; + float alphaTrackingFrame{-999.f}; + std::array positionTrackingFrame = {constants::UnusedIndex, constants::UnusedIndex}; + std::array covarianceTrackingFrame = {999., 999., 999.}; ClassDefNV(TrackingFrameInfo, 1); }; -} // namespace its -} // namespace o2 + +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_CACLUSTER_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 41077c2f0eeb1..0e7ad474ae455 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -9,42 +9,42 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_ITSMFT_TRACKING_LINE_H_ -#define O2_ITSMFT_TRACKING_LINE_H_ +#ifndef O2_ITS_CLUSTERLINES_H +#define O2_ITS_CLUSTERLINES_H #include #include #include "ITStracking/Cluster.h" -#include "ITStracking/Definitions.h" +#include "ITStracking/Constants.h" #include "ITStracking/Tracklet.h" +#include "GPUCommonRtypes.h" #include "GPUCommonMath.h" -namespace o2 +namespace o2::its { -namespace its -{ - struct Line final { - GPUhd() Line(); + GPUhdDefault() Line() = default; GPUhd() Line(const Line&); Line(std::array firstPoint, std::array secondPoint); - GPUhd() Line(const float firstPoint[3], const float secondPoint[3]); GPUhd() Line(const Tracklet&, const Cluster*, const Cluster*); - inline static float getDistanceFromPoint(const Line& line, const std::array& point); + static float getDistanceFromPoint(const Line& line, const std::array& point); GPUhd() static float getDistanceFromPoint(const Line& line, const float point[3]); static std::array getDCAComponents(const Line& line, const std::array point); GPUhd() static void getDCAComponents(const Line& line, const float point[3], float destArray[6]); - GPUhd() static float getDCA(const Line&, const Line&, const float precision = 1e-14); - static bool areParallel(const Line&, const Line&, const float precision = 1e-14); + GPUhd() static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); + static bool areParallel(const Line&, const Line&, const float precision = constants::Tolerance); GPUhd() unsigned char isEmpty() const { return (originPoint[0] == 0.f && originPoint[1] == 0.f && originPoint[2] == 0.f) && (cosinesDirector[0] == 0.f && cosinesDirector[1] == 0.f && cosinesDirector[2] == 0.f); } + GPUhdi() auto getDeltaROF() const { return rof[1] - rof[0]; } GPUhd() void print() const; bool operator==(const Line&) const; bool operator!=(const Line&) const; + short getMinROF() const { return rof[0] < rof[1] ? rof[0] : rof[1]; } - float originPoint[3], cosinesDirector[3]; // std::array originPoint, cosinesDirector; - float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; // std::array weightMatrix; + float originPoint[3] = {0, 0, 0}; + float cosinesDirector[3] = {0, 0, 0}; + // float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; // weightMatrix is a symmetric matrix internally stored as // 0 --> row = 0, col = 0 // 1 --> 0,1 @@ -52,12 +52,10 @@ struct Line final { // 3 --> 1,1 // 4 --> 1,2 // 5 --> 2,2 - // Debug quantities -}; + short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; -GPUhdi() Line::Line() : weightMatrix{1., 0., 0., 1., 0., 1.} -{ -} + ClassDefNV(Line, 1); +}; GPUhdi() Line::Line(const Line& other) { @@ -65,23 +63,11 @@ GPUhdi() Line::Line(const Line& other) originPoint[i] = other.originPoint[i]; cosinesDirector[i] = other.cosinesDirector[i]; } - for (int i{0}; i < 6; ++i) { - weightMatrix[i] = other.weightMatrix[i]; - } -} - -GPUhdi() Line::Line(const float firstPoint[3], const float secondPoint[3]) -{ - for (int i{0}; i < 3; ++i) { - originPoint[i] = firstPoint[i]; - cosinesDirector[i] = secondPoint[i] - firstPoint[i]; - } - - float inverseNorm{1.f / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; - - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; + // for (int i{0}; i < 6; ++i) { + // weightMatrix[i] = other.weightMatrix[i]; + // } + for (int i{0}; i < 2; ++i) { + rof[i] = other.rof[i]; } } @@ -95,12 +81,13 @@ 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 / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; + float inverseNorm{1.f / o2::gpu::CAMath::Hypot(cosinesDirector[0], cosinesDirector[1], cosinesDirector[2])}; + cosinesDirector[0] *= inverseNorm; + cosinesDirector[1] *= inverseNorm; + cosinesDirector[2] *= inverseNorm; - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } + rof[0] = tracklet.rof[0]; + rof[1] = tracklet.rof[1]; } // static functions: @@ -120,47 +107,38 @@ inline float Line::getDistanceFromPoint(const Line& line, const std::array precision) { - return o2::gpu::CAMath::Abs(distance / o2::gpu::CAMath::Sqrt(norm)); - } else { -#if defined(__CUDACC__) || defined(__HIPCC__) - float stdOriginPoint[3]; - for (int i{0}; i < 3; ++i) { - stdOriginPoint[i] = secondLine.originPoint[1]; - } -#else - std::array stdOriginPoint = {}; - std::copy_n(secondLine.originPoint, 3, stdOriginPoint.begin()); -#endif - return getDistanceFromPoint(firstLine, stdOriginPoint); + const float nx = (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) - + (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]); + const float ny = -(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + + (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); + const float nz = (firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) - + (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); + const float norm2 = (nx * nx) + (ny * ny) + (nz * nz); + + if (norm2 <= precision * precision) { + return getDistanceFromPoint(firstLine, secondLine.originPoint); } + + const float dx = secondLine.originPoint[0] - firstLine.originPoint[0]; + const float dy = secondLine.originPoint[1] - firstLine.originPoint[1]; + const float dz = secondLine.originPoint[2] - firstLine.originPoint[2]; + const float triple = (dx * nx) + (dy * ny) + (dz * nz); + + return o2::gpu::CAMath::Abs(triple) / o2::gpu::CAMath::Sqrt(norm2); } GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) @@ -173,9 +151,9 @@ GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], flo destArray[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; destArray[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; destArray[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - destArray[1] = std::sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); - destArray[2] = std::sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); - destArray[4] = std::sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); + destArray[1] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); + destArray[2] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); + destArray[4] = o2::gpu::CAMath::Sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); } inline bool Line::operator==(const Line& rhs) const @@ -189,17 +167,13 @@ inline bool Line::operator==(const Line& rhs) const inline bool Line::operator!=(const Line& rhs) const { - bool val; - for (int i{0}; i < 3; ++i) { - val &= this->originPoint[i] != rhs.originPoint[i]; - } - return val; + return !(*this == rhs); } GPUhdi() void Line::print() const { - printf("Line: originPoint = (%f, %f, %f), cosinesDirector = (%f, %f, %f)\n", - originPoint[0], originPoint[1], originPoint[2], cosinesDirector[0], cosinesDirector[1], cosinesDirector[2]); + printf("Line: originPoint = (%f, %f, %f), cosinesDirector = (%f, %f, %f), rofs = (%hd, %hd)\n", + originPoint[0], originPoint[1], originPoint[2], cosinesDirector[0], cosinesDirector[1], cosinesDirector[2], rof[0], rof[1]); } class ClusterLines final @@ -211,11 +185,13 @@ class ClusterLines final ClusterLines(const Line& firstLine, const Line& secondLine); void add(const int& lineLabel, const Line& line, const bool& weight = false); void computeClusterCentroid(); + void updateROFPoll(const Line&); inline std::vector& getLabels() { return mLabels; } inline int getSize() const { return mLabels.size(); } + inline short getROF() const { return mROF; } inline std::array getVertex() const { return mVertex; } inline std::array getRMS2() const { return mRMS2; } inline float getAvgDistance2() const { return mAvgDistance2; } @@ -230,8 +206,9 @@ class ClusterLines final std::array mVertex = {0.f}; // cluster centroid position std::array mRMS2 = {0.f}; // symmetric matrix: diagonal is RMS2 float mAvgDistance2 = 0.f; // substitute for chi2 + int mROFWeight = 0; // rof weight for voting + short mROF = constants::UnusedIndex; // rof }; -} // namespace its -} // namespace o2 -#endif /* O2_ITSMFT_TRACKING_LINE_H_ */ +} // namespace o2::its +#endif /* O2_ITS_CLUSTERLINES_H */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index b81b8b1705a21..10e1681c73e8d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -18,7 +18,7 @@ #ifndef GPUCA_GPUCODE_DEVICE #include -#include +#include #include #include #endif @@ -26,33 +26,15 @@ #include "DetectorsBase/Propagator.h" #include "ITStracking/Constants.h" -namespace o2 -{ -namespace its +namespace o2::its { -template -class Configuration : public Param -{ - public: - static Configuration& getInstance() - { - static Configuration instance; - return instance; - } - Configuration(const Configuration&) = delete; - const Configuration& operator=(const Configuration&) = delete; - - private: - Configuration() = default; -}; - struct TrackingParameters { - TrackingParameters& operator=(const TrackingParameters& t) = default; - - int CellMinimumLevel(); - int CellsPerRoad() const { return NLayers - 2; } - int TrackletsPerRoad() const { return NLayers - 1; } + int CellMinimumLevel() const noexcept { return MinTrackLength - constants::ClustersPerCell + 1; } + int NeighboursPerRoad() const noexcept { return NLayers - 3; } + int CellsPerRoad() const noexcept { return NLayers - 2; } + int TrackletsPerRoad() const noexcept { return NLayers - 1; } + std::string asString() const; int NLayers = 7; int DeltaROF = 0; @@ -64,10 +46,12 @@ struct TrackingParameters { std::vector SystErrorZ2 = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; int ZBins{256}; int PhiBins{128}; + int nROFsPerIterations = -1; bool UseDiamond = false; float Diamond[3] = {0.f, 0.f, 0.f}; /// General parameters + bool AllowSharingFirstCluster = false; int ClusterSharing = 0; int MinTrackLength = 7; float NSigmaCut = 5; @@ -80,25 +64,44 @@ struct TrackingParameters { float CellsPerClusterLimit = 2.f; /// Fitter parameters o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; - unsigned long MaxMemory = 12000000000UL; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; - bool UseTrackFollower = false; + int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this + std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; + uint16_t StartLayerMask = 0x7F; + bool RepeatRefitOut = false; // repeat outward refit using inward refit as a seed + bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster bool FindShortTracks = false; -}; + bool PerPrimaryVertexProcessing = false; + bool SaveTimeBenchmarks = false; + bool DoUPCIteration = false; + bool FataliseUponFailure = true; + /// Cluster attachment + bool UseTrackFollower = false; + bool UseTrackFollowerTop = false; + bool UseTrackFollowerBot = false; + bool UseTrackFollowerMix = false; + float TrackFollowerNSigmaCutZ = 1.f; + float TrackFollowerNSigmaCutPhi = 1.f; -inline int TrackingParameters::CellMinimumLevel() -{ - return MinTrackLength - constants::its::ClustersPerCell + 1; -} + bool createArtefactLabels{false}; + + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; +}; struct VertexingParameters { + std::string asString() const; + + int nIterations = 1; // Number of vertexing passes to perform + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round bool allowSingleContribClusters = false; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; int ZBins{1}; int PhiBins{128}; - + int deltaRof = 0; float zCut = 0.002f; float phiCut = 0.005f; float pairCut = 0.04f; @@ -114,54 +117,55 @@ struct VertexingParameters { int maxTrackletsPerCluster = 2e3; int phiSpan = -1; int zSpan = -1; + bool SaveTimeBenchmarks = false; + + bool useTruthSeeding = false; // overwrite found vertices with MC events + bool outputContLabels = false; int nThreads = 1; + bool PrintMemory = false; // print allocator usage in epilog report + size_t MaxMemory = std::numeric_limits::max(); + bool DropTFUponFailure = false; }; struct TimeFrameGPUParameters { - TimeFrameGPUParameters() = default; - TimeFrameGPUParameters(size_t cubBufferSize, - size_t maxTrkClu, - size_t cluLayCap, - size_t cluROfCap, - size_t maxTrkCap, - size_t maxVertCap, - size_t maxROFs); + std::string asString() const; size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes size_t maxTrackletsPerCluster = 1e2; size_t clustersPerLayerCapacity = 2.5e5; size_t clustersPerROfCapacity = 1.5e3; - size_t trackletsCapacity = maxTrackletsPerCluster * clustersPerROfCapacity; size_t validatedTrackletsCapacity = 1e3; size_t cellsLUTsize = validatedTrackletsCapacity; size_t maxNeighboursSize = 1e2; size_t neighboursLUTsize = maxNeighboursSize; + size_t maxRoadPerRofSize = 1e3; // pp! size_t maxLinesCapacity = 1e2; size_t maxVerticesCapacity = 5e4; size_t nMaxROFs = 1e3; size_t nTimeFrameChunks = 3; + size_t nROFsPerChunk = 768; // pp defaults int maxGPUMemoryGB = -1; }; -inline TimeFrameGPUParameters::TimeFrameGPUParameters(size_t cubBufferSize, - size_t maxTrkClu, - size_t cluLayCap, - size_t cluROfCap, - size_t maxTrkCap, - size_t maxVertCap, - size_t maxROFs) : tmpCUBBufferSize{cubBufferSize}, - maxTrackletsPerCluster{maxTrkClu}, - clustersPerLayerCapacity{cluLayCap}, - clustersPerROfCapacity{cluROfCap}, - maxLinesCapacity{maxTrkCap}, - maxVerticesCapacity{maxVertCap}, - nMaxROFs{maxROFs} +namespace TrackingMode { - trackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; -} +enum Type : int8_t { + Unset = -1, // Special value to leave a default in case we want to override via Configurable Params + Sync = 0, + Async = 1, + Cosmics = 2, + Off = 3, +}; + +Type fromString(std::string_view str); +std::string toString(Type mode); + +std::vector getTrackingParameters(Type mode); +std::vector getVertexingParameters(Type mode); + +}; // namespace TrackingMode -} // namespace its -} // namespace o2 +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_CONFIGURATION_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 3eaeee9ab30c9..22642f2e23229 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -16,108 +16,39 @@ #ifndef TRACKINGITSU_INCLUDE_CONSTANTS_H_ #define TRACKINGITSU_INCLUDE_CONSTANTS_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#endif +#include +#include #include "ITStracking/Definitions.h" -#include "CommonConstants/MathConstants.h" -#include "GPUCommonMath.h" -#include "GPUCommonDef.h" +#include "GPUCommonDefAPI.h" -namespace o2 -{ -namespace its +namespace o2::its::constants { -namespace constants -{ -constexpr float MB = 1024.f * 1024.f; -constexpr float GB = 1024.f * 1024.f * 1024.f; +constexpr float KB = 1024.f; +constexpr float MB = KB * KB; +constexpr float GB = MB * KB; constexpr bool DoTimeBenchmarks = true; +constexpr bool SaveTimeBenchmarks = false; -namespace math -{ -constexpr float Pi{3.14159265359f}; -constexpr float TwoPi{2.0f * Pi}; -constexpr float FloatMinThreshold{1e-20f}; -} // namespace math - -namespace its -{ -constexpr int LayersNumberVertexer{3}; -constexpr int ClustersPerCell{3}; -constexpr int UnusedIndex{-1}; -constexpr float Resolution{0.0005f}; - -GPUhdi() constexpr GPUArray VertexerHistogramVolume() -{ - return GPUArray{{1.98, 1.98, 40.f}}; -} -} // namespace its - -namespace its2 -{ -constexpr int LayersNumber{7}; -constexpr int TrackletsPerRoad{LayersNumber - 1}; -constexpr int CellsPerRoad{LayersNumber - 2}; - -GPUhdi() constexpr GPUArray LayersZCoordinate() -{ - constexpr double s = 1.; // safety margin - return GPUArray{{16.333f + s, 16.333f + s, 16.333f + s, 42.140f + s, 42.140f + s, 73.745f + s, 73.745f + s}}; -} -GPUhdi() constexpr GPUArray LayersRCoordinate() -{ - return GPUArray{{2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}}; -} - -constexpr int ZBins{256}; -constexpr int PhiBins{128}; -constexpr float InversePhiBinSize{PhiBins / constants::math::TwoPi}; -GPUhdi() constexpr GPUArray InverseZBinSize() -{ - constexpr auto zSize = LayersZCoordinate(); - return GPUArray{{0.5f * ZBins / (zSize[0]), 0.5f * ZBins / (zSize[1]), 0.5f * ZBins / (zSize[2]), - 0.5f * ZBins / (zSize[3]), 0.5f * ZBins / (zSize[4]), 0.5f * ZBins / (zSize[5]), - 0.5f * ZBins / (zSize[6])}}; -} -inline float getInverseZCoordinate(const int layerIndex) -{ - return 0.5f * ZBins / LayersZCoordinate()[layerIndex]; -} - -GPUhdi() int getZBinIndex(const int layerIndex, const float zCoordinate) -{ - return (zCoordinate + LayersZCoordinate()[layerIndex]) * - InverseZBinSize()[layerIndex]; -} +GPUconstexpr() float Tolerance{1e-12}; // numerical tolerance +GPUconstexpr() int ClustersPerCell{3}; +GPUconstexpr() int UnusedIndex{-1}; +GPUconstexpr() float Resolution{0.0005f}; +GPUconstexpr() float Radl = 9.36f; // Radiation length of Si [cm] +GPUconstexpr() float Rho = 2.33f; // Density of Si [g/cm^3] -GPUhdi() int getPhiBinIndex(const float currentPhi) +namespace helpers { - return (currentPhi * InversePhiBinSize); -} -GPUhdi() int getBinIndex(const int zIndex, const int phiIndex) +// initialize a std::array at compile time fully with T +template +constexpr std::array initArray() { - return o2::gpu::GPUCommonMath::Min(phiIndex * ZBins + zIndex, - ZBins * PhiBins - 1); + return [](std::index_sequence) { return std::array{(static_cast(Is), Value)...}; }(std::make_index_sequence{}); } -GPUhdi() constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - -} // namespace its2 - -namespace pdgcodes -{ -constexpr int PionCode{211}; -} -} // namespace constants -#ifndef __OPENCL__ /// FIXME: this is for compatibility with OCL -typedef std::vector> index_table_t; -#endif -} // namespace its -} // namespace o2 +} // namespace helpers +} // namespace o2::its::constants #endif /* TRACKINGITSU_INCLUDE_CONSTANTS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index a1d2fa338ba63..c3be0de2dade7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h @@ -15,18 +15,9 @@ #ifndef TRACKINGITS_DEFINITIONS_H_ #define TRACKINGITS_DEFINITIONS_H_ -// #define CA_DEBUG -// #define VTX_DEBUG -#define __USE_GPU_TRACER__ +#include -template -void discardResult(const T&) -{ -} - -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif +#include "ReconstructionDataFormats/Vertex.h" #ifdef CA_DEBUG #define CA_DEBUGGER(x) x @@ -36,103 +27,19 @@ void discardResult(const T&) } while (0) #endif -#if defined(__CUDA_ARCH__) // ???? -#define TRACKINGITSU_GPU_DEVICE -#endif - -#if defined(__CUDACC__) || defined(__HIPCC__) -#define MATH_CEIL ceil - -#ifndef GPUCA_GPUCODE_DEVICE -#include -#endif -#include "../GPU/ITStrackingGPU/Array.h" - -template -using GPUArray = o2::its::gpu::Array; - -#ifdef __CUDACC__ -#define GPU_ARCH "CUDA" - -typedef cudaStream_t GPUStream; -inline int getGPUCores(const int major, const int minor) -{ - // Defines for GPU Architecture types (using the SM version to determine the # of cores per SM - typedef struct - { - int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version - int Cores; - } sSMtoCores; - - sSMtoCores nGpuArchCoresPerSM[] = - { - {0x20, 32}, // Fermi Generation (SM 2.0) GF100 class - {0x21, 48}, // Fermi Generation (SM 2.1) GF10x class - {0x30, 192}, // Kepler Generation (SM 3.0) GK10x class - {0x32, 192}, // Kepler Generation (SM 3.2) GK10x class - {0x35, 192}, // Kepler Generation (SM 3.5) GK11x class - {0x37, 192}, // Kepler Generation (SM 3.7) GK21x class - {0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class - {0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class - {0x53, 128}, // Maxwell Generation (SM 5.3) GM20x class - {0x60, 64}, // Pascal Generation (SM 6.0) GP100 class - {0x61, 128}, // Pascal Generation (SM 6.1) GP10x class - {0x62, 128}, // Pascal Generation (SM 6.2) GP10x class - {0x70, 64}, // Volta Generation (SM 7.0) GV100 class - {0x72, 64}, // Volta Generation (SM 7.2) GV10B class - {0x75, 64}, // Turing Generation (SM 7.5) TU1xx class - {-1, -1}}; - - int index = 0; - - while (nGpuArchCoresPerSM[index].SM != -1) { - if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) { - return nGpuArchCoresPerSM[index].Cores; - } - - index++; - } - - // If we don't find the values, we default use the previous one to run properly - return nGpuArchCoresPerSM[index - 1].Cores; -} -inline int getGPUMaxThreadsPerComputingUnit() +namespace o2::its { - return 8; -} -#else // __HIPCC__ -#define GPU_ARCH "HIP" -typedef hipStream_t GPUStream; -inline int getGPUCores(const int major, const int minor) -{ - // Hardcoded result for AMD RADEON WX 9100, to be decided if and how determine this paramter - return 4096; -} +enum class TrackletMode { + Layer0Layer1 = 0, + Layer1Layer2 = 2 +}; -inline int getGPUMaxThreadsPerComputingUnit() -{ - return 8; -} -#endif +using Vertex = o2::dataformats::Vertex>; -#else -#define MATH_CEIL std::ceil -#ifndef __VECTOR_TYPES_H__ -#include "GPUCommonDef.h" -#endif -#ifndef __OPENCL__ -#include -template -using GPUArray = std::array; -#else -#include "../GPU/ITStrackingGPU/Array.h" -template -using GPUArray = o2::its::gpu::Array; -#endif +template +using maybe_const = typename std::conditional::type; -typedef struct _dummyStream { -} GPUStream; -#endif +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h new file mode 100644 index 0000000000000..7d1e98736db2c --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -0,0 +1,86 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ExternalAllocator.h +/// \brief +/// + +#ifndef TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ +#define TRACKINGITSU_INCLUDE_EXTERNALALLOCATOR_H_ + +#include +#include "GPUO2ExternalUser.h" +#include "Base/GPUMemoryResource.h" + +namespace o2::its +{ + +class ExternalAllocator +{ + using Type = std::underlying_type_t; + + public: + virtual void deallocate(char*, size_t) = 0; + virtual void* allocate(size_t) = 0; + void* allocate(size_t s, Type type) + { + auto old = mType; + mType = type; + void* p = allocate(s); + mType = old; + return p; + } + void* allocateStack(size_t s) + { + return allocate(s, (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + } + virtual void pushTagOnStack(uint64_t) = 0; + virtual void popTagOffStack(uint64_t) = 0; + + void setType(Type t) noexcept { mType = t; } + Type getType() const noexcept { return mType; } + + protected: + Type mType; +}; + +class ExternalAllocatorAdaptor final : public std::pmr::memory_resource +{ + public: + explicit ExternalAllocatorAdaptor(ExternalAllocator* alloc) : mAlloc(alloc) {} + + protected: + void* do_allocate(size_t bytes, size_t alignment) override + { + void* p = mAlloc->allocate(bytes, o2::gpu::GPUMemoryResource::MemoryType::MEMORY_HOST); + if (!p) { + throw std::bad_alloc(); + } + return p; + } + + void do_deallocate(void* p, size_t bytes, size_t) override + { + mAlloc->deallocate(static_cast(p), bytes); + } + + bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override + { + return this == &other; + } + + private: + ExternalAllocator* mAlloc; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h index ff25bc975bfb3..8adacdf58d74d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h @@ -16,66 +16,27 @@ #ifndef TRACKINGITSU_INCLUDE_EVENTLOADER_H_ #define TRACKINGITSU_INCLUDE_EVENTLOADER_H_ -#include -#include -#include #include -#include "DataFormatsITSMFT/ROFRecord.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/ROframe.h" -#include "ITStracking/Label.h" -#include "ITStracking/Road.h" -#include "ITStracking/TrackingConfigParam.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ReconstructionDataFormats/BaseCluster.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsITSMFT/ROFRecord.h" // TODO this is just included since the alignment code include it now -namespace o2 +namespace o2::its::ioutils { -class MCCompLabel; - -namespace dataformats -{ -template -class MCTruthContainer; -} - -namespace its -{ - -namespace ioutils -{ constexpr float DefClusErrorRow = o2::itsmft::SegmentationAlpide::PitchRow * 0.5; constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5; constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -void loadEventData(ROframe& events, gsl::span clusters, - gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* clsLabels = nullptr); -int loadROFrameData(const o2::itsmft::ROFRecord& rof, ROframe& events, gsl::span clusters, - gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mClsLabels = nullptr); - void convertCompactClusters(gsl::span clusters, gsl::span::iterator& pattIt, std::vector>& output, const itsmft::TopologyDictionary* dict); -inline static const o2::itsmft::ChipMappingITS& getChipMappingITS() -{ - static const o2::itsmft::ChipMappingITS MP; - return MP; -} - -std::vector> loadLabels(const int, const std::string&); -void writeRoadsReport(std::ofstream&, std::ofstream&, std::ofstream&, const std::vector>&, - const std::unordered_map&); - template o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const itsmft::TopologyDictionary* dict, T& sig2y, T& sig2z) { @@ -119,8 +80,6 @@ std::array extractClusterDataA(const itsmft::CompClusterExt& c, iterator& } } -} // namespace ioutils -} // namespace its -} // namespace o2 +} // namespace o2::its::ioutils #endif /* TRACKINGITSU_INCLUDE_EVENTLOADER_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h index 9bdc3a813092d..118557c970c35 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IndexTableUtils.h @@ -16,16 +16,19 @@ #ifndef TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ #define TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ +#include + #include "ITStracking/Constants.h" #include "ITStracking/Configuration.h" #include "ITStracking/Definitions.h" +#include "CommonConstants/MathConstants.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { + +template class IndexTableUtils { public: @@ -48,14 +51,15 @@ class IndexTableUtils int mNzBins = 0; int mNphiBins = 0; float mInversePhiBinSize = 0.f; - float mLayerZ[7] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float mInverseZBinSize[7] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array mLayerZ{}; + std::array mInverseZBinSize{}; }; +template template -inline void IndexTableUtils::setTrackingParameters(const T& params) +inline void IndexTableUtils::setTrackingParameters(const T& params) { - mInversePhiBinSize = params.PhiBins / constants::math::TwoPi; + mInversePhiBinSize = params.PhiBins / o2::constants::math::TwoPI; mNzBins = params.ZBins; mNphiBins = params.PhiBins; for (int iLayer{0}; iLayer < params.LayerZ.size(); ++iLayer) { @@ -66,28 +70,33 @@ inline void IndexTableUtils::setTrackingParameters(const T& params) } } -inline float IndexTableUtils::getInverseZCoordinate(const int layerIndex) const +template +inline float IndexTableUtils::getInverseZCoordinate(const int layerIndex) const { return 0.5f * mNzBins / mLayerZ[layerIndex]; } -GPUhdi() int IndexTableUtils::getZBinIndex(const int layerIndex, const float zCoordinate) const +template +GPUhdi() int IndexTableUtils::getZBinIndex(const int layerIndex, const float zCoordinate) const { return (zCoordinate + mLayerZ[layerIndex]) * mInverseZBinSize[layerIndex]; } -GPUhdi() int IndexTableUtils::getPhiBinIndex(const float currentPhi) const +template +GPUhdi() int IndexTableUtils::getPhiBinIndex(const float currentPhi) const { return (currentPhi * mInversePhiBinSize); } -GPUhdi() int IndexTableUtils::getBinIndex(const int zIndex, const int phiIndex) const +template +GPUhdi() int IndexTableUtils::getBinIndex(const int zIndex, const int phiIndex) const { return o2::gpu::GPUCommonMath::Min(phiIndex * mNzBins + zIndex, mNzBins * mNphiBins - 1); } -GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const int phiBinIndex, - const int minZBinIndex, const int maxZBinIndex) const +template +GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const int phiBinIndex, + const int minZBinIndex, const int maxZBinIndex) const { const int firstBinIndex{getBinIndex(minZBinIndex, phiBinIndex)}; const int maxBinIndex{firstBinIndex + maxZBinIndex - minZBinIndex + 1}; @@ -95,14 +104,14 @@ GPUhdi() int IndexTableUtils::countRowSelectedBins(const int* indexTable, const return indexTable[maxBinIndex] - indexTable[firstBinIndex]; } -GPUhdi() void IndexTableUtils::print() const +template +GPUhdi() void IndexTableUtils::print() const { printf("NzBins: %d, NphiBins: %d, InversePhiBinSize: %f\n", mNzBins, mNphiBins, mInversePhiBinSize); - for (int iLayer{0}; iLayer < 7; ++iLayer) { + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { printf("Layer %d: Z: %f, InverseZBinSize: %f\n", iLayer, mLayerZ[iLayer], mInverseZBinSize[iLayer]); } } -} // namespace its -} // namespace o2 +} // namespace o2::its #endif /* TRACKINGITSU_INCLUDE_INDEXTABLEUTILS_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h deleted file mode 100644 index ec45e6587a974..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Label.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Label.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_LABEL_H_ -#define TRACKINGITSU_INCLUDE_LABEL_H_ - -#include - -namespace o2 -{ -namespace its -{ - -struct Label final { - Label(const int, const float, const float, const float, const int, const int); - - int monteCarloId; - float transverseMomentum; - float phi; - float pseudorapidity; - int pdgCode; - int numberOfClusters; - - friend std::ostream& operator<<(std::ostream&, const Label&); -}; -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_LABEL_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index b9a2b599f0ba8..c5c1e4a8ce220 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -13,86 +13,95 @@ /// \brief /// -#ifndef TRACKINGITSU_INCLUDE_CAUTILS_H_ -#define TRACKINGITSU_INCLUDE_CAUTILS_H_ +#ifndef O2_ITS_TRACKING_MATHUTILS_H_ +#define O2_ITS_TRACKING_MATHUTILS_H_ -#ifndef GPUCA_GPUCODE_DEVICE -#include -#include -#include -#include -#endif - -#include "MathUtils/Utils.h" +#include "CommonConstants/MathConstants.h" #include "ITStracking/Constants.h" +#include "MathUtils/Utils.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its -{ - -namespace math_utils -{ -GPUhdni() float computePhi(const float, const float); -GPUhdni() float hypot(const float, const float); -GPUhdni() constexpr float getNormalizedPhi(const float); -GPUhdni() constexpr float3 crossProduct(const float3&, const float3&); -GPUhdni() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2); - -} // namespace math_utils - -GPUhdi() float math_utils::computePhi(const float x, const float y) +namespace o2::its::math_utils { - //return o2::gpu::CAMath::ATan2(-yCoordinate, -xCoordinate) + constants::math::Pi; - return o2::math_utils::fastATan2(-y, -x) + constants::math::Pi; -} -GPUhdi() float math_utils::hypot(const float x, const float y) +GPUhdi() float computePhi(float x, float y) { - return o2::gpu::CAMath::Sqrt(x * x + y * y); + return o2::math_utils::fastATan2(-y, -x) + o2::constants::math::PI; } -GPUhdi() constexpr float math_utils::getNormalizedPhi(const float phi) +GPUhdi() constexpr float hypot(float x, float y) { - return (phi < 0) ? phi + constants::math::TwoPi : (phi > constants::math::TwoPi) ? phi - constants::math::TwoPi - : phi; + return o2::gpu::CAMath::Hypot(x, y); } -GPUhdi() constexpr float3 math_utils::crossProduct(const float3& firstVector, const float3& secondVector) +GPUhdi() constexpr float getNormalizedPhi(float phi) { - - return float3{(firstVector.y * secondVector.z) - (firstVector.z * secondVector.y), - (firstVector.z * secondVector.x) - (firstVector.x * secondVector.z), - (firstVector.x * secondVector.y) - (firstVector.y * secondVector.x)}; + phi -= o2::constants::math::TwoPI * o2::gpu::CAMath::Floor(phi * (1.f / o2::constants::math::TwoPI)); + return phi; } -GPUhdi() float math_utils::computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) +GPUhdi() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3) { + // in case the triangle is degenerate we return infinite curvature. const float d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); + if (o2::gpu::CAMath::Abs(d) < o2::its::constants::Tolerance) { + return 0.f; + } const float a = 0.5f * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); const float b = 0.5f * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); - - return -1.f * d / o2::gpu::CAMath::Sqrt((d * x1 - a) * (d * x1 - a) + (d * y1 - b) * (d * y1 - b)); + const float den = o2::gpu::CAMath::Hypot(d * x1 - a, d * y1 - b); + if (den < o2::its::constants::Tolerance) { + return 0.f; + } + return -d / den; } -GPUhdi() float math_utils::computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) +GPUhdi() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3) { - const float k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); + // in case the triangle is degenerate we return set the centre to infinity. + float dx21 = x2 - x1, dx32 = x3 - x2; + if (o2::gpu::CAMath::Abs(dx21) < o2::its::constants::Tolerance || + o2::gpu::CAMath::Abs(dx32) < o2::its::constants::Tolerance) { // add small offset + x2 += 1e-4; + dx21 = x2 - x1; + dx32 = x3 - x2; + } + const float k1 = (y2 - y1) / dx21, k2 = (y3 - y2) / dx32; + if (o2::gpu::CAMath::Abs(k2 - k1) < o2::its::constants::Tolerance) { + return o2::constants::math::VeryBig; + } return 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); } -GPUhdi() float math_utils::computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) +GPUhdi() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2) +{ + // in case the points vertically align we go to pos/neg inifinity. + const float d = o2::gpu::CAMath::Hypot(x1 - x2, y1 - y2); + if (o2::gpu::CAMath::Abs(d) < o2::its::constants::Tolerance) { + return ((z1 > z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; + } + return (z1 - z2) / d; +} + +GPUhdi() float smallestAngleDifference(float a, float b) { - return (z1 - z2) / o2::gpu::CAMath::Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); } -} // namespace its -} // namespace o2 +GPUhdi() float Sq(float v) +{ + return v * v; +} -#endif /* TRACKINGITSU_INCLUDE_CAUTILS_H_ */ +GPUhdi() float MSangle(float mass, float p, float xX0) +{ + float beta = p / o2::gpu::CAMath::Hypot(mass, p); + return 0.0136f * o2::gpu::CAMath::Sqrt(xX0) * (1.f + 0.038f * o2::gpu::CAMath::Log(xX0)) / (beta * p); +} + +} // namespace o2::its::math_utils + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h deleted file mode 100644 index a39ee1b96c72a..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROframe.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file ROframe.h -/// \brief -/// - -#ifndef TRACKINGITSU_INCLUDE_ROFRAME_H_ -#define TRACKINGITSU_INCLUDE_ROFRAME_H_ - -#include -#include -#include -#include -#include - -#include "ITStracking/Cluster.h" -#include "ITStracking/Constants.h" - -#include "ReconstructionDataFormats/Vertex.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace its -{ - -using Vertex = o2::dataformats::Vertex>; - -class ROframe final -{ - public: - ROframe(int ROframeId, int nLayers); - int getROFrameId() const; - const float3& getPrimaryVertex(const int) const; - int getPrimaryVerticesNum() const; - void addPrimaryVertex(const float, const float, const float); - void addPrimaryVertices(std::vector vertices); - void addPrimaryReconstructedVertex(const float, const float, const float); - void printPrimaryVertices() const; - int getTotalClusters() const; - bool empty() const; - - const auto& getClusters() const { return mClusters; } - const std::vector& getClustersOnLayer(int layerId) const; - const std::vector& getTrackingFrameInfoOnLayer(int layerId) const; - const auto& getTrackingFrameInfo() const { return mTrackingFrameInfo; } - - const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; - const MCCompLabel& getClusterFirstLabel(int layerId, const Cluster& cl) const; - const MCCompLabel& getClusterFirstLabel(int layerId, const int clId) const; - gsl::span getClusterLabels(int layerId, const int clId) const; - gsl::span getClusterLabels(int layerId, const Cluster& cl) const; - int getClusterExternalIndex(int layerId, const int clId) const; - std::vector getTracksId(const int layerId, const std::vector& cl); - - template - void addClusterToLayer(int layer, T&&... args); - template - void addTrackingFrameInfoToLayer(int layer, T&&... args); - void setMClabelsContainer(const dataformats::MCTruthContainer* ptr); - void addClusterExternalIndexToLayer(int layer, const int idx); - bool hasMCinformation() const; - - void clear(); - - private: - const int mROframeId; - o2::dataformats::MCTruthContainer* mMClabels = nullptr; - std::vector mPrimaryVertices; - std::vector> mClusters; - std::vector> mTrackingFrameInfo; - std::vector> mClusterExternalIndices; -}; - -inline int ROframe::getROFrameId() const { return mROframeId; } - -inline const float3& ROframe::getPrimaryVertex(const int vertexIndex) const { return mPrimaryVertices[vertexIndex]; } - -inline int ROframe::getPrimaryVerticesNum() const { return mPrimaryVertices.size(); } - -inline bool ROframe::empty() const { return getTotalClusters() == 0; } - -inline const std::vector& ROframe::getClustersOnLayer(int layerId) const -{ - return mClusters[layerId]; -} - -inline const std::vector& ROframe::getTrackingFrameInfoOnLayer(int layerId) const -{ - return mTrackingFrameInfo[layerId]; -} - -inline const TrackingFrameInfo& ROframe::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const -{ - return mTrackingFrameInfo[layerId][cl.clusterId]; -} - -inline const MCCompLabel& ROframe::getClusterFirstLabel(int layerId, const Cluster& cl) const -{ - return getClusterFirstLabel(layerId, cl.clusterId); -} - -inline const MCCompLabel& ROframe::getClusterFirstLabel(int layerId, const int clId) const -{ - return *(mMClabels->getLabels(getClusterExternalIndex(layerId, clId)).begin()); -} - -inline gsl::span ROframe::getClusterLabels(int layerId, const int clId) const -{ - return mMClabels->getLabels(getClusterExternalIndex(layerId, clId)); -} - -inline gsl::span ROframe::getClusterLabels(int layerId, const Cluster& cl) const -{ - return getClusterLabels(layerId, cl.clusterId); -} - -inline int ROframe::getClusterExternalIndex(int layerId, const int clId) const -{ - return mClusterExternalIndices[layerId][clId]; -} - -inline std::vector ROframe::getTracksId(const int layerId, const std::vector& cl) -{ - std::vector tracksId; - for (auto& cluster : cl) { - tracksId.push_back(getClusterFirstLabel(layerId, cluster).isNoise() ? -1 : getClusterFirstLabel(layerId, cluster).getTrackID()); - } - return tracksId; -} - -template -void ROframe::addClusterToLayer(int layer, T&&... values) -{ - mClusters[layer].emplace_back(std::forward(values)...); -} - -template -void ROframe::addTrackingFrameInfoToLayer(int layer, T&&... values) -{ - mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); -} - -inline void ROframe::setMClabelsContainer(const dataformats::MCTruthContainer* ptr) -{ - mMClabels = const_cast*>(ptr); -} - -inline void ROframe::addClusterExternalIndexToLayer(int layer, const int idx) -{ - mClusterExternalIndices[layer].push_back(idx); -} - -inline void ROframe::clear() -{ - for (unsigned int iL = 0; iL < mClusters.size(); ++iL) { - mClusters[iL].clear(); - mTrackingFrameInfo[iL].clear(); - // mClusterLabels[iL].clear(); - mClusterExternalIndices[iL].clear(); - } - mPrimaryVertices.clear(); - mMClabels = nullptr; -} - -inline bool ROframe::hasMCinformation() const -{ - // for (const auto& vect : mClusterLabels) { - // if (!vect.empty()) { - // return true; - // } - // } - // return false; - return mMClabels; -} - -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ROFRAME_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h index ceb799cc82899..009f3a1b5b146 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h @@ -13,58 +13,60 @@ /// \brief /// -#ifndef TRACKINGITSU_INCLUDE_ROAD_H_ -#define TRACKINGITSU_INCLUDE_ROAD_H_ +#ifndef TRACKINGCA_INCLUDE_ROAD_H +#define TRACKINGCA_INCLUDE_ROAD_H -#ifndef GPUCA_GPUCODE_DEVICE #include -#endif #include "ITStracking/Constants.h" #include "GPUCommonDef.h" -namespace o2 -{ -namespace its +namespace o2::its { +template class Road final { public: - Road(); - Road(int, int); - - int getRoadSize() const; - int getLabel() const; - void setLabel(const int); - bool isFakeRoad() const; - void setFakeRoad(const bool); - GPUhdni() int& operator[](const int&); - - void resetRoad(); - void addCell(int, int); - - static constexpr int mMaxRoadSize = 13; + GPUhdDefault() Road() = default; + GPUhd() Road(int cellLayer, int cellId) : Road() { addCell(cellLayer, cellId); } + + GPUhdDefault() Road(const Road&) = default; + GPUhdDefault() Road(Road&&) noexcept = default; + GPUhdDefault() ~Road() = default; + + GPUhdDefault() Road& operator=(const Road&) = default; + GPUhdDefault() Road& operator=(Road&&) noexcept = default; + + GPUhdi() uint8_t getRoadSize() const { return mRoadSize; } + GPUhdi() bool isFakeRoad() const { return mIsFakeRoad; } + GPUhdi() void setFakeRoad(const bool fake) { mIsFakeRoad = fake; } + GPUhdi() int& operator[](const int& i) { return mCellIds[i]; } + GPUhdi() int operator[](const int& i) const { return mCellIds[i]; } + + GPUhd() void resetRoad() + { + for (int i = 0; i < maxRoadSize; i++) { + mCellIds[i] = constants::UnusedIndex; + } + mRoadSize = 0; + } + + GPUhd() void addCell(int cellLayer, int cellId) + { + if (mCellIds[cellLayer] == constants::UnusedIndex) { + ++mRoadSize; + } + + mCellIds[cellLayer] = cellId; + } private: - int mCellIds[mMaxRoadSize]; - int mRoadSize; - int mLabel; - bool mIsFakeRoad; + std::array mCellIds = constants::helpers::initArray(); + unsigned char mRoadSize{0}; + bool mIsFakeRoad{false}; }; -inline int Road::getRoadSize() const { return mRoadSize; } - -inline int Road::getLabel() const { return mLabel; } +} // namespace o2::its -inline void Road::setLabel(const int label) { mLabel = label; } - -GPUhdi() int& Road::operator[](const int& i) { return mCellIds[i]; } - -inline bool Road::isFakeRoad() const { return mIsFakeRoad; } - -inline void Road::setFakeRoad(const bool isFakeRoad) { mIsFakeRoad = isFakeRoad; } -} // namespace its -} // namespace o2 - -#endif /* TRACKINGITSU_INCLUDE_ROAD_H_ */ +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h index 2dcd521797837..101f4b8d72601 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h @@ -17,7 +17,6 @@ #include "ReconstructionDataFormats/Track.h" #include "DataFormatsITS/TrackITS.h" #include "DetectorsBase/Propagator.h" -#include "ITStracking/ROframe.h" namespace o2 { @@ -28,14 +27,14 @@ template class Smoother { public: - Smoother(TrackITSExt& track, size_t layer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr); + // Smoother(TrackITSExt& track, size_t layer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr); ~Smoother(); bool isValidInit() const { return mInitStatus; } - bool testCluster(const int clusterId, const ROframe& event); + // bool testCluster(const int clusterId, const ROframe& event); bool getSmoothedTrack(); float getChi2() const { return mBestChi2; } float getLastChi2() const { return mLastChi2; } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 4a1f770786d6c..acc884ea68b8b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -16,12 +16,9 @@ #include #include #include +#include #include -#include #include -#include -#include -#include #include "DataFormatsITS/TrackITS.h" @@ -34,14 +31,20 @@ #include "ITStracking/Road.h" #include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" - +#include "ITStracking/ExternalAllocator.h" +#include "ITStracking/BoundedAllocator.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include "ReconstructionDataFormats/Vertex.h" +#include "DetectorsBase/Propagator.h" namespace o2 { +namespace gpu +{ +class GPUChainITS; +} namespace itsmft { @@ -53,154 +56,201 @@ class ROFRecord; namespace its { -using Vertex = o2::dataformats::Vertex>; - -struct lightVertex { - lightVertex(float x, float y, float z, std::array rms2, int cont, float avgdis2, int stamp); - float mX; - float mY; - float mZ; - std::array mRMS2; - float mAvgDistance2; - int mContributors; - int mTimeStamp; -}; - -class TimeFrame +namespace gpu { - public: - TimeFrame(int nLayers = 7); - const Vertex& getPrimaryVertex(const int) const; - gsl::span getPrimaryVertices(int tf) const; +template +class TimeFrameGPU; +} + +template +struct TimeFrame { + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; + friend class gpu::TimeFrameGPU; + + TimeFrame() = default; + virtual ~TimeFrame() = default; + + const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } + gsl::span getPrimaryVertices(int rofId) const; gsl::span getPrimaryVertices(int romin, int romax) const; - gsl::span> getPrimaryVerticesLabels(const int rof) const; - gsl::span> getPrimaryVerticesXAlpha(int tf) const; + gsl::span> getPrimaryVerticesMCRecInfo(const int rofId) const; + gsl::span getPrimaryVerticesContributors(const int rofId) const; + gsl::span> getPrimaryVerticesXAlpha(int rofId) const; void fillPrimaryVerticesXandAlpha(); - int getPrimaryVerticesNum(int rofID = -1) const; - void addPrimaryVertices(const std::vector& vertices); - void addPrimaryVertices(const gsl::span& vertices); - void addPrimaryVertices(const std::vector&); + int getPrimaryVerticesNum(int rofId = -1) const; + void addPrimaryVerticesLabels(bounded_vector>& labels); + void addPrimaryVerticesContributorLabels(bounded_vector& labels); + void addPrimaryVertices(const bounded_vector& vertices, const int iteration); + void addPrimaryVerticesInROF(const bounded_vector& vertices, const int rofId, const int iteration); + void addPrimaryVerticesLabelsInROF(const bounded_vector>& labels, const int rofId); + void addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId); void removePrimaryVerticesInROf(const int rofId); int loadROFrameData(const o2::itsmft::ROFRecord& rof, gsl::span clusters, const dataformats::MCTruthContainer* mcLabels = nullptr); - int loadROFrameData(gsl::span rofs, + int loadROFrameData(gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, const itsmft::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels = nullptr); + void resetROFrameData(size_t nROFs); + void prepareROFrameData(gsl::span rofs, + gsl::span clusters); int getTotalClusters() const; - bool empty() const; - - int getSortedIndex(int rof, int layer, int i) const; - int getSortedStartIndex(const int, const int) const; - int getNrof() const; + auto& getTotVertIteration() { return mTotVertPerIteration; } + bool empty() const { return getTotalClusters() == 0; } + int getSortedIndex(int rofId, int layer, int idx) const { return mROFramesClusters[layer][rofId] + idx; } + int getSortedStartIndex(const int rofId, const int layer) const { return mROFramesClusters[layer][rofId]; } + int getNrof() const { return mNrof; } void resetBeamXY(const float x, const float y, const float w = 0); void setBeamPosition(const float x, const float y, const float s2, const float base = 50.f, const float systematic = 0.f) { isBeamPositionOverridden = true; - resetBeamXY(x, y, s2 / std::sqrt(base * base + systematic)); + resetBeamXY(x, y, s2 / o2::gpu::CAMath::Sqrt(base * base + systematic)); } - float getBeamX() const; - float getBeamY() const; - + float getBeamX() const { return mBeamPos[0]; } + float getBeamY() const { return mBeamPos[1]; } + auto& getMinRs() { return mMinR; } + auto& getMaxRs() { return mMaxR; } float getMinR(int layer) const { return mMinR[layer]; } float getMaxR(int layer) const { return mMaxR[layer]; } float getMSangle(int layer) const { return mMSangles[layer]; } + auto& getMSangles() { return mMSangles; } float getPhiCut(int layer) const { return mPhiCuts[layer]; } + auto& getPhiCuts() { return mPhiCuts; } float getPositionResolution(int layer) const { return mPositionResolution[layer]; } + auto& getPositionResolutions() { return mPositionResolution; } gsl::span getClustersOnLayer(int rofId, int layerId); gsl::span getClustersOnLayer(int rofId, int layerId) const; gsl::span getClustersPerROFrange(int rofMin, int range, int layerId) const; gsl::span getUnsortedClustersOnLayer(int rofId, int layerId) const; - gsl::span getROframesClustersPerROFrange(int rofMin, int range, int layerId) const; - gsl::span getROframeClusters(int layerId) const; + gsl::span getUsedClustersROF(int rofId, int layerId); + gsl::span getUsedClustersROF(int rofId, int layerId) const; + gsl::span getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const; + gsl::span getROFrameClusters(int layerId) const; gsl::span getNClustersROFrange(int rofMin, int range, int layerId) const; gsl::span getIndexTablePerROFrange(int rofMin, int range, int layerId) const; gsl::span getIndexTable(int rofId, int layerId); - std::vector& getIndexTableWhole(int layerId) { return mIndexTables[layerId]; } - const std::vector& getTrackingFrameInfoOnLayer(int layerId) const; + auto& getIndexTableWhole(int layerId) { return mIndexTables[layerId]; } + const auto& getTrackingFrameInfoOnLayer(int layerId) const { return mTrackingFrameInfo[layerId]; } const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; - const gsl::span getClusterLabels(int layerId, const Cluster& cl) const; - const gsl::span getClusterLabels(int layerId, const int clId) const; - int getClusterExternalIndex(int layerId, const int clId) const; + gsl::span getClusterLabels(int layerId, const Cluster& cl) const { return getClusterLabels(layerId, cl.clusterId); } + gsl::span getClusterLabels(int layerId, const int clId) const { return mClusterLabels->getLabels(mClusterExternalIndices[layerId][clId]); } + int getClusterExternalIndex(int layerId, const int clId) const { return mClusterExternalIndices[layerId][clId]; } + int getClusterSize(int clusterId) const { return mClusterSize[clusterId]; } + void setClusterSize(bounded_vector& v) { mClusterSize = std::move(v); } - std::vector& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } - std::vector& getCellsLabel(int layer) { return mCellLabels[layer]; } + auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } + auto& getCellsLabel(int layer) { return mCellLabels[layer]; } - bool hasMCinformation() const; - void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = 7); + bool hasMCinformation() const { return mClusterLabels; } + void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = 7, bool resetVertices = true); void resetRofPV() { - mPrimaryVertices.clear(); - mROframesPV.resize(1, 0); - }; + deepVectorClear(mPrimaryVertices); + mROFramesPV.resize(1, 0); + mTotVertPerIteration.resize(1); + } - bool isClusterUsed(int layer, int clusterId) const; - void markUsedCluster(int layer, int clusterId); + bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } + void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } + gsl::span getUsedClusters(const int layer); - std::vector>& getTracklets(); - std::vector>& getTrackletsLookupTable(); + auto& getTracklets() { return mTracklets; } + auto& getTrackletsLookupTable() { return mTrackletsLookupTable; } - std::vector>& getClusters(); - std::vector>& getUnsortedClusters(); + auto& getClusters() { return mClusters; } + auto& getUnsortedClusters() { return mUnsortedClusters; } int getClusterROF(int iLayer, int iCluster); - std::vector>& getCells(); - std::vector>& getCellsLookupTable(); - std::vector>>& getCellsNeighbours(); - std::vector& getRoads(); - std::vector& getTracks(int rof) { return mTracks[rof]; } - std::vector& getTracksLabel(const int rof) { return mTracksLabel[rof]; } - std::vector& getLinesLabel(const int rof) { return mLinesLabels[rof]; } - std::vector>& getVerticesLabels() { return mVerticesLabels; } + auto& getCells() { return mCells; } - int getNumberOfClusters() const; - int getNumberOfCells() const; - int getNumberOfTracklets() const; - int getNumberOfTracks() const; + auto& getCellsLookupTable() { return mCellsLookupTable; } + auto& getCellsNeighbours() { return mCellsNeighbours; } + auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } + auto& getRoads() { return mRoads; } + auto& getTracks(int rofId) { return mTracks[rofId]; } + auto& getTracksLabel(const int rofId) { return mTracksLabel[rofId]; } + auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } + auto& getVerticesMCRecInfo() { return mVerticesMCRecInfo; } + int getNumberOfClusters() const; + virtual int getNumberOfCells() const; + virtual int getNumberOfTracklets() const; + virtual int getNumberOfNeighbours() const; + size_t getNumberOfTracks() const; + size_t getNumberOfUsedClusters() const; + auto getNumberOfExtendedTracks() const { return mNExtendedTracks; } + auto getNumberOfUsedExtendedClusters() const { return mNExtendedUsedClusters; } + + /// memory management + void setMemoryPool(std::shared_ptr pool); + auto& getMemoryPool() const noexcept { return mMemoryPool; } bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } - unsigned long getArtefactsMemory(); - int getROfCutClusterMult() const { return mCutClusterMult; }; - int getROfCutVertexMult() const { return mCutVertexMult; }; - int getROfCutAllMult() const { return mCutClusterMult + mCutVertexMult; } + unsigned long getArtefactsMemory() const; + void printArtefactsMemory() const; + + /// ROF cuts + int getROFCutClusterMult() const { return mCutClusterMult; }; + int getROFCutVertexMult() const { return mCutVertexMult; }; + int getROFCutAllMult() const { return mCutClusterMult + mCutVertexMult; } // Vertexer - void computeTrackletsScans(const int nThreads = 1); - int& getNTrackletsROf(int tf, int combId); - std::vector& getLines(int tf); - std::vector& getTrackletClusters(int tf); + void computeTrackletsPerROFScans(); + void computeTracletsPerClusterScans(); + int& getNTrackletsROF(int rofId, int combId) { return mNTrackletsPerROF[combId][rofId]; } + auto& getLines(int rofId) { return mLines[rofId]; } + int getNLinesTotal() const noexcept { return mTotalLines; } + void setNLinesTotal(uint32_t a) noexcept { mTotalLines = a; } + auto& getTrackletClusters(int rofId) { return mTrackletClusters[rofId]; } gsl::span getFoundTracklets(int rofId, int combId) const; gsl::span getFoundTracklets(int rofId, int combId); gsl::span getLabelsFoundTracklets(int rofId, int combId) const; gsl::span getNTrackletsCluster(int rofId, int combId); + gsl::span getExclusiveNTrackletsCluster(int rofId, int combId); uint32_t getTotalTrackletsTF(const int iLayer) { return mTotalTracklets[iLayer]; } int getTotalClustersPerROFrange(int rofMin, int range, int layerId) const; std::array& getBeamXY() { return mBeamPos; } + unsigned int& getNoVertexROF() { return mNoVertexROF; } + void insertPastVertex(const Vertex& vertex, const int refROFId); // \Vertexer void initialiseRoadLabels(); void setRoadLabel(int i, const unsigned long long& lab, bool fake); - const unsigned long long& getRoadLabel(int i) const; - bool isRoadFake(int i) const; + const unsigned long long& getRoadLabel(int i) const { return mRoadLabels[i].first; } + bool isRoadFake(int i) const { return mRoadLabels[i].second; } - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } + void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } + void setROFMask(const std::vector& rofMask) { mROFMask = rofMask; } + void swapMasks() { mMultiplicityCutMask.swap(mROFMask); } int hasBogusClusters() const { return std::accumulate(mBogusClusters.begin(), mBogusClusters.end(), 0); } void setBz(float bz) { mBz = bz; } float getBz() const { return mBz; } + /// State if memory will be externally managed by the GPU framework + ExternalAllocator* mExternalAllocator{nullptr}; + std::shared_ptr mExtMemoryPool; // host memory pool managed by the framework + auto getFrameworkAllocator() { return mExternalAllocator; }; + void setFrameworkAllocator(ExternalAllocator* ext); + bool hasFrameworkAllocator() const noexcept { return mExternalAllocator != nullptr; } + std::pmr::memory_resource* getMaybeFrameworkHostResource(bool forceHost = false) { return (hasFrameworkAllocator() && !forceHost) ? mExtMemoryPool.get() : mMemoryPool.get(); } + + // Propagator + const o2::base::PropagatorImpl* getDevicePropagator() const { return mPropagatorDevice; } + virtual void setDevicePropagator(const o2::base::PropagatorImpl*) {}; + template void addClusterToLayer(int layer, T&&... args); template void addTrackingFrameInfoToLayer(int layer, T&&... args); - void addClusterExternalIndexToLayer(int layer, const int idx); + void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } /// Debug and printing void checkTrackletLUTs(); @@ -211,399 +261,437 @@ class TimeFrame void printCellLUTonLayer(int i); void printTrackletLUTs(); void printCellLUTs(); + void printSliceInfo(const int, const int); - IndexTableUtils mIndexTableUtils; + IndexTableUtilsN mIndexTableUtils; - bool mIsGPU = false; - std::vector> mClusters; - std::vector> mTrackingFrameInfo; - std::vector> mClusterExternalIndices; - std::vector> mROframesClusters; + std::array, nLayers> mClusters; + std::array, nLayers> mTrackingFrameInfo; + std::array, nLayers> mClusterExternalIndices; + std::array, nLayers> mROFramesClusters; const dataformats::MCTruthContainer* mClusterLabels = nullptr; - std::array, 2> mNTrackletsPerCluster; // TODO: remove in favour of mNTrackletsPerROf - std::vector> mNClustersPerROF; - std::vector> mIndexTables; - std::vector> mTrackletsLookupTable; - std::vector> mUsedClusters; + std::array, 2> mNTrackletsPerCluster; + std::array, 2> mNTrackletsPerClusterSum; + std::array, nLayers> mNClustersPerROF; + std::array, nLayers> mIndexTables; + std::vector> mTrackletsLookupTable; + std::array, nLayers> mUsedClusters; int mNrof = 0; - std::vector mROframesPV = {0}; - std::vector mPrimaryVertices; - - private: + int mNExtendedTracks{0}; + int mNExtendedUsedClusters{0}; + bounded_vector mROFramesPV; + bounded_vector mPrimaryVertices; + + std::array, nLayers> mUnsortedClusters; + std::vector> mTracklets; + std::vector> mCells; + bounded_vector> mRoads; + std::vector> mTracks; + std::vector> mCellsNeighbours; + std::vector> mCellsLookupTable; + std::vector mMultiplicityCutMask; + + const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU + + virtual void wipe(); + + // interface + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } + + protected: + void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); float mBz = 5.; + unsigned int mNTotalLowPtVertices = 0; int mBeamPosWeight = 0; std::array mBeamPos = {0.f, 0.f}; bool isBeamPositionOverridden = false; - std::vector mMinR; - std::vector mMaxR; - std::vector mMSangles; - std::vector mPhiCuts; - std::vector mPositionResolution; - std::vector mMultiplicityCutMask; - std::vector> mPValphaX; /// PV x and alpha for track propagation - std::vector> mUnsortedClusters; - std::vector> mTrackletLabels; - std::vector> mCellLabels; - std::vector> mCells; - std::vector> mCellsLookupTable; - std::vector>> mCellsNeighbours; - std::vector mRoads; - std::vector> mTracksLabel; - std::vector> mTracks; - std::vector mBogusClusters; /// keep track of clusters with wild coordinates - - std::vector> mTracklets; - - std::vector> mRoadLabels; - int mCutClusterMult; - int mCutVertexMult; + std::array mMinR; + std::array mMaxR; + bounded_vector mMSangles; + bounded_vector mPhiCuts; + bounded_vector mPositionResolution; + bounded_vector mClusterSize; + + std::vector mROFMask; + bounded_vector> mPValphaX; /// PV x and alpha for track propagation + std::vector> mTrackletLabels; + std::vector> mCellLabels; + std::vector> mCellsNeighboursLUT; + std::vector> mTracksLabel; + bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates + + bounded_vector> mRoadLabels; + int mCutClusterMult{-999}; + int mCutVertexMult{-999}; // Vertexer - std::vector> mNTrackletsPerROf; - std::vector> mLines; - std::vector> mTrackletClusters; - std::vector> mTrackletsIndexROf; - std::vector> mLinesLabels; - std::vector> mVerticesLabels; + std::vector> mNTrackletsPerROF; + std::vector> mLines; + std::vector> mTrackletClusters; + std::array, 2> mTrackletsIndexROF; + std::vector> mLinesLabels; + std::vector> mVerticesMCRecInfo; + bounded_vector mVerticesContributorLabels; std::array mTotalTracklets = {0, 0}; + uint32_t mTotalLines = 0; + unsigned int mNoVertexROF = 0; + bounded_vector mTotVertPerIteration; // \Vertexer -}; -inline const Vertex& TimeFrame::getPrimaryVertex(const int vertexIndex) const { return mPrimaryVertices[vertexIndex]; } + std::shared_ptr mMemoryPool; +}; -inline gsl::span TimeFrame::getPrimaryVertices(int rof) const +template +inline gsl::span TimeFrame::getPrimaryVertices(int rofId) const { - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded + if (mPrimaryVertices.empty()) { + return {}; + } + const int start = mROFramesPV[rofId]; + const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; + int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded return {&mPrimaryVertices[start], static_cast::size_type>(delta)}; } -inline gsl::span> TimeFrame::getPrimaryVerticesLabels(const int rof) const +template +inline gsl::span> TimeFrame::getPrimaryVerticesMCRecInfo(const int rofId) const { - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&mVerticesLabels[start], static_cast::size_type>(delta)}; + const int start = mROFramesPV[rofId]; + const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; + int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded + return {&(mVerticesMCRecInfo[start]), static_cast>::size_type>(delta)}; } -inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const +template +inline gsl::span TimeFrame::getPrimaryVerticesContributors(const int rofId) const { - return {&mPrimaryVertices[mROframesPV[romin]], static_cast::size_type>(mROframesPV[romax + 1] - mROframesPV[romin])}; + // count the number of cont. in rofs before target rof + unsigned int start{0}, delta{0}; + const auto& pvsBefore = getPrimaryVertices(0, rofId - 1); + for (const auto& pv : pvsBefore) { + start += pv.getNContributors(); + } + const auto& pvsIn = getPrimaryVertices(rofId); + for (const auto& pv : pvsIn) { + delta += pv.getNContributors(); + } + return {&(mVerticesContributorLabels[start]), static_cast::size_type>(delta)}; } -inline gsl::span> TimeFrame::getPrimaryVerticesXAlpha(int rof) const +template +inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const { - const int start = mROframesPV[rof]; - const int stop_idx = rof >= mNrof - 1 ? mNrof : rof + 1; - int delta = mMultiplicityCutMask[rof] ? mROframesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mPValphaX[start]), static_cast>::size_type>(delta)}; + if (mPrimaryVertices.empty()) { + return {}; + } + const int stop_idx = romax >= mNrof - 1 ? mNrof : romax + 1; + return {&mPrimaryVertices[mROFramesPV[romin]], static_cast::size_type>(mROFramesPV[stop_idx] - mROFramesPV[romin])}; } -inline int TimeFrame::getPrimaryVerticesNum(int rofID) const +template +inline gsl::span> TimeFrame::getPrimaryVerticesXAlpha(int rofId) const { - return rofID < 0 ? mPrimaryVertices.size() : mROframesPV[rofID + 1] - mROframesPV[rofID]; + const int start = mROFramesPV[rofId]; + const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; + int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded + return {&(mPValphaX[start]), static_cast>::size_type>(delta)}; } -inline bool TimeFrame::empty() const { return getTotalClusters() == 0; } - -inline int TimeFrame::getSortedIndex(int rof, int layer, int index) const { return mROframesClusters[layer][rof] + index; } - -inline int TimeFrame::getSortedStartIndex(const int rof, const int layer) const { return mROframesClusters[layer][rof]; } - -inline int TimeFrame::getNrof() const { return mNrof; } +template +inline int TimeFrame::getPrimaryVerticesNum(int rofId) const +{ + return rofId < 0 ? mPrimaryVertices.size() : mROFramesPV[rofId + 1] - mROFramesPV[rofId]; +} -inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) +template +inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) { mBeamPos[0] = x; mBeamPos[1] = y; mBeamPosWeight = w; } -inline float TimeFrame::getBeamX() const { return mBeamPos[0]; } +template +inline gsl::span TimeFrame::getROFrameClusters(int layerId) const +{ + return {&mROFramesClusters[layerId][0], static_cast::size_type>(mROFramesClusters[layerId].size())}; +} -inline float TimeFrame::getBeamY() const { return mBeamPos[1]; } +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) +{ + if (rofId < 0 || rofId >= mNrof) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; +} -inline gsl::span TimeFrame::getROframeClusters(int layerId) const +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const { - return {&mROframesClusters[layerId][0], static_cast::size_type>(mROframesClusters[layerId].size())}; + if (rofId < 0 || rofId >= mNrof) { + return {}; + } + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const { if (rofMin < 0 || rofMin >= mNrof) { - return gsl::span(); + return {}; } - int startIdx{mROframesClusters[layerId][rofMin]}; // First cluster of rofMin - int endIdx{mROframesClusters[layerId][std::min(rofMin + range, mNrof)]}; + int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, mNrof)]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; } -inline gsl::span TimeFrame::getROframesClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; - return {&mROframesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; + return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const { int startIdx{rofMin}; // First cluster of rofMin - int endIdx{std::min(rofMin + range, mNrof)}; - return mROframesClusters[layerId][endIdx] - mROframesClusters[layerId][startIdx]; + int endIdx{o2::gpu::CAMath::Min(rofMin + range, mNrof)}; + return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } -inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const { const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; } -inline int TimeFrame::getClusterROF(int iLayer, int iCluster) +template +inline int TimeFrame::getClusterROF(int iLayer, int iCluster) { - return std::lower_bound(mROframesClusters[iLayer].begin(), mROframesClusters[iLayer].end(), iCluster + 1) - mROframesClusters[iLayer].begin() - 1; + return std::lower_bound(mROFramesClusters[iLayer].begin(), mROFramesClusters[iLayer].end(), iCluster + 1) - mROFramesClusters[iLayer].begin() - 1; } -inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - int startIdx{mROframesClusters[layerId][rofId]}; - return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROframesClusters[layerId][rofId + 1] - startIdx)}; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -inline const std::vector& TimeFrame::getTrackingFrameInfoOnLayer(int layerId) const -{ - return mTrackingFrameInfo[layerId]; -} - -inline const TrackingFrameInfo& TimeFrame::getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const -{ - return mTrackingFrameInfo[layerId][cl.clusterId]; -} - -inline const gsl::span TimeFrame::getClusterLabels(int layerId, const Cluster& cl) const -{ - return getClusterLabels(layerId, cl.clusterId); -} - -inline const gsl::span TimeFrame::getClusterLabels(int layerId, int clId) const -{ - return mClusterLabels->getLabels(mClusterExternalIndices[layerId][clId]); -} - -inline int TimeFrame::getClusterExternalIndex(int layerId, const int clId) const -{ - return mClusterExternalIndices[layerId][clId]; -} - -inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) +template +inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - return {&mIndexTables[layer][rofId * (mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)], - static_cast::size_type>(mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1)}; -} - -inline std::vector& TimeFrame::getLines(int rof) -{ - return mLines[rof]; -} - -inline std::vector& TimeFrame::getTrackletClusters(int rof) -{ - return mTrackletClusters[rof]; + const int tableSize = mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1; + return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; } +template template -void TimeFrame::addClusterToLayer(int layer, T&&... values) +void TimeFrame::addClusterToLayer(int layer, T&&... values) { mUnsortedClusters[layer].emplace_back(std::forward(values)...); } +template template -void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) +void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) { mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); } -inline void TimeFrame::addClusterExternalIndexToLayer(int layer, const int idx) -{ - mClusterExternalIndices[layer].push_back(idx); -} - -inline bool TimeFrame::hasMCinformation() const -{ - return mClusterLabels; -} - -inline bool TimeFrame::isClusterUsed(int layer, int clusterId) const -{ - return mUsedClusters[layer][clusterId]; -} - -inline void TimeFrame::markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } - -inline std::vector>& TimeFrame::getTracklets() +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) { - return mTracklets; + return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; } -inline std::vector>& TimeFrame::getTrackletsLookupTable() -{ - return mTrackletsLookupTable; -} - -inline void TimeFrame::initialiseRoadLabels() +template +inline void TimeFrame::initialiseRoadLabels() { mRoadLabels.clear(); mRoadLabels.resize(mRoads.size()); } -inline void TimeFrame::setRoadLabel(int i, const unsigned long long& lab, bool fake) +template +inline void TimeFrame::setRoadLabel(int i, const unsigned long long& lab, bool fake) { mRoadLabels[i].first = lab; mRoadLabels[i].second = fake; } -inline const unsigned long long& TimeFrame::getRoadLabel(int i) const -{ - return mRoadLabels[i].first; -} - -inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) +template +inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - auto startIdx{mROframesClusters[1][rofId]}; - return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROframesClusters[1][rofId + 1] - startIdx)}; + auto startIdx{mROFramesClusters[1][rofId]}; + return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - startIdx)}; } -inline int& TimeFrame::getNTrackletsROf(int rof, int combId) +template +inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) { - return mNTrackletsPerROf[combId][rof]; -} - -inline bool TimeFrame::isRoadFake(int i) const -{ - return mRoadLabels[i].second; -} - -inline std::vector>& TimeFrame::getClusters() -{ - return mClusters; -} - -inline std::vector>& TimeFrame::getUnsortedClusters() -{ - return mUnsortedClusters; -} - -inline std::vector>& TimeFrame::getCells() { return mCells; } + if (rofId < 0 || rofId >= mNrof) { + return {}; + } + auto clusStartIdx{mROFramesClusters[1][rofId]}; -inline std::vector>& TimeFrame::getCellsLookupTable() -{ - return mCellsLookupTable; + return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; } -inline std::vector>>& TimeFrame::getCellsNeighbours() +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - return mCellsNeighbours; + if (rofId < 0 || rofId >= mNrof || mTracklets[combId].empty()) { + return {}; + } + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline std::vector& TimeFrame::getRoads() { return mRoads; } - -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const { if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + return {}; } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const +template +inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const { - if (rofId < 0 || rofId >= mNrof) { - return gsl::span(); + if (rofId < 0 || rofId >= mNrof || !hasMCinformation()) { + return {}; } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + auto startIdx{mNTrackletsPerROF[combId][rofId]}; + return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const +template +inline int TimeFrame::getTotalClusters() const { - if (rofId < 0 || rofId >= mNrof || !hasMCinformation()) { - return gsl::span(); + size_t totalClusters{0}; + for (const auto& clusters : mUnsortedClusters) { + totalClusters += clusters.size(); } - auto startIdx{mNTrackletsPerROf[combId][rofId]}; - return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROf[combId][rofId + 1] - startIdx)}; + return int(totalClusters); } -inline int TimeFrame::getNumberOfClusters() const +template +inline int TimeFrame::getNumberOfClusters() const { int nClusters = 0; - for (auto& layer : mClusters) { + for (const auto& layer : mClusters) { nClusters += layer.size(); } return nClusters; } -inline int TimeFrame::getNumberOfCells() const +template +inline int TimeFrame::getNumberOfCells() const { int nCells = 0; - for (auto& layer : mCells) { + for (const auto& layer : mCells) { nCells += layer.size(); } return nCells; } -inline int TimeFrame::getNumberOfTracklets() const +template +inline int TimeFrame::getNumberOfTracklets() const { int nTracklets = 0; - for (auto& layer : mTracklets) { + for (const auto& layer : mTracklets) { nTracklets += layer.size(); } return nTracklets; } -inline int TimeFrame::getNumberOfTracks() const +template +inline int TimeFrame::getNumberOfNeighbours() const +{ + int n{0}; + for (const auto& l : mCellsNeighbours) { + n += l.size(); + } + return n; +} + +template +inline size_t TimeFrame::getNumberOfTracks() const { int nTracks = 0; - for (auto& t : mTracks) { + for (const auto& t : mTracks) { nTracks += t.size(); } return nTracks; } +template +inline size_t TimeFrame::getNumberOfUsedClusters() const +{ + size_t nClusters = 0; + for (const auto& layer : mUsedClusters) { + nClusters += std::count(layer.begin(), layer.end(), true); + } + return nClusters; +} + +template +inline void TimeFrame::insertPastVertex(const Vertex& vertex, const int iteration) +{ + int rofId = vertex.getTimeStamp().getTimeStamp(); + mPrimaryVertices.insert(mPrimaryVertices.begin() + mROFramesPV[rofId], vertex); + for (int i = rofId + 1; i < mROFramesPV.size(); ++i) { + mROFramesPV[i]++; + } + mTotVertPerIteration[iteration]++; +} + } // namespace its } // namespace o2 -#endif /* TRACKINGITSU_INCLUDE_TimeFrame_H_ */ +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index e5e830c717521..3ea382c626fed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -23,16 +23,20 @@ #include #include #include +#include #include #include +#include + #include "ITStracking/Configuration.h" #include "CommonConstants/MathConstants.h" #include "ITStracking/Definitions.h" -#include "ITStracking/ROframe.h" #include "ITStracking/MathUtils.h" #include "ITStracking/TimeFrame.h" +#include "ITStracking/TrackerTraits.h" #include "ITStracking/Road.h" +#include "ITStracking/BoundedAllocator.h" #include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -46,89 +50,103 @@ class GPUChainITS; } namespace its { -class TrackerTraits; +template class Tracker { + using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); - - Tracker(const Tracker&) = delete; - Tracker& operator=(const Tracker&) = delete; - ~Tracker(); + Tracker(TrackerTraits* traits); - void adoptTimeFrame(TimeFrame& tf); + void adoptTimeFrame(TimeFrame& tf); void clustersToTracks( - std::function = [](std::string s) { std::cout << s << std::endl; }, std::function = [](std::string s) { std::cerr << s << std::endl; }); - std::vector& getTracks(); + const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, + const LogFunc& = [](const std::string& s) { std::cerr << s << '\n'; }); - void setParameters(const std::vector&); + void setParameters(const std::vector& p) { mTrkParams = p; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } - void getGlobalConfiguration(); - void setBz(float); - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type); - bool isMatLUT() const; - void setNThreads(int n); - int getNThreads() const; + void setBz(float bz) { mTraits->setBz(bz); } + bool isMatLUT() const { return mTraits->isMatLUT(); } + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } + void printSummary() const; + void computeTracksMClabels(); private: - void initialiseTimeFrame(int& iteration); - void computeTracklets(int& iteration); - void computeCells(int& iteration); - void findCellsNeighbours(int& iteration); - void findRoads(int& iteration); - void findShortPrimaries(); - void findTracks(); - void extendTracks(int& iteration); + void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } + void computeTracklets(int iteration, int iROFslice, int iVertex) { mTraits->computeLayerTracklets(iteration, iROFslice, iVertex); } + void computeCells(int iteration) { mTraits->computeLayerCells(iteration); } + void findCellsNeighbours(int iteration) { mTraits->findCellsNeighbours(iteration); } + void findRoads(int iteration) { mTraits->findRoads(iteration); } + void findShortPrimaries() { mTraits->findShortPrimaries(); } + void extendTracks(int iteration) { mTraits->extendTracks(iteration); } // MC interaction void computeRoadsMClabels(); - void computeTracksMClabels(); void rectifyClusterIndices(); - template - float evaluateTask(void (Tracker::*)(T...), const char*, std::function logger, T&&... args); + template + float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); - TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class std::vector mTrkParams; - std::uint32_t mTimeFrameCounter = 0; o2::gpu::GPUChainITS* mRecoChain = nullptr; - unsigned int mNumberOfRuns{0}; + unsigned int mNumberOfDroppedTFs{0}; + unsigned int mTimeFrameCounter{0}; + double mTotalTime{0}; + std::shared_ptr mMemoryPool; + + enum State { + TFInit = 0, + Trackleting, + Celling, + Neighbouring, + Roading, + NStates, + }; + State mCurState{TFInit}; + static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding"}; }; -inline void Tracker::setParameters(const std::vector& trkPars) -{ - mTrkParams = trkPars; -} - -template -float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, std::function logger, - T&&... args) +template +template +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); - (this->*task)(std::forward(args)...); + (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration diff_t{end - start}; diff = diff_t.count(); std::stringstream sstream; - if (taskName == nullptr) { + if (taskName.empty()) { sstream << diff << "\t"; } else { sstream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms"; } logger(sstream.str()); + + if (mTrkParams[0].SaveTimeBenchmarks) { + std::string taskNameStr(taskName); + std::transform(taskNameStr.begin(), taskNameStr.end(), taskNameStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::replace(taskNameStr.begin(), taskNameStr.end(), ' ', '_'); + if (std::ofstream file{"its_time_benchmarks.txt", std::ios::app}) { + file << "trk:" << iteration << '\t' << taskNameStr << '\t' << diff << '\n'; + } + } + } else { - (this->*task)(std::forward(args)...); + (this->*task)(std::forward(args)...); } return diff; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 03ec737250f50..ddc32ed18cbfe 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -16,23 +16,15 @@ #ifndef TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ #define TRACKINGITSU_INCLUDE_TRACKERTRAITS_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "DetectorsBase/Propagator.h" -#include "DetectorsBase/MatLayerCylSet.h" #include "ITStracking/Configuration.h" -#include "ITStracking/Definitions.h" #include "ITStracking/MathUtils.h" +#include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" -#include "ITStracking/Road.h" +#include "ITStracking/Cell.h" +#include "ITStracking/BoundedAllocator.h" // #define OPTIMISATION_OUTPUT @@ -44,122 +36,99 @@ class GPUChainITS; } namespace its { - class TrackITSExt; -typedef std::function& roads, std::vector&, std::vector&, const std::vector>&, std::vector&)> FuncRunITSTrackFit_t; +template class TrackerTraits { public: + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; + virtual ~TrackerTraits() = default; - virtual void adoptTimeFrame(TimeFrame* tf); - virtual void initialiseTimeFrame(const int iteration); - virtual void computeLayerTracklets(const int iteration); + virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } + virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); } + + virtual void computeLayerTracklets(const int iteration, int iROFslice, int iVertex); virtual void computeLayerCells(const int iteration); virtual void findCellsNeighbours(const int iteration); virtual void findRoads(const int iteration); - virtual void findTracks(); + + virtual bool supportsExtendTracks() const noexcept { return true; } virtual void extendTracks(const int iteration); + virtual bool supportsFindShortPrimaries() const noexcept { return true; } virtual void findShortPrimaries(); - virtual void refitTracks(const int iteration, const std::vector>&, std::vector&); - virtual void setBz(float bz); + virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); + virtual void processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId); - void UpdateTrackingParameters(const std::vector& trkPars); - TimeFrame* getTimeFrame() { return mTimeFrame; } + void updateTrackingParameters(const std::vector& trkPars) { mTrkParams = trkPars; } + TimeFrame* getTimeFrame() { return mTimeFrame; } - void setIsGPU(const unsigned char isgpu) { mIsGPU = isgpu; }; - float getBz() const; - void setCorrType(const o2::base::PropagatorImpl::MatCorrType type) { mCorrType = type; } + virtual void setBz(float bz); + float getBz() const { return mBz; } bool isMatLUT() const; + virtual const char* getName() const noexcept { return "CPU"; } + virtual bool isGPU() const noexcept { return false; } + void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } + auto getMemoryPool() const noexcept { return mMemoryPool; } // Others - GPUhd() static constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - const int4 getBinsRect(const Cluster&, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz); - void SetRecoChain(o2::gpu::GPUChainITS* chain, FuncRunITSTrackFit_t&& funcRunITSTrackFit) - { - mChainRunITSTrackFit = funcRunITSTrackFit; - mChain = chain; - } + GPUhd() static consteval int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } + const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) const noexcept { return getBinsRect(layer, phi, maxdeltaphi, z, z, maxdeltaz); } + const int4 getBinsRect(const Cluster& cls, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept { return getBinsRect(layer, cls.phi, maxdeltaphi, z1, z2, maxdeltaz); } + const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept; + void SetRecoChain(o2::gpu::GPUChainITS* chain) { mChain = chain; } void setSmoothing(bool v) { mApplySmoothing = v; } bool getSmoothing() const { return mApplySmoothing; } - void setNThreads(int n); - int getNThreads() const { return mNThreads; } + void setNThreads(int n, std::shared_ptr& arena); + int getNThreads() { return mTaskArena->max_concurrency(); } - // TimeFrame information forwarding - virtual int getTFNumberOfClusters() const; - virtual int getTFNumberOfTracklets() const; - virtual int getTFNumberOfCells() const; + o2::gpu::GPUChainITS* getChain() const { return mChain; } - float mBz = 5.f; + // TimeFrame information forwarding + virtual int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } + virtual int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } + virtual int getTFNumberOfCells() const { return mTimeFrame->getNumberOfCells(); } private: - void traverseCellsTree(const int, const int); - track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const Cluster& cluster3, const TrackingFrameInfo& tf3); - bool fitTrack(TrackITSExt& track, int start, int end, int step, const float chi2clcut = o2::constants::math::VeryBig, const float chi2ndfcut = o2::constants::math::VeryBig, const float maxQoverPt = o2::constants::math::VeryBig); + track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, bool reverse = false); + TrackITSExt seedTrackForRefit(const CellSeedN& seed); + bool fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut = o2::constants::math::VeryBig, float chi2ndfcut = o2::constants::math::VeryBig, float maxQoverPt = o2::constants::math::VeryBig, int nCl = 0, o2::track::TrackPar* refLin = nullptr); - int mNThreads = 1; bool mApplySmoothing = false; - o2::base::PropagatorImpl::MatCorrType mCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; - - // virtual bool checkTFMemory(const int iteration); + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; protected: - TimeFrame* mTimeFrame; - std::vector mTrkParams; - bool mIsGPU = false; o2::gpu::GPUChainITS* mChain = nullptr; - FuncRunITSTrackFit_t mChainRunITSTrackFit; -}; - -inline float TrackerTraits::getBz() const -{ - return mBz; -} - -inline void TrackerTraits::UpdateTrackingParameters(const std::vector& trkPars) -{ - mTrkParams = trkPars; -} - -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z, float maxdeltaz) -{ - return getBinsRect(layerIndex, phi, maxdeltaphi, z, z, maxdeltaz); -} - -inline const int4 TrackerTraits::getBinsRect(const Cluster& currentCluster, int layerIndex, float z1, float z2, float maxdeltaz, float maxdeltaphi) -{ - return getBinsRect(layerIndex, currentCluster.phi, maxdeltaphi, z1, z2, maxdeltaz); -} + TimeFrame* mTimeFrame; + std::vector mTrkParams; -inline void TrackerTraits::initialiseTimeFrame(const int iteration) -{ - mTimeFrame->initialise(iteration, mTrkParams[iteration], 7); - setIsGPU(false); -} + float mBz{-999.f}; + bool mIsZeroField{false}; +}; -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, - float z1, float z2, float maxdeltaz) +template +inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept { const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; - const float phiRangeMin = phi - maxdeltaphi; + const float phiRangeMin = (maxdeltaphi > o2::constants::math::PI) ? 0.f : phi - maxdeltaphi; const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; - const float phiRangeMax = phi + maxdeltaphi; - - if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex + 1] || - zRangeMin > mTrkParams[0].LayerZ[layerIndex + 1] || zRangeMin > zRangeMax) { + const float phiRangeMax = (maxdeltaphi > o2::constants::math::PI) ? o2::constants::math::TwoPI : phi + maxdeltaphi; + if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex] || + zRangeMin > mTrkParams[0].LayerZ[layerIndex] || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } - const IndexTableUtils& utils{mTimeFrame->mIndexTableUtils}; - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + const IndexTableUtilsN& utils{mTimeFrame->mIndexTableUtils}; + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex, zRangeMin)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), // /!\ trkParams can potentially change across iterations + o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), // /!\ trkParams can potentially change across iterations utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } + } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 94c629d6c22d3..0529bd53f2073 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -12,19 +12,22 @@ #ifndef ALICEO2_ITSDPLTRACKINGPARAM_H_ #define ALICEO2_ITSDPLTRACKINGPARAM_H_ +#include #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" -namespace o2 -{ -namespace its +namespace o2::its { struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { + bool saveTimeBenchmarks = false; // dump metrics on file - bool allowSingleContribClusters = false; + int nIterations = 1; // Number of vertexing passes to perform. + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. + bool allowSingleContribClusters = false; // attempt to find vertices in case of a single tracklet found. + int deltaRof = 0; // Number of ROFs to be considered for the vertexing. - // geometrical cuts + // geometrical cuts for tracklet selection float zCut = 0.002f; float phiCut = 0.005f; float pairCut = 0.04f; @@ -38,25 +41,37 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper::max(); + bool dropTFUponFailure = false; O2ParamDef(VertexerParamConfig, "ITSVertexerParam"); }; struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper { - // Use TGeo for mat. budget - bool useMatCorrTGeo = false; - bool useFastMaterial = false; - float sysErrY2[7] = {0}; // systematic error^2 in Y per layer - float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer + static const int MaxIter = 4; + static const int MinTrackLength = 4; + static const int MaxTrackLength = 7; + bool useMatCorrTGeo = false; // use full geometry to corect for material budget accounting in the fits. Default is to use the material budget LUT. + bool useFastMaterial = false; // use faster material approximation for material budget accounting in the fits. + int deltaRof = 0; // configure the width of the window in ROFs to be considered for the tracking. + int minTrackLgtIter[MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[MaxIter] = {}; // mask of start layer for this iteration (if >0) + float minPtIterLgt[MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + float sysErrY2[7] = {0}; // systematic error^2 in Y per layer + float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer float maxChi2ClusterAttachment = -1.f; float maxChi2NDF = -1.f; float nSigmaCut = -1.f; @@ -65,37 +80,73 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper 0 off + float trackFollowerNSigmaZ = 1.f; // sigma in z-cut for track-following search rectangle + float trackFollowerNSigmaPhi = 1.f; // sigma in phi-cut for track-following search rectangle float cellsPerClusterLimit = -1.f; float trackletsPerClusterLimit = -1.f; int findShortTracks = -1; + int nROFsPerIterations = 0; // size of the slice of ROFs to be processed at a time, preferably integer divisors of nROFs per TF, to balance the iterations. + int nOrbitsPerIterations = 0; // not implemented: size of the slice of ROFs to be processed at a time, computed using the number of ROFs per orbit. + bool perPrimaryVertexProcessing = false; // perform the full tracking considering the vertex hypotheses one at the time. + bool saveTimeBenchmarks = false; // dump metrics on file + bool overrideBeamEstimation = false; // use beam position from meanVertex CCDB object + int trackingMode = -1; // -1: unset, 0=sync, 1=async, 2=cosmics used by gpuwf only + bool doUPCIteration = false; // Perform an additional iteration for UPC events on tagged vertices. You want to combine this config with VertexerParamConfig.nIterations=2 + int nIterations = MaxIter; // overwrite the number of iterations + int reseedIfShorter = 6; // for the final refit reseed the track with circle if they are shorter than this value + bool shiftRefToCluster{true}; // TrackFit: after update shift the linearization reference to cluster + bool repeatRefitOut{false}; // repeat outward refit using inward refit as a seed + bool createArtefactLabels{false}; // create on-the-fly labels for the artefacts + int nThreads = 1; + bool printMemory = false; + size_t maxMemory = std::numeric_limits::max(); + bool dropTFUponFailure = false; + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); }; -struct GpuRecoParamConfig : public o2::conf::ConfigurableParamHelper { - // GPU-specific parameters - size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes - size_t maxTrackletsPerCluster = 1e2; - size_t clustersPerLayerCapacity = 2.5e5; - size_t clustersPerROfCapacity = 1.5e3; - size_t trackletsCapacity = maxTrackletsPerCluster * clustersPerLayerCapacity; - size_t validatedTrackletsCapacity = 1e5; - size_t cellsLUTsize = validatedTrackletsCapacity; - size_t maxNeighboursSize = 1e4; - size_t neighboursLUTsize = maxNeighboursSize; - size_t maxLinesCapacity = 1e2; - size_t maxVerticesCapacity = 5e4; - size_t nTimeFramePartitions = 3; - int maxGPUMemoryGB = -1; - - O2ParamDef(GpuRecoParamConfig, "ITSGpuRecoParam"); +struct ITSGpuTrackingParamConfig : public o2::conf::ConfigurableParamHelper { + static constexpr int MaxIter = TrackerParamConfig::MaxIter; + + /// Set nBlocks/nThreads to summarily override all kernel launch parameters in each iteration. + /// Parameters must start with nBlocks/nThreads. + static constexpr int OverrideValue{-1}; + static constexpr char const* BlocksName = "nBlocks"; + static constexpr char const* ThreadsName = "nThreads"; + int nBlocks = OverrideValue; + int nThreads = OverrideValue; + void maybeOverride() const; + + /// Individual kernel launch parameter for each iteration + int nBlocksLayerTracklets[MaxIter] = {60, 60, 60, 60}; + int nThreadsLayerTracklets[MaxIter] = {256, 256, 256, 256}; + + int nBlocksLayerCells[MaxIter] = {60, 60, 60, 60}; + int nThreadsLayerCells[MaxIter] = {256, 256, 256, 256}; + + int nBlocksFindNeighbours[MaxIter] = {60, 60, 60, 60}; + int nThreadsFindNeighbours[MaxIter] = {256, 256, 256, 256}; + + int nBlocksProcessNeighbours[MaxIter] = {60, 60, 60, 60}; + int nThreadsProcessNeighbours[MaxIter] = {256, 256, 256, 256}; + + int nBlocksTracksSeeds[MaxIter] = {60, 60, 60, 60}; + int nThreadsTracksSeeds[MaxIter] = {256, 256, 256, 256}; + + int nBlocksVtxComputeTracklets[2] = {60, 60}; + int nThreadsVtxComputeTracklets[2] = {256, 256}; + + int nBlocksVtxComputeMatching[2] = {60, 60}; + int nThreadsVtxComputeMatching[2] = {256, 256}; + + O2ParamDef(ITSGpuTrackingParamConfig, "ITSGpuTrackingParam"); }; -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h new file mode 100644 index 0000000000000..a882ca9b779c4 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -0,0 +1,102 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS_TRACKINGINTERFACE +#define O2_ITS_TRACKINGINTERFACE + +#include "Framework/DataProcessorSpec.h" + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Tracker.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/Vertexer.h" +#include "ITStracking/VertexerTraits.h" +#include "ITStracking/BoundedAllocator.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "DataFormatsCalibration/MeanVertexObject.h" + +#include "GPUDataTypesIO.h" +#include "GPUO2ExternalUser.h" +#include "GPUChainITS.h" + +#include + +namespace o2::its +{ +class ITSTrackingInterface +{ + static constexpr int NLayers{7}; + using VertexerN = Vertexer; + using VertexerTraitsN = VertexerTraits; + using TrackerN = Tracker; + using TrackerTraitsN = TrackerTraits; + using TimeFrameN = TimeFrame; + + public: + ITSTrackingInterface(bool isMC, + int trgType, + const bool overrBeamEst) + : mIsMC{isMC}, + mUseTriggers{trgType}, + mOverrideBeamEstimation{overrBeamEst} {} + + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + void setMeanVertex(const o2::dataformats::MeanVertexObject* v) + { + if (v == nullptr) { + LOGP(error, "Mean Vertex Object is nullptr"); + return; + } else { + LOGP(info, "Mean Vertex set with x: {} y: {}", v->getX(), v->getY()); + } + mMeanVertex = v; + } + // Task callbacks + void initialise(); + void run(framework::ProcessingContext& pc); + void printSummary() const; + + virtual void updateTimeDependentParams(framework::ProcessingContext& pc); + virtual void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj); + + // Custom + void setTraitsFromProvider(VertexerTraitsN*, TrackerTraitsN*, TimeFrameN*); + void setTrackingMode(TrackingMode::Type mode = TrackingMode::Unset) { mMode = mode; } + + auto getTracker() const { return mTracker.get(); } + auto getVertexer() const { return mVertexer.get(); } + + TimeFrameN* mTimeFrame = nullptr; + + protected: + virtual void loadROF(gsl::span& trackROFspan, + gsl::span clusters, + gsl::span::iterator& pattIt, + const dataformats::MCTruthContainer* mcLabels); + + private: + bool mIsMC = false; + bool mRunVertexer = true; + bool mCosmicsProcessing = false; + int mUseTriggers = 0; + TrackingMode::Type mMode = TrackingMode::Unset; + bool mOverrideBeamEstimation = false; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + std::unique_ptr mTracker = nullptr; + std::unique_ptr mVertexer = nullptr; + const o2::dataformats::MeanVertexObject* mMeanVertex; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; +}; + +} // namespace o2::its +#endif // O2_ITS_TRACKINGINTERFACE diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index a7f7adbb8abad..e6c9db55198a3 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h @@ -13,89 +13,77 @@ /// \brief /// -#ifndef TRACKINGITSU_INCLUDE_TRACKLET_H_ -#define TRACKINGITSU_INCLUDE_TRACKLET_H_ +#ifndef TRACKINGITS_INCLUDE_TRACKLET_H_ +#define TRACKINGITS_INCLUDE_TRACKLET_H_ +#include "ITStracking/Constants.h" #include "ITStracking/Cluster.h" -#include +#include "GPUCommonRtypes.h" #include "GPUCommonMath.h" #include "GPUCommonDef.h" +#include "GPUCommonLogger.h" -namespace o2 -{ -namespace its +#ifndef GPUCA_GPUCODE_DEVICE +#ifndef GPU_NO_FMT +#include +#include +#endif +#endif + +namespace o2::its { struct Tracklet final { - GPUhdi() Tracklet(); - GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, int rof0, int rof1); - GPUhdi() Tracklet(const int, const int, float tanL, float phi, int rof0, int rof1); - GPUhdi() bool operator==(const Tracklet&) const; - GPUhdi() bool operator!=(const Tracklet&) const; + GPUhdDefault() Tracklet() = default; + GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, short rof0, short rof1); + GPUhdi() Tracklet(const int, const int, float tanL, float phi, short rof0, short rof1); + GPUhdDefault() bool operator==(const Tracklet&) const = default; GPUhdi() unsigned char isEmpty() const { return firstClusterIndex < 0 || secondClusterIndex < 0; } - GPUhdi() void dump(); - GPUhdi() void dump() const; - GPUhdi() void dump(const int, const int); - GPUhdi() void dump(const int, const int) const; + GPUhdi() auto getMinRof() const noexcept { return o2::gpu::CAMath::Min(rof[0], rof[1]); } + GPUhdi() auto getMaxRof() const noexcept { return o2::gpu::CAMath::Max(rof[0], rof[1]); } + GPUhdi() auto getDeltaRof() const { return rof[1] - rof[0]; } + GPUhdi() auto getSpanRof(const Tracklet& o) const noexcept { return o2::gpu::CAMath::Max(getMaxRof(), o.getMaxRof()) - o2::gpu::CAMath::Min(getMinRof(), o.getMinRof()); } GPUhdi() unsigned char operator<(const Tracklet&) const; + GPUhd() void print() const + { + printf("TRKLT: fClIdx:%d fROF:%d sClIdx:%d sROF:%d (DROF:%d) tgl=%f phi=%f\n", firstClusterIndex, rof[0], secondClusterIndex, rof[1], getDeltaRof(), tanLambda, phi); + } - int firstClusterIndex; - int secondClusterIndex; - float tanLambda; - float phi; - unsigned short rof[2]; -}; + int firstClusterIndex{constants::UnusedIndex}; + int secondClusterIndex{constants::UnusedIndex}; + float tanLambda{-999}; + float phi{-999}; + short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; -GPUhdi() Tracklet::Tracklet() : firstClusterIndex{-1}, secondClusterIndex{-1}, tanLambda{0.0f}, phi{0.0f} -{ - rof[0] = 0; - rof[1] = 0; -} + ClassDefNV(Tracklet, 1); +}; GPUhdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, - const Cluster& firstCluster, const Cluster& secondCluster, int rof0 = -1, int rof1 = -1) + const Cluster& firstCluster, const Cluster& secondCluster, short rof0 = -1, short rof1 = -1) : firstClusterIndex{firstClusterOrderingIndex}, secondClusterIndex{secondClusterOrderingIndex}, tanLambda{(firstCluster.zCoordinate - secondCluster.zCoordinate) / (firstCluster.radius - secondCluster.radius)}, phi{o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, firstCluster.xCoordinate - secondCluster.xCoordinate)}, - rof{static_cast(rof0), static_cast(rof1)} + rof{static_cast(rof0), static_cast(rof1)} { // Nothing to do } -GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, int rof0, int rof1) +GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, short rof0, short rof1) : firstClusterIndex{idx0}, secondClusterIndex{idx1}, tanLambda{tanL}, phi{phi}, - rof{static_cast(rof0), static_cast(rof1)} + rof{static_cast(rof0), static_cast(rof1)} { // Nothing to do } -GPUhdi() bool Tracklet::operator==(const Tracklet& rhs) const -{ - return this->firstClusterIndex == rhs.firstClusterIndex && - this->secondClusterIndex == rhs.secondClusterIndex && - this->tanLambda == rhs.tanLambda && - this->phi == rhs.phi && - this->rof[0] == rhs.rof[0] && - this->rof[1] == rhs.rof[1]; -} - -GPUhdi() bool Tracklet::operator!=(const Tracklet& rhs) const -{ - return this->firstClusterIndex != rhs.firstClusterIndex || - this->secondClusterIndex != rhs.secondClusterIndex || - this->tanLambda != rhs.tanLambda || - this->phi != rhs.phi; -} - GPUhdi() unsigned char Tracklet::operator<(const Tracklet& t) const { if (isEmpty()) { @@ -104,47 +92,6 @@ GPUhdi() unsigned char Tracklet::operator<(const Tracklet& t) const return true; } -// GPUhdi() void Tracklet::dump() -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex, secondClusterIndex, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump() const -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex, secondClusterIndex, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1], phi, tanLambda); -// } - -// GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) const -// { -// printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu phi: %f tl: %f \n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1], phi, tanLambda); -// } - -GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1]); -} - -GPUhdi() void Tracklet::dump(const int offsetFirst, const int offsetSecond) const -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex + offsetFirst, secondClusterIndex + offsetSecond, rof[0], rof[1]); -} - -GPUhdi() void Tracklet::dump() -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex, secondClusterIndex, rof[0], rof[1]); -} - -GPUhdi() void Tracklet::dump() const -{ - printf("fClIdx: %d sClIdx: %d rof1: %hu rof2: %hu\n", firstClusterIndex, secondClusterIndex, rof[0], rof[1]); -} - -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif /* TRACKINGITSU_INCLUDE_TRACKLET_H_ */ +#endif /* TRACKINGITS_INCLUDE_TRACKLET_H_ */ diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index 6e245966c318f..d66bcd6ee2358 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -21,107 +21,110 @@ #include #include #include +#include + +#include -#include "ITStracking/ROframe.h" #include "ITStracking/Constants.h" +#include "ITStracking/Definitions.h" #include "ITStracking/Configuration.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/VertexerTraits.h" -#include "ReconstructionDataFormats/Vertex.h" - -#include "ITStracking/ClusterLines.h" -#include "ITStracking/Tracklet.h" -#include "ITStracking/Cluster.h" - -#include "GPUCommonLogger.h" +#include "ITStracking/BoundedAllocator.h" -class TTree; - -namespace o2 -{ -namespace its +namespace o2::its { -using TimeFrame = o2::its::TimeFrame; -using Vertex = o2::dataformats::Vertex>; +template class Vertexer { + using TimeFrameN = TimeFrame; + using VertexerTraitsN = VertexerTraits; + using LogFunc = std::function; + public: - Vertexer(VertexerTraits* traits); + Vertexer(VertexerTraitsN* traits); virtual ~Vertexer() = default; Vertexer(const Vertexer&) = delete; Vertexer& operator=(const Vertexer&) = delete; - void adoptTimeFrame(TimeFrame& tf); - VertexingParameters& getVertParameters() const; - void getGlobalConfiguration(); + void adoptTimeFrame(TimeFrameN& tf); + auto& getVertParameters() const { return mTraits->getVertexingParameters(); } + void setParameters(const std::vector& vertParams) { mVertParams = vertParams; } + const auto& getParameters() const noexcept { return mVertParams; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector exportVertices(); - VertexerTraits* getTraits() const { return mTraits; }; + VertexerTraitsN* getTraits() const { return mTraits; }; - float clustersToVertices(std::function = [](std::string s) { std::cout << s << std::endl; }); + float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); - void validateTracklets(); template - void findTracklets(T&&... args); + void findTracklets(T&&... args) + { + mTraits->computeTracklets(std::forward(args)...); + } + template + void validateTracklets(T&&... args) + { + mTraits->computeTrackletMatching(std::forward(args)...); + } + template + void findVertices(T&&... args) + { + mTraits->computeVertices(std::forward(args)...); + } - void findTrivialMCTracklets(); - void findVertices(); - void findHistVertices(); + void addTruthSeeds() { mTraits->addTruthSeedingVertices(); } template - void initialiseVertexer(T&&... args); + void initialiseVertexer(T&&... args) + { + mTraits->initialise(std::forward(args)...); + } template void initialiseTimeFrame(T&&... args); // Utils - void dumpTraits(); template - float evaluateTask(void (Vertexer::*)(T...), const char*, std::function logger, T&&... args); - void printEpilog(std::function logger, const float total); + float evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args); + + void printEpilog(LogFunc& logger, + const unsigned int trackletN01, const unsigned int trackletN12, + const unsigned selectedN, const unsigned int vertexN, const float initT, + const float trackletT, const float selecT, const float vertexT); + + void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } private: std::uint32_t mTimeFrameCounter = 0; - VertexerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + VertexerTraitsN* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrameN* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + + std::vector mVertParams; + std::shared_ptr mMemoryPool; + + enum State { + Init = 0, + Trackleting, + Validating, + Finding, + TruthSeeding, + NStates, + }; + State mCurState{Init}; + static constexpr std::array StateNames{"Initialisation", "Tracklet finding", "Tracklet validation", "Vertex finding", "Truth seeding"}; }; +template template -void Vertexer::initialiseVertexer(T&&... args) -{ - mTraits->initialise(std::forward(args)...); -} - -template -void Vertexer::findTracklets(T&&... args) -{ - mTraits->computeTracklets(std::forward(args)...); -} - -inline VertexingParameters& Vertexer::getVertParameters() const -{ - return mTraits->getVertexingParameters(); -} - -inline void Vertexer::dumpTraits() -{ - mTraits->dumpVertexerTraits(); -} - -inline void Vertexer::validateTracklets() -{ - mTraits->computeTrackletMatching(); -} - -template -float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, std::function logger, - T&&... args) +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); @@ -130,12 +133,22 @@ float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, diff = diff_t.count(); std::stringstream sstream; - if (taskName == nullptr) { + if (taskName.empty()) { sstream << diff << "\t"; } else { sstream << std::setw(2) << " - " << taskName << " completed in: " << diff << " ms"; } logger(sstream.str()); + + if (mVertParams[0].SaveTimeBenchmarks) { + std::string taskNameStr(taskName); + std::transform(taskNameStr.begin(), taskNameStr.end(), taskNameStr.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::replace(taskNameStr.begin(), taskNameStr.end(), ' ', '_'); + if (std::ofstream file{"its_time_benchmarks.txt", std::ios::app}) { + file << "vtx:" << iteration << '\t' << taskNameStr << '\t' << diff << '\n'; + } + } } else { (this->*task)(std::forward(args)...); } @@ -143,6 +156,5 @@ float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, return diff; } -} // namespace its -} // namespace o2 +} // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index 4a86dbf29c1eb..b1422d66e12df 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -17,9 +17,11 @@ #define O2_ITS_TRACKING_VERTEXER_TRAITS_H_ #include +#include #include #include +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cluster.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Configuration.h" @@ -27,103 +29,115 @@ #include "ITStracking/IndexTableUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/Tracklet.h" +#include "ITStracking/MathUtils.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" +#include + namespace o2 { class MCCompLabel; namespace its { -class ROframe; -using constants::its::LayersNumberVertexer; - -enum class TrackletMode { - Layer0Layer1 = 0, - Layer1Layer2 = 2 -}; +template class VertexerTraits { + using IndexTableUtilsN = IndexTableUtils; + using TimeFrameN = TimeFrame; + public: VertexerTraits() = default; virtual ~VertexerTraits() = default; - GPUhd() static constexpr int4 getEmptyBinsRect() + GPUhdi() static consteval int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } GPUhd() const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi); - GPUhd() const int2 getPhiBins(float phi, float deltaPhi); - - GPUhd() static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi, const IndexTableUtils&); - GPUhd() static const int2 getPhiBins(float phi, float deltaPhi, const IndexTableUtils&); + GPUhd() static const int4 getBinsRect(const Cluster&, const int, const float, float maxdeltaz, float maxdeltaphi, const IndexTableUtilsN&); + GPUhd() static const int2 getPhiBins(float phi, float deltaPhi, const IndexTableUtilsN&); + GPUhd() const int2 getPhiBins(float phi, float deltaPhi) { return getPhiBins(phi, deltaPhi, mIndexTableUtils); } // virtual vertexer interface - virtual void initialise(const TrackingParameters& trackingParams); - virtual void computeTracklets(); - virtual void computeTrackletMatching(); - virtual void computeVertices(); - virtual void adoptTimeFrame(TimeFrame* tf); - virtual void updateVertexingParameters(const VertexingParameters& vrtPar, const TimeFrameGPUParameters& gpuTfPar); - - void computeVerticesInRof(int, - gsl::span&, - std::vector&, - std::vector&, - std::array&, - std::vector&, - std::vector&, - TimeFrame*, - std::vector*); - - VertexingParameters getVertexingParameters() const { return mVrtParams; } - static const std::vector> selectClusters(const int* indexTable, - const std::array& selectedBinsRect, - const IndexTableUtils& utils); + virtual void initialise(const TrackingParameters& trackingParams, const int iteration = 0); + virtual void computeTracklets(const int iteration = 0); + virtual void computeTrackletMatching(const int iteration = 0); + virtual void computeVertices(const int iteration = 0); + virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } + virtual void updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& gpuTfPar); + + // truth tracking + void addTruthSeedingVertices(); // utils - VertexingParameters& getVertexingParameters() { return mVrtParams; } - void setIsGPU(const unsigned char isgpu) { mIsGPU = isgpu; }; - unsigned char getIsGPU() const { return mIsGPU; }; - void dumpVertexerTraits(); - void setNThreads(int n); - int getNThreads() const { return mNThreads; } + auto& getVertexingParameters() { return mVrtParams; } + auto getVertexingParameters() const { return mVrtParams; } + void setVertexingParameters(std::vector& vertParams) { mVrtParams = vertParams; } + void setNThreads(int n, std::shared_ptr& arena); + int getNThreads() { return mTaskArena->max_concurrency(); } + virtual bool isGPU() const noexcept { return false; } + virtual const char* getName() const noexcept { return "CPU"; } + virtual bool usesMemoryPool() const noexcept { return true; } + void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } + + static std::pair computeMain(const bounded_vector& elements) + { + // we only care about the source&event of the tracks, not the trackId + auto composeVtxLabel = [](const o2::MCCompLabel& lbl) -> o2::MCCompLabel { + return {o2::MCCompLabel::maxTrackID(), lbl.getEventID(), lbl.getSourceID(), lbl.isFake()}; + }; + std::unordered_map frequency; + for (const auto& element : elements) { + ++frequency[composeVtxLabel(element)]; + } + o2::MCCompLabel elem{}; + size_t maxCount = 0; + for (const auto& [key, count] : frequency) { + if (count > maxCount) { + maxCount = count; + elem = key; + } + } + return std::make_pair(elem, static_cast(maxCount) / static_cast(elements.size())); + } protected: - unsigned char mIsGPU; - int mNThreads = 1; - - VertexingParameters mVrtParams; - IndexTableUtils mIndexTableUtils; - std::vector mVertices; + std::vector mVrtParams; + IndexTableUtilsN mIndexTableUtils; // Frame related quantities - TimeFrame* mTimeFrame = nullptr; + TimeFrameN* mTimeFrame = nullptr; // observer ptr + private: + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; + + // debug output + void debugComputeTracklets(int iteration); + void debugComputeTrackletMatching(int iteration); + void debugComputeVertices(int iteration); }; -inline void VertexerTraits::initialise(const TrackingParameters& trackingParams) -{ - mTimeFrame->initialise(0, trackingParams, 3); - setIsGPU(false); -} - -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi) +template +inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) { - return VertexerTraits::getPhiBins(phi, dPhi, mIndexTableUtils); + mTimeFrame->initialise(0, trackingParams, 3, (bool)(!iteration)); // iteration for initialisation must be 0 for correctly resetting the frame, we need to pass the non-reset flag for vertices as well, tho. } -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtils& utils) +template +GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) { return int2{utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi - dPhi)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi + dPhi))}; } -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi, - const IndexTableUtils& utils) +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float directionZIntersection, float maxdeltaz, float maxdeltaphi, + const IndexTableUtilsN& utils) { const float zRangeMin = directionZIntersection - 2 * maxdeltaz; const float phiRangeMin = currentCluster.phi - maxdeltaphi; @@ -141,14 +155,13 @@ GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, c utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, - const float directionZIntersection, float maxdeltaz, float maxdeltaphi) +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, + const float directionZIntersection, float maxdeltaz, float maxdeltaphi) { return VertexerTraits::getBinsRect(currentCluster, layerIndex, directionZIntersection, maxdeltaz, maxdeltaphi, mIndexTableUtils); } -inline void VertexerTraits::adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } - } // namespace its } // namespace o2 #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h deleted file mode 100644 index d1d246b329907..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/json.h +++ /dev/null @@ -1,16313 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.1.2 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -Copyright (c) 2013-2018 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef NLOHMANN_JSON_HPP -#define NLOHMANN_JSON_HPP - -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 1 -#define NLOHMANN_JSON_VERSION_PATCH 2 - -#include // all_of, find, for_each -#include // assert -#include // and, not, or -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#include // istream, ostream -#include // iterator_traits, random_access_iterator_tag -#include // accumulate -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap - -// #include -#ifndef NLOHMANN_JSON_FWD_HPP -#define NLOHMANN_JSON_FWD_HPP - -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector - -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument - -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer; - -template