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/clean-test.yml b/.github/workflows/clean-test.yml index c926d5a7dea46..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,18 +27,23 @@ 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 - type: boolean - default: true 'check_build/O2/o2-dataflow-cs8': description: build/O2/o2-dataflow-cs8 type: boolean default: true + 'check_build/O2/o2/aarch64': + description: build/O2/o2/aarch64 + type: boolean + default: true + 'check_build/O2/o2_slc9': + description: build/O2/o2_slc9 + type: boolean + default: true + permissions: {} 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/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 affd78216b50e..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 + - uses: actions/checkout@v5 - name: Set up Python 3.10 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.10' - - uses: actions/cache@v2 + - uses: actions/cache@v5 name: Configure pip caching with: path: ~/.cache/pip @@ -126,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 6db76441528d9..d58d1e151800b 100644 --- a/.gitignore +++ b/.gitignore @@ -82,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/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 9436fa37de8e6..691c3311e117c 100644 --- a/CCDB/CMakeLists.txt +++ b/CCDB/CMakeLists.txt @@ -92,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 7994c60448121..fd0fe7aa6d05b 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -20,9 +20,12 @@ #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 @@ -57,6 +60,7 @@ class CCDBManagerInstance int queries = 0; int fetches = 0; int failures = 0; + std::map cacheOfHeaders; bool isValid(long ts) { return ts < endvalidity && ts >= startvalidity; } bool isCacheValid(long ts) { @@ -70,6 +74,7 @@ class CCDBManagerInstance uuid = ""; startvalidity = 0; endvalidity = -1; + cacheOfHeaders.clear(); } }; @@ -98,19 +103,28 @@ 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; } @@ -121,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 @@ -186,7 +203,7 @@ class CCDBManagerInstance /// 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; } @@ -220,13 +237,14 @@ class CCDBManagerInstance }; 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) { @@ -242,15 +260,32 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) mFetchedSize += s; } } + + if (headers) { + *headers = mHeaders; + } } else { auto& cached = mCache[path]; 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++; @@ -297,7 +332,6 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp) } else { cached.cacheValidUntil = -1; } - mHeaders.clear(); mMetaData.clear(); if (!ptr) { if (mFatalWhenNull) { @@ -308,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: @@ -338,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 e53421dcc26fc..6c057a537a096 100644 --- a/CCDB/include/CCDB/CCDBDownloader.h +++ b/CCDB/include/CCDB/CCDBDownloader.h @@ -47,6 +47,7 @@ struct HeaderObjectPair_t { typedef struct DownloaderRequestData { std::vector hosts; + std::vector locations; std::string path; long timestamp; HeaderObjectPair_t hoPair; @@ -87,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 @@ -226,12 +232,13 @@ class CCDBDownloader std::string prepareRedirectedURL(std::string address, std::string potentialHost) const; /** - * Returns a vector of possible content locations based on the redirect headers. + * Updates the locations vector with the the locations. * - * @param baseUrl Content path. * @param headerMap Map containing response headers. + * @param locations Location list to be updated. + * @param locIndex Index of the next locaiton to be tried. */ - std::vector getLocations(std::multimap* headerMap) const; + void updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const; std::string mUserAgentId = "CCDBDownloader"; /** diff --git a/CCDB/include/CCDB/CcdbApi.h b/CCDB/include/CCDB/CcdbApi.h index ea396a5b49402..4dab11d5972d8 100644 --- a/CCDB/include/CCDB/CcdbApi.h +++ b/CCDB/include/CCDB/CcdbApi.h @@ -281,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. @@ -348,6 +348,11 @@ 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__) typedef struct RequestContext { o2::pmr::vector& dest; @@ -383,7 +388,7 @@ class CcdbApi //: public DatabaseInterface 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) const; + 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, @@ -392,6 +397,9 @@ class CcdbApi //: public DatabaseInterface // 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; } template @@ -548,7 +556,7 @@ class CcdbApi //: public DatabaseInterface * @param tcl The TClass object describing the serialized type * @return raw pointer to created object */ - void* downloadFilesystemContent(std::string const& fullUrl, std::type_info const& tinfo, std::map* headers) const; + void* downloadFilesystemContent(std::string const& fullUrl, std::type_info const& tinfo, std::map* headers) const; // initialize the TGrid (Alien connection) bool initTGrid() const; @@ -568,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; @@ -610,6 +615,16 @@ 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; @@ -632,6 +647,8 @@ class CcdbApi //: public DatabaseInterface 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); }; 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 0fe72c88fcb46..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,44 +34,47 @@ 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) { + if (headers.size() != 0) { std::string report{}; - auto strt = response.find("STF"); - auto stop = response.find("ETF"); - long valStrt = (strt == response.end()) ? -1L : boost::lexical_cast(strt->second); - long valStop = (stop == response.end()) ? -1L : boost::lexical_cast(stop->second); + 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 = response.find("SOX"); - valStrt = (strt == response.end()) ? -1L : boost::lexical_cast(strt->second); + 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 = response.find("SOR"); - valStrt = (strt == response.end()) ? -1L : boost::lexical_cast(strt->second); + strt = headers.find("SOR"); + valStrt = (strt == headers.end()) ? -1L : boost::lexical_cast(strt->second); } - stop = response.find("EOX"); - valStop = (stop == response.end()) ? -1L : boost::lexical_cast(stop->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 = response.find("EOR"); - valStop = (stop == response.end()) ? -1L : boost::lexical_cast(stop->second); + stop = headers.find("EOR"); + valStop = (stop == headers.end()) ? -1L : boost::lexical_cast(stop->second); } if (!report.empty()) { LOGP(warn, "{}", report); } } - if (valStrt > 0 && valStop >= valStrt) { - return std::make_pair(valStrt, valStop); - } + return std::make_pair(valStrt, valStop); } - // failure - if (fatal) { + return std::make_pair(-1L, -1L); +} + +std::pair CCDBManagerInstance::getRunDuration(o2::ccdb::CcdbApi const& api, int runnumber, bool 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 std::make_pair(-1L, -1L); + return response; } std::pair CCDBManagerInstance::getRunDuration(int runnumber, bool fatal) diff --git a/CCDB/src/CCDBDownloader.cxx b/CCDB/src/CCDBDownloader.cxx index eac0c07f03131..2f033a50b36e7 100644 --- a/CCDB/src/CCDBDownloader.cxx +++ b/CCDB/src/CCDBDownloader.cxx @@ -12,6 +12,7 @@ #include #include "CommonUtils/StringUtils.h" #include "CCDB/CCDBTimeStampUtils.h" +#include "Framework/Signpost.h" #include #include @@ -29,29 +30,38 @@ #include #include +O2_DECLARE_DYNAMIC_STACKTRACE_LOG(ccdb_downloader); + namespace o2::ccdb { -void uvErrorCheck(int code) +void uvErrorCheck(int code, DownloaderErrorLevel level) { if (code != 0) { char buf[1000]; uv_strerror_r(code, buf, 1000); - LOG(error) << "CCDBDownloader: UV error - " << buf; + 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) { - LOG(error) << "CCDBDownloader: CURL error - " << curl_easy_strerror(code); + 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) { - LOG(error) << "CCDBDownloader: CURL error - " << curl_multi_strerror(code); + 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 @@ -82,7 +92,7 @@ CCDBDownloader::CCDBDownloader(uv_loop_t* uv_loop) // Preparing timer to be used by curl mTimeoutTimer = (uv_timer_t*)malloc(sizeof(*mTimeoutTimer)); mTimeoutTimer->data = this; - uvErrorCheck(uv_timer_init(mUVLoop, mTimeoutTimer)); + uvErrorCheck(uv_timer_init(mUVLoop, mTimeoutTimer), SEVERE); mHandleMap[(uv_handle_t*)mTimeoutTimer] = true; initializeMultiHandle(); @@ -91,7 +101,7 @@ CCDBDownloader::CCDBDownloader(uv_loop_t* uv_loop) void CCDBDownloader::setupInternalUVLoop() { mUVLoop = new uv_loop_t(); - uvErrorCheck(uv_loop_init(mUVLoop)); + uvErrorCheck(uv_loop_init(mUVLoop), SEVERE); } void CCDBDownloader::initializeMultiHandle() @@ -147,7 +157,7 @@ void CCDBDownloader::closesocketCallback(void* clientp, curl_socket_t item) // 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)); + 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) { @@ -155,12 +165,14 @@ void CCDBDownloader::closesocketCallback(void* clientp, curl_socket_t item) } CD->mSocketTimerMap.erase(item); if (close(item) == -1) { - LOG(error) << "CCDBDownloader: Socket failed to close"; + 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) { - LOG(error) << "CCDBDownloader: Socket failed to close"; + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); } } } @@ -170,12 +182,13 @@ 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) { - LOG(error) << "CCDBDownloader: Socket failed to open"; + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to open"); } if (CD->mExternalLoop) { CD->mSocketTimerMap[sock] = (uv_timer_t*)malloc(sizeof(*CD->mSocketTimerMap[sock])); - uvErrorCheck(uv_timer_init(CD->mUVLoop, 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(); @@ -194,10 +207,11 @@ void CCDBDownloader::closeSocketByTimer(uv_timer_t* handle) auto sock = data->socket; if (CD->mSocketTimerMap.find(sock) != CD->mSocketTimerMap.end()) { - uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[sock])); + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[sock]), SEVERE); CD->mSocketTimerMap.erase(sock); if (close(sock) == -1) { - LOG(error) << "CCDBDownloader: Socket failed to close"; + O2_SIGNPOST_ID_GENERATE(sid, ccdb_downloader); + O2_SIGNPOST_EVENT_EMIT_ERROR(ccdb_downloader, sid, "CCDBDownloader", "CCDBDownloader: Socket failed to close"); } delete data; } @@ -213,7 +227,7 @@ void CCDBDownloader::curlTimeout(uv_timer_t* handle) void CCDBDownloader::curlPerform(uv_poll_t* handle, int status, int events) { - uvErrorCheck(status); + uvErrorCheck(status, MINOR); int running_handles; int flags = 0; if (events & UV_READABLE) { @@ -252,20 +266,20 @@ int CCDBDownloader::handleSocket(CURL* easy, curl_socket_t s, int action, void* } if (CD->mExternalLoop && CD->mSocketTimerMap.find(s) != CD->mSocketTimerMap.end()) { - uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[s])); + uvErrorCheck(uv_timer_stop(CD->mSocketTimerMap[s]), SEVERE); } - uvErrorCheck(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->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)); + uvErrorCheck(uv_timer_start(CD->mSocketTimerMap[s], closeSocketByTimer, CD->mKeepaliveTimeoutMS, 0), SEVERE); } } - uvErrorCheck(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); curlMultiErrorCheck(curl_multi_assign(socketData->curlm, s, nullptr)); } @@ -325,7 +339,7 @@ CCDBDownloader::curl_context_t* CCDBDownloader::createCurlContext(curl_socket_t context->sockfd = sockfd; context->poll_handle = (uv_poll_t*)malloc(sizeof(*context->poll_handle)); - uvErrorCheck(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; @@ -348,7 +362,7 @@ 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; + 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); @@ -360,9 +374,11 @@ void CCDBDownloader::getLocalContent(PerformData* performData, std::string& newL 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"; } } @@ -382,7 +398,7 @@ std::string CCDBDownloader::getNewLocation(PerformData* performData, std::vector void CCDBDownloader::httpRedirect(PerformData* performData, std::string& newLocation, CURL* easy_handle) { auto requestData = performData->requestData; - LOG(debug) << "Trying content location " << newLocation; + LOG(debug) << "Trying content location " << newLocation << "\n"; curl_easy_setopt(easy_handle, CURLOPT_URL, newLocation.c_str()); mHandlesToBeAdded.push_back(easy_handle); } @@ -390,7 +406,7 @@ void CCDBDownloader::httpRedirect(PerformData* performData, std::string& newLoca void CCDBDownloader::followRedirect(PerformData* performData, CURL* easy_handle, std::vector& locations, bool& rescheduled, bool& contentRetrieved) { std::string newLocation = getNewLocation(performData, locations); - if (newLocation.find("alien:/", 0) != std::string::npos || newLocation.find("file:/", 0) != std::string::npos) { + while (!contentRetrieved && (newLocation.find("alien:/", 0) != std::string::npos || newLocation.find("file:/", 0) != std::string::npos)) { getLocalContent(performData, newLocation, contentRetrieved, locations); } if (!contentRetrieved && newLocation != "") { @@ -470,6 +486,10 @@ void CCDBDownloader::transferFinished(CURL* easy_handle, CURLcode curlCode) 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); @@ -490,8 +510,8 @@ void CCDBDownloader::transferFinished(CURL* easy_handle, CURLcode curlCode) std::string currentHost = requestData->hosts[performData->hostInd]; std::string loggingMessage = prepareLogMessage(currentHost, requestData->userAgent, requestData->path, requestData->timestamp, requestData->headers, httpCode); - // Get alternative locations for the same host - auto locations = getLocations(&(requestData->hoPair.header)); + // 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) { @@ -499,17 +519,30 @@ void CCDBDownloader::transferFinished(CURL* easy_handle, CURLcode curlCode) 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 < locations.size()) { - followRedirect(performData, easy_handle, locations, rescheduled, contentRetrieved); + } 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; + contentRetrieved = true; // Can be overruled by following error check } } else { LOG(error) << loggingMessage; } - // Check if content was retrieved, or scheduled to be retrieved - if (!rescheduled && !contentRetrieved && performData->locInd == locations.size()) { + // 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); @@ -579,12 +612,12 @@ int CCDBDownloader::startTimeout(CURLM* multi, long timeout_ms, void* userp) auto timeout = (uv_timer_t*)userp; if (timeout_ms < 0) { - uvErrorCheck(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 } - uvErrorCheck(uv_timer_start(timeout, curlTimeout, timeout_ms, 0)); + uvErrorCheck(uv_timer_start(timeout, curlTimeout, timeout_ms, 0), SEVERE); } return 0; } @@ -627,24 +660,37 @@ CURLcode CCDBDownloader::perform(CURL* handle) return batchBlockingPerform(handleVector).back(); } -std::vector CCDBDownloader::getLocations(std::multimap* headerMap) const +void CCDBDownloader::updateLocations(std::multimap* headerMap, std::vector* locations, int* locIndex) const { - std::vector locs; + std::vector newLocations; + auto iter = headerMap->find("Location"); if (iter != headerMap->end()) { - locs.push_back(iter->second); + 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); + } + } + } } + // 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(locs.begin(), locs.end(), it->second) == locs.end()) { - locs.push_back(it->second); + 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); + } } } } - return locs; + + // 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) diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 72db59af50b73..42bc13904bf61 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -24,6 +24,7 @@ #include "Framework/DataTakingContext.h" #include #include +#include #include #include #include @@ -39,13 +40,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include "rapidjson/document.h" #include "rapidjson/writer.h" @@ -116,13 +117,7 @@ CcdbApi::~CcdbApi() void CcdbApi::setUniqueAgentID() { - std::string host = boost::asio::ip::host_name(); - char const* jobID = getenv("ALIEN_PROC_ID"); - if (jobID) { - mUniqueAgentID = fmt::format("{}-{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6), jobID); - } else { - mUniqueAgentID = fmt::format("{}-{}-{}", host, getCurrentTimestamp() / 1000, o2::utils::Str::getRandomString(6)); - } + mUniqueAgentID = TAlienUserAgent::BasedOnEnvironment().ToString(); } bool CcdbApi::checkAlienToken() @@ -164,6 +159,10 @@ void CcdbApi::curlInit() 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://"; @@ -227,12 +226,12 @@ void CcdbApi::init(std::string const& host) if (deploymentMode == o2::framework::DeploymentMode::OnlineDDS || deploymentMode == o2::framework::DeploymentMode::OnlineAUX || deploymentMode == o2::framework::DeploymentMode::OnlineECS) { - mCurlTimeoutDownload = 5; + mCurlTimeoutDownload = 15; } else if (deploymentMode == o2::framework::DeploymentMode::Grid || deploymentMode == o2::framework::DeploymentMode::FST) { mCurlTimeoutDownload = 15; } else if (deploymentMode == o2::framework::DeploymentMode::Local) { - mCurlTimeoutDownload = 1; + mCurlTimeoutDownload = 5; } } @@ -297,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()); @@ -370,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); @@ -413,8 +416,14 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin auto mime = curl_mime_init(curl); auto field = curl_mime_addpart(mime); curl_mime_name(field, "send"); - curl_mime_filedata(field, filename.c_str()); - curl_mime_data(field, buffer, size); + 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:"; @@ -431,7 +440,7 @@ int CcdbApi::storeAsBinaryFile(const char* buffer, size_t size, const std::strin 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()); @@ -476,30 +485,30 @@ int CcdbApi::storeAsTFile(const TObject* rootObject, std::string const& path, st 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); } @@ -507,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); } @@ -656,7 +665,37 @@ size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userd const auto key = boost::algorithm::trim_copy(header.substr(0, index)); const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); LOGP(debug, "Adding #{} {} -> {}", headers->size(), key, value); - headers->insert(std::make_pair(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; } @@ -725,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); @@ -800,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 @@ -848,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; } @@ -855,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); } @@ -928,14 +970,14 @@ void* CcdbApi::extractFromLocalFile(std::string const& filename, std::type_info 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 { @@ -944,10 +986,10 @@ bool CcdbApi::initTGrid() const errorShown = true; } } - return mAlienInstance != nullptr; + return gGrid != nullptr; } -void* CcdbApi::downloadFilesystemContent(std::string const& url, std::type_info const& tinfo, std::map* headers) const +void* CcdbApi::downloadFilesystemContent(std::string const& url, std::type_info const& tinfo, std::map* headers) const { if ((url.find("alien:/", 0) != std::string::npos) && !initTGrid()) { return nullptr; @@ -986,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 @@ -1134,7 +1176,7 @@ void* CcdbApi::retrieveFromTFile(std::type_info const& tinfo, std::string const& CURL* curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); - string fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp); // todo check if function still works correctly in case mInSnapshotMode + 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) { auto res = extractFromLocalFile(fullUrl, tinfo, headers); @@ -1188,8 +1230,8 @@ std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string 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()); } @@ -1200,7 +1242,7 @@ std::string CcdbApi::list(std::string const& path, bool latestOnly, std::string 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); @@ -1260,7 +1302,7 @@ 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(); @@ -1406,7 +1448,7 @@ std::map CcdbApi::retrieveHeaders(std::string const& p auto do_remote_header_call = [this, &path, &metadata, timestamp]() -> std::map { CURL* curl = curl_easy_init(); CURLcode res = CURL_LAST; - string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); + std::string fullUrl = getFullUrlForRetrieval(curl, path, metadata, timestamp); std::map headers; if (curl != nullptr) { @@ -1448,7 +1490,7 @@ std::map CcdbApi::retrieveHeaders(std::string const& p if (!mSnapshotCachePath.empty()) { // protect this sensitive section by a multi-process named semaphore - auto semaphore_barrier = std::make_unique(mSnapshotCachePath, path); + auto semaphore_barrier = std::make_unique(mSnapshotCachePath + std::string("_headers"), path); std::string logfile = mSnapshotCachePath + "/log"; std::fstream out(logfile, ios_base::out | ios_base::app); @@ -1602,12 +1644,12 @@ int 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; -} - 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 @@ -1674,11 +1707,15 @@ void CcdbApi::scheduleDownload(RequestContext& requestContext, size_t* requestCo ho.counter++; try { 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()) { - sz = std::max(chunk.size() + realsize, (size_t)std::stol(cl->second)); + size_t sizeFromHeader = std::stol(cl->second); + sz = hsize + std::max(chunk.size() * (sizeFromHeader ? 1 : 2) + realsize, sizeFromHeader); } else { - sz = chunk.size() + realsize; + sz = hsize + std::max(chunk.size() * 2, chunk.size() + realsize); // LOGP(debug, "SIZE IS NOT IN HEADER, allocate {}", sz); } chunk.reserve(sz); @@ -1694,7 +1731,7 @@ void CcdbApi::scheduleDownload(RequestContext& requestContext, size_t* requestCo CURL* curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, mUniqueAgentID.c_str()); - string fullUrl = getFullUrlForRetrieval(curl_handle, requestContext.path, requestContext.metadata, requestContext.timestamp); + 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); @@ -1853,6 +1890,21 @@ void CcdbApi::saveSnapshot(RequestContext& requestContext) 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 +{ + 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); + } +} + void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& path, std::map const& metadata, long timestamp, std::map* headers, std::string const& etag, @@ -1871,6 +1923,25 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& p 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; + } + dest[cnt++] = 0; + }; + + 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 +} + void CcdbApi::navigateSourcesAndLoadFile(RequestContext& requestContext, int& fromSnapshot, size_t* requestCounter) const { LOGP(debug, "loadFileToMemory {} ETag=[{}]", requestContext.path, requestContext.etag); @@ -1920,20 +1991,32 @@ void CcdbApi::vectoredLoadFileToMemory(std::vector& requestConte bool CcdbApi::loadLocalContentToMemory(o2::pmr::vector& dest, std::string& url) const { if (url.find("alien:/", 0) != std::string::npos) { - loadFileToMemory(dest, url, nullptr); // headers loaded from the file in case of the snapshot reading only - return true; + 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 { + return true; + } } if ((url.find("file:/", 0) != std::string::npos)) { std::string path = url.substr(7); if (std::filesystem::exists(path)) { - loadFileToMemory(dest, path, nullptr); - return true; + 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 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; @@ -1981,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) { 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 c834f2f30f64a..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 @@ -589,4 +589,11 @@ BOOST_AUTO_TEST_CASE(vectored) for (auto context : contexts) { BOOST_CHECK(context.dest.size() != 0); } -} \ No newline at end of file +} + +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/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 d28f191021fdf..adecffc0f4dbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,6 @@ o2_build_sanity_checks() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -include(dependencies/FindONNXRuntime.cmake) - include(O2CheckCXXFeatures) o2_check_cxx_features() @@ -88,7 +86,6 @@ include(O2AddTestRootMacro) include(O2ReportNonTestedMacros) include(O2TargetRootDictionary) include(O2DataFile) -include(O2TargetManPage) include(O2AddWorkflow) include(O2SetROOTPCMDependencies) include(O2AddHipifiedExecutable) @@ -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 diff --git a/CODEOWNERS b/CODEOWNERS index 91af5306f247a..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,19 +28,19 @@ /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 @iarsene @matthiasrichter +/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 @mcoquet642 @mconcas @shahor02 +/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 @martenole @wille10 +/DataFormats/Detectors/TRD @f3sch @bazinski @wille10 /DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 /DataFormats/Detectors/ZDC @coppedis @@ -53,26 +53,27 @@ /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 @iarsene @matthiasrichter -/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 @mcoquet642 @mconcas @shahor02 +/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 @martenole @wille10 +/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 @@ -110,13 +111,13 @@ /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 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 6870b8ddd5712..9ef3b4dba5ae0 100644 --- a/Common/Constants/include/CommonConstants/MathConstants.h +++ b/Common/Constants/include/CommonConstants/MathConstants.h @@ -22,7 +22,8 @@ namespace constants { namespace math { -constexpr float Almost0 = 1.175494351e-38f; +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; diff --git a/Common/Constants/include/CommonConstants/PhysicsConstants.h b/Common/Constants/include/CommonConstants/PhysicsConstants.h index 3b00789b6d818..46aeff98d6033 100644 --- a/Common/Constants/include/CommonConstants/PhysicsConstants.h +++ b/Common/Constants/include/CommonConstants/PhysicsConstants.h @@ -31,18 +31,35 @@ namespace o2::constants::physics /// \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, @@ -53,34 +70,61 @@ enum Pdg { 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 + 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; @@ -91,19 +135,29 @@ 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 MassHyperTriton = 2.99131; -constexpr double MassHyperHydrogen4 = 3.9226; -constexpr double MassHyperHelium4 = 3.9217; +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; @@ -174,9 +228,16 @@ 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 -constexpr float LightSpeedCm2S = 299792458.e2; // C in cm/s -constexpr float LightSpeedCm2NS = LightSpeedCm2S * 1e-9; // C in cm/ns } // 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 index b848de0d2e46d..f83c44bb401db 100755 --- a/Common/Constants/include/CommonConstants/make_pdg_header.py +++ b/Common/Constants/include/CommonConstants/make_pdg_header.py @@ -12,7 +12,7 @@ # or submit itself to any jurisdiction. """! -@brief Generates the body of a C++ header with PDG codes and particle masses. +@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 """ @@ -21,9 +21,12 @@ from ctypes import c_bool from enum import Enum -import ROOT # pylint: disable=import-error +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 -name_script = os.path.basename(__file__) # Enum of PDG_t particles class PdgROOT(Enum): @@ -86,18 +89,35 @@ class PdgROOT(Enum): # 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 @@ -108,22 +128,32 @@ class Pdg(Enum): 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 = ROOT.o2.O2DatabasePDG +dbPdg = o2.O2DatabasePDG def mass(code): @@ -133,49 +163,109 @@ def mass(code): return dbPdg.Mass(code, success) -def declare_mass(pdg, type="double") -> str: +def declare_mass(pdg, mass_type="double") -> str: """Returns a C++ declaration of a particle mass constant.""" - return f"constexpr {type} Mass{pdg.name[1:]} = {mass(pdg.value)};\n" + return f"constexpr {mass_type} Mass{pdg.name[1:]} = {mass(pdg.value)};" -# Comment at the beginning of the output -str_block_begin = f"""// BEGINNING OF THE GENERATED BLOCK. -// DO NOT EDIT THIS BLOCK DIRECTLY! -// It has been generated by the {name_script} script. -// For modifications, edit the script and generate this block again. -""" -# Comment at the end of the output -str_block_end = """// END OF THE GENERATED BLOCK -""" -# Start of enum declarations of additional particles -str_enum_head = """/// \\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 { -""" -# End of enum declarations of additional particles -str_enum_foot = "};\n" -# Documentation string for mass declarations of additional particles -str_mass_o2_head = """/// \\brief Declarations of masses for additional particles -""" -# Documentation string for mass declarations of PDG_t particles -str_mass_root_head = """/// \\brief Declarations of masses for particles in ROOT PDG_t -""" +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 + -# Additional particles -str_enum = str_enum_head -str_mass_o2 = str_mass_o2_head -for c in Pdg: - str_enum += f" {c.name} = {c.value},\n" - str_mass_o2 += declare_mass(c) -str_enum = str_enum[:-2] + "\n" # Remove the last comma. -str_enum += str_enum_foot - -# PDG_t particles -str_mass_root = str_mass_root_head -for d in PdgROOT: - str_mass_root += declare_mass(d) - -# Header body -str_header = "\n".join((str_block_begin, str_enum, str_mass_o2, str_mass_root, str_block_end)) -print(str_header) +if __name__ == "__main__": + main() diff --git a/Common/DCAFitter/CMakeLists.txt b/Common/DCAFitter/CMakeLists.txt index 74d68fbf470c6..c0b2d0dca1026 100644 --- a/Common/DCAFitter/CMakeLists.txt +++ b/Common/DCAFitter/CMakeLists.txt @@ -22,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) @@ -40,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 548cb7321e104..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,11 +156,11 @@ 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 @@ -120,12 +171,12 @@ class DCAFitterN ///< 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 was done + ///< 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) @@ -133,7 +184,9 @@ class DCAFitterN 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]; } @@ -141,112 +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); + 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; } - void setCollinear(bool isCollinear) { mIsCollinear = isCollinear; } - - 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; - int getFitterID() const { return mFitterID; } - void setFitterID(int i) { mFitterID = i; } - size_t getCallID() const { return mCallID; } + 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); - bool propagateParamToX(o2::track::TrackPar& t, float x); - - 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; @@ -256,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; @@ -267,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; @@ -314,11 +389,16 @@ class DCAFitterN std::array mNIters; // number of iterations for each seed std::array mTrPropDone{}; // Flag that the tracks are fully propagated to PCA std::array mPropFailed{}; // Flag that some propagation failed for this PCA candidate - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T + 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 @@ -341,13 +421,13 @@ class DCAFitterN float mMaxStep = 2.0; // Max step for propagation with Propagator int mFitterID = 0; // locat fitter ID (mostly for debugging) size_t mCallID = 0; - ClassDefNV(DCAFitterN, 2); + 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++; @@ -358,10 +438,8 @@ int DCAFitterN::process(const Tr&... args) mTrAux[i].set(*mOrigTrPtr[i], mBz); } 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 - return 0; // no crossing - } - for (int ih = 0; ih < MAXHYP; ih++) { - mPropFailed[ih] = false; + mFitStatus[mCurHyp] = FitStatus::NoCrossing; + return 0; } if (mUseAbsDCA) { calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization @@ -379,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]; @@ -401,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]); } } } @@ -415,10 +491,11 @@ int DCAFitterN::process(const Tr&... args) //__________________________________________________________________________ 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; @@ -441,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; @@ -509,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 @@ -559,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--;) { @@ -579,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 @@ -597,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 @@ -608,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 @@ -622,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--;) { @@ -631,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]); } } } @@ -646,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]; @@ -657,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)) { @@ -667,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; @@ -682,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]; @@ -704,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++; @@ -724,14 +817,15 @@ ROOT::Math::SMatrix> DCAFitterN -void DCAFitterN::calcTrackResiduals() +GPUd() void DCAFitterN::calcTrackResiduals() { // calculate residuals Vec3D vtxLoc; @@ -745,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--;) { @@ -755,7 +849,7 @@ inline void DCAFitterN::calcTrackDerivatives() //___________________________________________________________________ template -inline double DCAFitterN::calcChi2() const +GPUdi() double DCAFitterN::calcChi2() const { // calculate current chi2 double chi2 = 0; @@ -769,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; @@ -782,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--;) { @@ -797,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]; @@ -834,7 +928,7 @@ bool DCAFitterN::propagateTracksToVertex(int icand) //___________________________________________________________________ template -inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) +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]; @@ -845,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; } @@ -864,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; + } + if (!propagateToX(mCandTr[mCurHyp][i], x)) { return false; } - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], XerrFactor); // prepare inverse cov.matrices at starting point + setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions + if (!mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], 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; } @@ -894,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; } @@ -910,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; } @@ -947,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; } @@ -963,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; } @@ -991,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]; @@ -1001,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); @@ -1040,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); @@ -1062,37 +1225,57 @@ 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) +GPUdi() bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) { bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { +#ifndef GPUCA_GPUCODE res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { 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) +GPUdi() bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) { bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { +#ifndef GPUCA_GPUCODE res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); +#endif } else { 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; } @@ -1100,6 +1283,18 @@ inline bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, floa 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 39dee1d399848..0000000000000 --- a/Common/DCAFitter/include/DCAFitter/HelixHelper.h +++ /dev/null @@ -1,302 +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, 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; - 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, isCollinear); - } 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, isCollinear); - } 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 (std::abs(xDist) < std::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 = 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, 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 + std::abs(rBSign); - float r1_r = trcA.rC / r2r; - float r2_r = std::abs(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 - 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, 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; - } - - CrossInfo() = default; - - template - 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/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 bfd671315adf6..bd00b5bed841e 100644 --- a/Common/DCAFitter/test/testDCAFitterN.cxx +++ b/Common/DCAFitter/test/testDCAFitterN.cxx @@ -134,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(); } } @@ -143,6 +143,22 @@ 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; @@ -159,13 +175,15 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) 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); @@ -196,6 +214,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -208,6 +227,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -220,30 +240,34 @@ 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) << "Processing 2-prong Helix - Helix case 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); @@ -253,6 +277,8 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) 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; @@ -274,6 +300,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -286,6 +313,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -298,30 +326,35 @@ 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 from gamma conversion"; 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 < 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 @@ -352,6 +385,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -364,6 +398,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -376,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 @@ -429,6 +469,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -441,6 +482,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -453,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); @@ -505,6 +552,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDA += minD; nfoundA++; } + ++fitstat[ft.getFitStatus()][0]; ft.setUseAbsDCA(true); ft.setWeightedFinalPCA(true); @@ -517,6 +565,7 @@ BOOST_AUTO_TEST_CASE(DCAFitterNProngs) meanDAW += minD; nfoundAW++; } + ++fitstat[ft.getFitStatus()][1]; ft.setUseAbsDCA(false); ft.setWeightedFinalPCA(false); @@ -529,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 5caad34d56dd4..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; 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 dc1d6966fbdba..d618bb8549175 100644 --- a/Common/MathUtils/CMakeLists.txt +++ b/Common/MathUtils/CMakeLists.txt @@ -24,7 +24,8 @@ o2_add_library( O2::GPUCommon ROOT::GenVector ROOT::Geom - Vc::Vc) + Vc::Vc + Boost::boost) o2_target_root_dictionary( MathUtils @@ -39,7 +40,8 @@ o2_target_root_dictionary( include/MathUtils/Primitive2D.h include/MathUtils/SMatrixGPU.h include/MathUtils/SymMatrixSolver.h - include/MathUtils/Tsallis.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 9ca6d0def4682..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" @@ -250,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 7175339db8592..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,12 +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 { @@ -67,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(); @@ -114,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]; } @@ -205,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); @@ -284,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()); } @@ -304,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; } @@ -470,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 @@ -481,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: @@ -518,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; @@ -525,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) { @@ -548,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() { @@ -676,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; @@ -1399,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 { @@ -1425,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); @@ -1468,5 +1509,5 @@ GPUdi() SMatrixGPU> Similarity(const SMatrixGPU(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/basicMath.h b/Common/MathUtils/include/MathUtils/detail/basicMath.h index 3fc3fe374b380..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 { @@ -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 462affdceb17f..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; diff --git a/Common/MathUtils/include/MathUtils/fit.h b/Common/MathUtils/include/MathUtils/fit.h index 00c39486a4ba0..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" @@ -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}; } @@ -788,6 +789,169 @@ T MAD2Sigma(int np, T* 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 b56ea7456aa0c..0b070e537afcd 100644 --- a/Common/MathUtils/src/MathUtilsLinkDef.h +++ b/Common/MathUtils/src/MathUtilsLinkDef.h @@ -43,4 +43,9 @@ #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/SimConfig/include/SimConfig/G4Params.h b/Common/SimConfig/include/SimConfig/G4Params.h index c6a5bc0882320..aa8aa05263c0a 100644 --- a/Common/SimConfig/include/SimConfig/G4Params.h +++ b/Common/SimConfig/include/SimConfig/G4Params.h @@ -22,17 +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/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index b215f22546c8e..be88d9fbd8c33 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -37,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 { @@ -83,6 +84,7 @@ struct SimConfigData { 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 @@ -118,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 @@ -177,6 +179,10 @@ class SimConfig 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; //! 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/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/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 0f798247d55c5..15879687872d5 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -24,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.")( @@ -35,7 +42,7 @@ 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("ALICE2"), + "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" @@ -69,7 +76,7 @@ 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 isUpgrade) @@ -91,7 +98,8 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules[i] != "TF3" && activeModules[i] != "RCH" && activeModules[i] != "MI3" && - activeModules[i] != "ECL") { + activeModules[i] != "ECL" && + activeModules[i] != "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a module from the upgrades.", activeModules[i]); } } @@ -105,7 +113,8 @@ void SimConfig::determineActiveModules(std::vector const& inputargs activeModules[i] == "TF3" || activeModules[i] == "RCH" || activeModules[i] == "MI3" || - activeModules[i] == "ECL") { + activeModules[i] == "ECL" || + activeModules[i] == "FD3") { LOGP(fatal, "List of active modules contains {}, which is not a run 3 module", activeModules[i]); } } @@ -123,6 +132,7 @@ void SimConfig::determineActiveModules(std::vector const& inputargs d == DetID::TF3 || d == DetID::RCH || d == DetID::ECL || + d == DetID::FD3 || d == DetID::MI3) { activeModules.emplace_back(DetID::getName(d)); } @@ -142,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 && d != DetID::TF3 && d != DetID::RCH && d != DetID::ECL && d != DetID::MI3) { + 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)); } } @@ -190,7 +200,11 @@ bool SimConfig::determineActiveModulesList(const std::string& version, std::vect return false; } modules = map[version]; - LOGP(info, "Running with official detector version '{}'", 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") { @@ -263,6 +277,21 @@ 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; @@ -326,17 +355,8 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& mConfigData.mFilterNoHitEvents = true; } mConfigData.mFromCollisionContext = vm["fromCollContext"].as(); - // 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); - } - adjustFromCollContext(collcontextfile, simprefix); + auto collcontext_simprefix = getCollContextFilenameAndEventPrefix(); + adjustFromCollContext(collcontext_simprefix.first, collcontext_simprefix.second); // analyse vertex options if (!parseVertexModeString(vm["vertexMode"].as(), mConfigData.mVertexMode)) { @@ -378,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; } @@ -474,8 +497,8 @@ void SimConfig::adjustFromCollContext(std::string const& collcontextfile, std::s // 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; } @@ -490,7 +513,7 @@ bool SimConfig::resetFromArguments(int argc, char* argv[]) 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 9c27536be5eb8..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> + ; 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 786ccc8f784fe..849a3d70f62e1 100644 --- a/Common/Utils/CMakeLists.txt +++ b/Common/Utils/CMakeLists.txt @@ -26,7 +26,7 @@ o2_add_library(CommonUtils 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 @@ -51,6 +51,15 @@ o2_target_root_dictionary(CommonUtils 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 @@ -81,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/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 717a4c425fc82..39b24bbbbd57c 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -162,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) @@ -321,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/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/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/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/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index 7a2edbf3b2f53..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 { @@ -146,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/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 b6e0e82229c16..8497a485fca39 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -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) @@ -204,12 +228,42 @@ void ConfigurableParam::setValue(std::string const& key, std::string const& valu initialize(); } 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 { 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 + 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) { @@ -447,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; diff --git a/Common/Utils/src/ConfigurableParamHelper.cxx b/Common/Utils/src/ConfigurableParamHelper.cxx index 0fb213b722e26..161735b3a5ce4 100644 --- a/Common/Utils/src/ConfigurableParamHelper.cxx +++ b/Common/Utils/src/ConfigurableParamHelper.cxx @@ -26,6 +26,7 @@ #include #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/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/FileFetcher.cxx b/Common/Utils/src/FileFetcher.cxx index 41265764246da..32c51ac704d4b 100644 --- a/Common/Utils/src/FileFetcher.cxx +++ b/Common/Utils/src/FileFetcher.cxx @@ -329,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"; @@ -339,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/IRFrameSelector.cxx b/Common/Utils/src/IRFrameSelector.cxx index 8122484659f45..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 = {}; } diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 03bf68df5a41c..687225d069ed2 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -12,7 +12,10 @@ #include "CommonUtils/StringUtils.h" #include #include +#ifndef GPUCA_STANDALONE #include +#include +#endif #include using namespace o2::utils; @@ -34,6 +37,19 @@ 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) { @@ -64,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); @@ -91,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/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/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/DataFormats/Detectors/CTP/CMakeLists.txt b/DataFormats/Detectors/CTP/CMakeLists.txt index 2a8ccbef3dee8..37c5af7b07b6e 100644 --- a/DataFormats/Detectors/CTP/CMakeLists.txt +++ b/DataFormats/Detectors/CTP/CMakeLists.txt @@ -8,15 +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. - o2_add_library(DataFormatsCTP SOURCES src/Digits.cxx src/Configuration.cxx src/Scalers.cxx src/CTF.cxx src/TriggerOffsetsParam.cxx - src/RunManager.cxx src/LumiInfo.cxx + src/CTPRateFetcher.cxx PUBLIC_LINK_LIBRARIES O2::CommonDataFormat O2::Headers O2::CommonUtils @@ -29,6 +28,6 @@ o2_target_root_dictionary(DataFormatsCTP include/DataFormatsCTP/Configuration.h include/DataFormatsCTP/Scalers.h include/DataFormatsCTP/LumiInfo.h - include/DataFormatsCTP/TriggerOffsetsParam.h - include/DataFormatsCTP/RunManager.h) + include/DataFormatsCTP/CTPRateFetcher.h + include/DataFormatsCTP/TriggerOffsetsParam.h) 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 b9378d51872c3..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -160,6 +160,9 @@ class CTPConfiguration 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; @@ -170,6 +173,8 @@ class CTPConfiguration 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; @@ -200,6 +205,18 @@ class CTPConfiguration 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/RunManager.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/RunManager.h deleted file mode 100644 index 2df164474e4c4..0000000000000 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/RunManager.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 RunManager.h -/// \brief Managing runs for config and scalers -/// \author Roman Lietava -#ifndef _CTP_RUNMANAGER_H_ -#define _CTP_RUNMANAGER_H_ -#include "DataFormatsCTP/Configuration.h" -namespace o2 -{ -namespace ctp -{ -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, bool& ok); - 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); -}; -} // namespace ctp -} // namespace o2 -#endif //_CTP_RUNMANAGER_H_ diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h index a9a831a07635e..45d54b034f8d9 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h @@ -87,13 +87,21 @@ struct CTPScalerRecordO2 { 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; }; @@ -106,7 +114,6 @@ 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); @@ -115,23 +122,31 @@ class CTPRunScalers // 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 NCOUNTERSv2 = 1071; - static constexpr uint32_t NCOUNTERS = 1085; - 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 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 { diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h index f931e9eaa8360..063336e5461ce 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h @@ -24,9 +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 a26feae5d7f85..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -510,8 +510,8 @@ int CTPConfiguration::processConfigurationLineRun3(std::string& line, int& level } int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& level, std::map>& descInputsIndex) { - LOG(info) << "Processing line"; - LOG(info) << "line:" << line << " lev:" << level; + LOG(debug) << "Processing line"; + LOG(debug) << "line:" << line << " lev:" << level; // std::vector tokens = o2::utils::Str::tokenize(line, ' '); size_t ntokens = tokens.size(); @@ -557,7 +557,7 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev level = UNKNOWN; } } - LOG(info) << "Level:" << level; + LOG(debug) << "Level:" << level; switch (level) { case VERSION: { break; @@ -585,7 +585,7 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev uint32_t index = std::stoul(tokens[2]); ctpinp.inputMask = (1ull << (index - 1)); mInputs.push_back(ctpinp); - LOG(info) << "Input:" << ctpinp.name << " index:" << index; + LOG(debug) << "Input:" << ctpinp.name << " index:" << index; break; } case MASKS: { @@ -596,7 +596,7 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev } bcmask.setBCmask(tokens); mBCMasks.push_back(bcmask); - LOG(info) << "BC mask added:" << bcmask.name; + LOG(debug) << "BC mask added:" << bcmask.name; break; } case GENS: { @@ -604,13 +604,20 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev gen.name = tokens[0]; gen.frequency = tokens[1]; mGenerators.push_back(gen); - LOG(info) << "Gen added:" << line; + LOG(debug) << "Gen added:" << line; break; } case DESCRIPTORS: { - if ((tokens.size() < 2) && (line.find("DTRUE") == std::string::npos)) { - LOG(warning) << "Dsecriptor:" << line; - break; + 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]; @@ -630,9 +637,9 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev o2::detectors::DetID det(detname.c_str()); if (isDetector(det)) { ctpdet.detID = det.getID(); - LOG(info) << "Detector found:" << det.getID() << " " << detname; + LOG(debug) << "Detector found:" << det.getID() << " " << detname; } else { - LOG(info) << "Unknown detectors:" << line; + LOG(error) << "Unknown detectors:" << line; } mDetectors.push_back(ctpdet); level = LTGitems; @@ -642,7 +649,7 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev if (ntokens == 1) { mDetectors.back().mode = tokens[0]; } - LOG(info) << "LTGitem:" << line; + LOG(debug) << "LTGitem:" << line; break; } case CLUSTER: { @@ -650,10 +657,10 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev try { cluster.hwMask = std::stoull(tokens[0]); } catch (...) { - LOG(info) << "Cluster syntax error:" << line; + LOG(error) << "Cluster syntax error:" << line; return level; } - LOG(info) << "Cluster:" << line; + LOG(debug) << "Cluster:" << line; cluster.name = tokens[2]; o2::detectors::DetID::mask_t mask; for (uint32_t item = 3; item < ntokens; item++) { @@ -680,10 +687,10 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev try { index = std::stoull(tokens[1]); } catch (...) { - LOG(info) << "Class syntax error:" << line; + LOG(error) << "Class syntax error:" << line; return level; } - LOG(info) << "Class:" << line; + LOG(debug) << "Class:" << line; CTPClass cls; cls.classMask = 1ull << index; cls.name = tokens[0]; @@ -716,7 +723,7 @@ int CTPConfiguration::processConfigurationLineRun3v2(std::string& line, int& lev break; } default: { - LOG(info) << "unknown line:" << line; + LOG(warning) << "unknown line:" << line; } } return 0; @@ -773,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) { @@ -877,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(); @@ -1124,8 +1187,64 @@ int CTPInputsConfiguration::getInputIndexFromName(std::string& name) 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 ecab7df8827d3..ac2a83d31edda 100644 --- a/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h +++ b/DataFormats/Detectors/CTP/src/DataFormatsCTPLinkDef.h @@ -44,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 + ; @@ -55,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/RunManager.cxx b/DataFormats/Detectors/CTP/src/RunManager.cxx deleted file mode 100644 index a41af579d84e6..0000000000000 --- a/DataFormats/Detectors/CTP/src/RunManager.cxx +++ /dev/null @@ -1,445 +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 RunManager.cxx -/// \author Roman Lietava - -#include "DataFormatsCTP/Configuration.h" -#include "DataFormatsCTP/RunManager.h" -#include "CCDB/CcdbApi.h" -#include "CCDB/BasicCCDBManager.h" -#include -#include -#include "CommonUtils/StringUtils.h" -#include -using namespace o2::ctp; -std::string CTPRunManager::mCCDBHost = "http://o2-ccdb.internal"; -/// -/// Run Manager to manage Config and Scalers -/// -void CTPRunManager::init() -{ - for (uint32_t i = 0; i < NRUNS; i++) { - mActiveRuns[i] = nullptr; - } - loadScalerNames(); - LOG(info) << "CCDB host:" << mCCDBHost; - LOG(info) << "CTP vNew:" << mNew; - 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) << "ctpcfg: 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); - 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; - } - } - */ - 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(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("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("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(); - } - 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; - } - static int nerror = 0; - if (topic == "rocnts") { - if (nerror < 1) { - LOG(warning) << "Skipping topic rocnts"; - 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(info) << "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(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); - } - } - 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, bool& ok) -{ - 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; - ok = 0; - } else { - // ctpconfigdb->printStream(std::cout); - LOG(info) << "CTP config found. Run:" << run; - ok = 1; - } - return *ctpconfigdb; -} -CTPConfiguration CTPRunManager::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 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 (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/DataFormats/Detectors/CTP/src/Scalers.cxx b/DataFormats/Detectors/CTP/src/Scalers.cxx index 8e5b608df2180..256722fc1e5ae 100644 --- a/DataFormats/Detectors/CTP/src/Scalers.cxx +++ b/DataFormats/Detectors/CTP/src/Scalers.cxx @@ -308,6 +308,11 @@ int CTPRunScalers::convertRawToO2() 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; @@ -396,7 +401,7 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // LMB >= LMA >= L0B >= L0A >= L1B >= L1A: 5 relations // broken for classes started at L0 // - int64_t difThres = 2; + int64_t difThres = 6; int64_t dif = (scal1.lmAfter - scal0.lmAfter) - (scal1.lmBefore - scal0.lmBefore); if (dif <= difThres) { eCnts.lmBlmAd1++; @@ -432,12 +437,14 @@ int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& // ret++; } 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:" << dif; + // LOG(error) << "L1B > L0A Before error:" << dif << " " << scal1.l1Before << " " << scal1.l0After << " " << scal0.l1Before << " " << scal0.l0After; + LOG(warning) << "L1B > L0A Before error:" << dif; } ret++; } @@ -466,10 +473,11 @@ int CTPRunScalers::updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScal } int CTPRunScalers::checkConsistency(const CTPScalerRecordO2& rec0, const CTPScalerRecordO2& rec1, errorCounters& eCnts) const { + int ret = 0; for (uint32_t i = 0; i < rec0.scalers.size(); i++) { - checkConsistency(rec0.scalers[i], rec1.scalers[i], eCnts); + 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 { @@ -649,12 +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 // type - 7 : inputs // type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a -std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type) const +std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -666,46 +744,59 @@ std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, // 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 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 + 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) { - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; switch (type) { case 1: - return (s1->lmBefore - s0->lmBefore) / timedelta; + return (s1.lmBefore - s0.lmBefore) / timedelta; case 2: - return (s1->lmAfter - s0->lmAfter) / timedelta; + return (s1.lmAfter - s0.lmAfter) / timedelta; case 3: - return (s1->l0Before - s0->l0Before) / timedelta; + return (s1.l0Before - s0.l0Before) / timedelta; case 4: - return (s1->l0After - s0->l0After) / timedelta; + return (s1.l0After - s0.l0After) / timedelta; case 5: - return (s1->l1Before - s0->l1Before) / timedelta; + return (s1.l1Before - s0.l1Before) / timedelta; case 6: - return (s1->l1After - s0->l1After) / timedelta; + return (s1.l1After - s0.l1After) / timedelta; default: LOG(error) << "Wrong type:" << type; return -1; // wrong type } } else if (type == 7) { - auto s0 = &(prev->scalersInps[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalersInps[classindex]); + 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)); } @@ -715,7 +806,7 @@ std::pair CTPRunScalers::getRate(uint32_t orbit, int classindex, // rate in Hz at a certain orbit number within the run // type - 7 : inputs // type - 1..6 : lmb,lma,l0b,l0a,l1b,l1a -std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type) const +std::pair CTPRunScalers::getRateGivenT(double timestamp, int classindex, int type, bool qc) const { if (mScalerRecordO2.size() <= 1) { LOG(error) << "not enough data"; @@ -730,49 +821,61 @@ std::pair CTPRunScalers::getRateGivenT(double timestamp, int cla // 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 = iter - mScalerRecordO2.begin(); + 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 + 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) { - auto s0 = &(prev->scalers[classindex]); // type CTPScalerO2* - auto s1 = &(next->scalers[classindex]); + const auto& s0 = sprev.scalers[classindex]; // type CTPScalerO2* + const auto& s1 = snext.scalers[classindex]; switch (type) { case 1: - return (s1->lmBefore - s0->lmBefore) / timedelta; + return (s1.lmBefore - s0.lmBefore) / timedelta; case 2: - return (s1->lmAfter - s0->lmAfter) / timedelta; + return (s1.lmAfter - s0.lmAfter) / timedelta; case 3: - return (s1->l0Before - s0->l0Before) / timedelta; + return (s1.l0Before - s0.l0Before) / timedelta; case 4: - return (s1->l0After - s0->l0After) / timedelta; + return (s1.l0After - s0.l0After) / timedelta; case 5: - return (s1->l1Before - s0->l1Before) / timedelta; + return (s1.l1Before - s0.l1Before) / timedelta; case 6: - return (s1->l1After - s0->l1After) / timedelta; + 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 = prev->scalersInps[classindex]; // type CTPScalerO2* - auto s1 = next->scalersInps[classindex]; + 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 " << (long)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.); 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 a2767c7620cdd..2d2383783cfc3 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/DetID.h @@ -87,7 +87,8 @@ class DetID static constexpr ID RCH = 23; static constexpr ID MI3 = 24; static constexpr ID ECL = 25; - static constexpr ID Last = ECL; + static constexpr ID FD3 = 26; + static constexpr ID Last = FD3; #else static constexpr ID Last = FOC; ///< if extra detectors added, update this !!! #endif @@ -181,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", "TF3", "RCH", "MI3", "ECL", 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 @@ -195,7 +196,7 @@ class DetID #ifdef ENABLE_UPGRADES , o2h::gDataOriginIT3, o2h::gDataOriginTRK, o2h::gDataOriginFT3, o2h::gDataOriginFCT, o2h::gDataOriginTF3, - o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL + o2h::gDataOriginRCH, o2h::gDataOriginMI3, o2h::gDataOriginECL, o2h::gDataOriginFD3 #endif }; #endif // GPUCA_GPUCODE_DEVICE @@ -211,10 +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::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::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 6fb8825f7c395..ba6b853f7fb23 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h @@ -468,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(); } @@ -899,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; } } @@ -929,9 +932,11 @@ CTFIOSize EncodedBlocks::decode(D_IT dest, // it 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); + constexpr size_t word_size = sizeof(W); + if (ansVersion == ANSVersionCompat) { if (!block.getNStored()) { - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; } if (md.opt == Metadata::OptStore::EENCODE) { return decodeCompatImpl(dest, slot, decoderExt); @@ -943,7 +948,7 @@ CTFIOSize EncodedBlocks::decode(D_IT dest, // it return decodeUnpackImpl(dest, slot); } if (!block.getNStored()) { - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * word_size}; } if (md.opt == Metadata::OptStore::EENCODE) { return decodeRansV1Impl(dest, slot, decoderExt); @@ -991,7 +996,7 @@ CTFIOSize EncodedBlocks::decodeCompatImpl(dst_IT dstBegin, int slot, co } else { getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, NDecoderStreams); } - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; }; template @@ -1045,7 +1050,7 @@ CTFIOSize EncodedBlocks::decodeRansV1Impl(dst_IT dstBegin, int slot, co } else { getDecoder().process(block.getData() + block.getNData(), dstBegin, md.messageLength, md.nStreams); } - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; }; template @@ -1079,7 +1084,7 @@ CTFIOSize EncodedBlocks::decodeUnpackImpl(dst_IT dest, int slot) const } else { rans::unpack(srcIt, messageLength, dest, packingWidth, offset); } - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; }; template @@ -1098,7 +1103,7 @@ CTFIOSize EncodedBlocks::decodeCopyImpl(dst_IT dest, int slot) const destPtr_t srcEnd = srcBegin + md.messageLength * sizeof(dest_t); std::copy(srcBegin, srcEnd, dest); - return {0, md.getUncompressedSize(), md.getCompressedSize()}; + return {0, md.getUncompressedSize(), md.getCompressedSize() * sizeof(W)}; }; ///_____________________________________________________________________________ @@ -1268,7 +1273,7 @@ o2::ctf::CTFIOSize EncodedBlocks::entropyCodeRANSCompat(const input_IT dataSize, nLiteralWords); - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; } template @@ -1349,7 +1354,7 @@ CTFIOSize EncodedBlocks::encodeRANSV1External(const input_IT srcBegin, dataSize, literalsSize); - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; }; template @@ -1458,7 +1463,7 @@ CTFIOSize EncodedBlocks::encodeRANSV1Inplace(const input_IT srcBegin, c dataSize, literalsSize); - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; }; // namespace ctf template @@ -1491,7 +1496,7 @@ o2::ctf::CTFIOSize EncodedBlocks::pack(const input_IT srcBegin, const i } LOGP(debug, "StoreData {} bytes, offs: {}:{}", packedSize * sizeof(storageBuffer_t), thisBlock->getOffsData(), thisBlock->getOffsData() + packedSize * sizeof(storageBuffer_t)); - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; }; template @@ -1513,7 +1518,7 @@ o2::ctf::CTFIOSize EncodedBlocks::store(const input_IT srcBegin, const *thisMetadata = detail::makeMetadataStore(messageLength, opt, nBufferElems); - return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize()}; + return {0, thisMetadata->getUncompressedSize(), thisMetadata->getCompressedSize() * sizeof(W)}; }; /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h index abf7561eb25a9..975522767dce1 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/Metadata.h @@ -37,7 +37,7 @@ struct Metadata { 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; // how many Bytes is the rANS encoder emmiting during a stream-out step. + 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. @@ -48,8 +48,21 @@ struct Metadata { 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; } - size_t getCompressedSize() const { return (nDictWords + nDataWords + nLiteralWords) * streamSize; } + + /** + * @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; diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h index 8f9cbcfbdba43..37c4b790d181b 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/SimTraits.h @@ -99,7 +99,8 @@ class SimTraits /*TF3*/ VS{ "TF3Hit" }, /*RCH*/ VS{ "RCHHit" }, /*MI3*/ VS{ "MI3Hit" }, - /*ECL*/ VS{ "ECLHit" } + /*ECL*/ VS{ "ECLHit" }, + /*FD */ VS{ "FDHit" } #endif }; // clang-format on 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/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 #include -#include "Rtypes.h" namespace o2 { @@ -35,10 +35,15 @@ class CellLabel public: // CellLabel() = default; - /// \brief Constructor + /// \brief Constructor using std::vector by moving NOT copying /// \param labels list of mc labels /// \param amplitudeFractions list of amplitude fractions - CellLabel(const gsl::span labels, const gsl::span amplitudeFractions); + 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; @@ -52,13 +57,22 @@ class CellLabel /// \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: - gsl::span mLabels; ///< List of MC particles that generated the cluster, ordered in deposited energy. - gsl::span mAmplitudeFraction; ///< List of the fraction of the cell energy coming from a MC particle. Index aligns with mLabels! + 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 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/src/CellLabel.cxx b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx index 8dde7ea90c435..70a1a642c5449 100644 --- a/DataFormats/Detectors/EMCAL/src/CellLabel.cxx +++ b/DataFormats/Detectors/EMCAL/src/CellLabel.cxx @@ -12,12 +12,39 @@ /// \file CellLabel.cxx #include "DataFormatsEMCAL/CellLabel.h" +#include "fairlogger/Logger.h" +#include +#include +#include +#include +#include using namespace o2::emcal; -CellLabel::CellLabel(const gsl::span labels, const gsl::span amplitudeFractions) : mLabels(labels), mAmplitudeFraction(amplitudeFractions) +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 index 2d3abd09d0b04..9ad1f9be5459f 100644 --- a/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx +++ b/DataFormats/Detectors/EMCAL/src/ClusterLabel.cxx @@ -71,5 +71,5 @@ 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; }); + [](const labelWithE& a, const labelWithE& b) { return a.energyFraction > b.energyFraction; }); } 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/FIT/FDD/CMakeLists.txt b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt index b8f002591c6f3..140ba1165bff8 100644 --- a/DataFormats/Detectors/FIT/FDD/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FDD/CMakeLists.txt @@ -11,12 +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 @@ -28,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/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/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 d7d6615453661..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -26,6 +26,7 @@ o2_add_library(DataFormatsFT0 O2::CommonDataFormat O2::Headers O2::CCDB + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFT0 @@ -46,4 +47,5 @@ o2_target_root_dictionary(DataFormatsFT0 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/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/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/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/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 6849ad5927392..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -55,4 +55,7 @@ #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/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/FV0/CMakeLists.txt b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt index bc21c382ff446..35bc653a8234e 100644 --- a/DataFormats/Detectors/FIT/FV0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FV0/CMakeLists.txt @@ -22,6 +22,7 @@ o2_add_library(DataFormatsFV0 O2::SimulationDataFormat O2::CommonDataFormat Microsoft.GSL::GSL + O2::DetectorsCommonDataFormats ) o2_target_root_dictionary(DataFormatsFV0 @@ -34,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/RecPoints.h b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h index d7ee2e67613fc..b3527fdd049d2 100644 --- a/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h +++ b/DataFormats/Detectors/FIT/FV0/include/DataFormatsFV0/RecPoints.h @@ -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/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 85fe4c76d2faa..61dbcabc7f087 100644 --- a/DataFormats/Detectors/FIT/common/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/common/CMakeLists.txt @@ -13,6 +13,7 @@ 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) @@ -20,4 +21,6 @@ o2_add_library(DataFormatsFIT 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 64ae3dc9653d0..aa4bb1fba8d41 100644 --- a/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h +++ b/DataFormats/Detectors/FIT/common/include/DataFormatsFIT/LookUpTable.h @@ -15,8 +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 @@ -158,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: @@ -174,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 @@ -243,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; @@ -419,6 +413,7 @@ 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 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/GlobalTracking/CMakeLists.txt b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt index 631c026f3abc7..b219de73f5b47 100644 --- a/DataFormats/Detectors/GlobalTracking/CMakeLists.txt +++ b/DataFormats/Detectors/GlobalTracking/CMakeLists.txt @@ -36,7 +36,7 @@ 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 d128467168c92..31d531ef19265 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h @@ -225,6 +225,7 @@ 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); @@ -377,6 +378,7 @@ struct RecoContainer { 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, 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); diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index e634395f6d723..dd206ffe3b70d 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -123,7 +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}); - addInput({"clusTPCoccmap", "TPC", "TPCOCCUPANCYMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"trackTPCMCTR", "TPC", "TRACKSMCLBL", 0, Lifetime::Timeframe}); @@ -267,6 +267,12 @@ 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}); @@ -275,7 +281,7 @@ void DataRequest::requestTPCClusters(bool mc) } if (requestMap.find("trackTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); - addInput({"clusTPCoccmap", "TPC", "TPCOCCUPANCYMAP", 0, Lifetime::Timeframe}); + requestTPCOccMap(); } if (mc) { addInput({"clusTPCMC", ConcreteDataTypeMatcher{"TPC", "CLNATIVEMCLBL"}, Lifetime::Timeframe}); @@ -704,10 +710,17 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques 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()) { auto tracksON = reqMap.find("trackTPC") != reqMap.end(); - addTPCClusters(pc, req->second, tracksON, tracksON); + addTPCClusters(pc, req->second, tracksON, tracksON && (!TPCOccDone)); } req = reqMap.find("trigTPC"); @@ -833,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); } } @@ -856,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); } } @@ -1104,6 +1113,12 @@ void RecoContainer::addMFTClusters(ProcessingContext& pc, bool mc) } } +//__________________________________________________________ +void RecoContainer::addTPCOccMap(ProcessingContext& pc) +{ + occupancyMapTPC = pc.inputs().get>("clusTPCoccmap"); +} + //__________________________________________________________ void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap, bool occmap) { @@ -1425,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}; @@ -1451,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; } //________________________________________________________ @@ -1532,8 +1546,6 @@ const o2::dataformats::MCTruthContainer* RecoContainer::getE void RecoContainer::getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr) const { const auto& match = getITSTPCTRDTOFMatches()[gid]; - auto gidx = match.getTrackRef(); // this should be corresponding ITS-TPC-TRD track - // const auto& tofCl = getTOFClusters()[match.getTOFClIndex()]; 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; } @@ -1542,8 +1554,6 @@ void RecoContainer::getTrackTimeITSTPCTRDTOF(GTrackID gid, float& t, float& tErr void RecoContainer::getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) const { const auto& match = getTPCTRDTOFMatches()[gid]; - auto gidx = match.getTrackRef(); // this should be corresponding ITS-TPC-TRD track - // const auto& tofCl = getTOFClusters()[match.getTOFClIndex()]; 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; } @@ -1552,8 +1562,6 @@ void RecoContainer::getTrackTimeTPCTRDTOF(GTrackID gid, float& t, float& tErr) c void RecoContainer::getTrackTimeITSTPCTOF(GTrackID gid, float& t, float& tErr) const { const auto& match = getITSTPCTOFMatches()[gid]; - auto gidx = match.getTrackRef(); // this should be corresponding ITS-TPC track - // const auto& tofCl = getTOFClusters()[match.getTOFClIndex()]; 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; } @@ -1579,7 +1587,7 @@ void RecoContainer::getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) c //________________________________________________________ void RecoContainer::getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) const { - const auto trigTPCTRD = getITSTPCTRDTriggers(); + const auto trigTPCTRD = getTPCTRDTriggers(); // very slow: find the trigger this track belongs to for (const auto& trig : trigTPCTRD) { if (trig.getTrackRefs().getEntriesBound() > gid.getIndex()) { diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index a6772751e04b5..06d4fba51bd54 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -101,8 +101,9 @@ class TrackITS : public o2::track::TrackParCov GPUhdi() void setPattern(uint32_t p) { mPattern = p; } GPUhdi() uint32_t getPattern() const { return mPattern; } - bool hasHitOnLayer(int i) const { return mPattern & (0x1 << i); } - bool isFakeOnLayer(int i) const { return !(mPattern & (0x1 << (16 + i))); } + 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)}; @@ -119,7 +120,7 @@ class TrackITS : public o2::track::TrackParCov } return s; } - int getNFakeClusters(); + int getNFakeClusters() const; void setNextROFbit(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kNextROF) : (mClusterSizes & ~kNextROF); } bool hasHitInNextROF() const { return mClusterSizes & kNextROF; } @@ -169,14 +170,14 @@ class TrackITSExt : public TrackITS using TrackITS::TrackITS; // inherit base constructors GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, - o2::track::TrackParCov&& outer, o2::gpu::gpustd::array cls) + o2::track::TrackParCov&& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, - o2::track::TrackParCov& outer, o2::gpu::gpustd::array cls) + o2::track::TrackParCov& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); @@ -204,13 +205,13 @@ class TrackITSExt : public TrackITS mIndex[layer] = idx; } - GPUh() o2::gpu::gpustd::array& getClusterIndexes() + GPUh() std::array& getClusterIndexes() { return mIndex; } private: - o2::gpu::gpustd::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters + std::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters ClassDefNV(TrackITSExt, 2); }; } // namespace its diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx index 61080ba7261a1..0244756647120 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TrackITS.cxx @@ -107,15 +107,15 @@ bool TrackITS::isBetter(const TrackITS& best, float maxChi2) const 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/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 62fc09ffcad00..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,10 +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/TimeDeadMap.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/CompCluster.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h index 18acc82e72239..361544798dc80 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CompCluster.h @@ -88,6 +88,7 @@ class CompCluster } void print() const; + std::string asString() const; ClassDefNV(CompCluster, 2); }; @@ -97,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) @@ -116,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 ba98bf2e1e101..25b7f451b6452 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/NoiseMap.h @@ -169,7 +169,6 @@ 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; } @@ -203,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 f5af1deeedd85..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; } @@ -91,8 +101,8 @@ class ROFRecord 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) diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h index a0b214f705d7c..6c7c01dc888b7 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TimeDeadMap.h @@ -14,11 +14,12 @@ #ifndef ALICEO2_ITSMFT_TIMEDEADMAP_H #define ALICEO2_ITSMFT_TIMEDEADMAP_H -#include "Rtypes.h" -#include "DetectorsCommonDataFormats/DetID.h" -#include -#include +#include + +#include #include +#include +#include namespace o2 { @@ -26,6 +27,8 @@ namespace o2 namespace itsmft { +class NoiseMap; + class TimeDeadMap { public: @@ -56,96 +59,17 @@ class TimeDeadMap mStaticDeadMap.clear(); } - void decodeMap(o2::itsmft::NoiseMap& noisemap) - { // 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 decodeMap(unsigned long orbit, o2::itsmft::NoiseMap& noisemap, bool includeStaticMap = true, long orbitGapAllowed = 330000) - { // 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); - } - } - } - }; - + 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() - { - std::vector keys; - std::transform(mEvolvingDeadMap.begin(), mEvolvingDeadMap.end(), std::back_inserter(keys), - [](const auto& O) { return O.first; }); - return keys; - } - - void getStaticMap(std::vector& mmap) { mmap = mStaticDeadMap; }; - - long getMapAtOrbit(unsigned long orbit, std::vector& mmap) - { // 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; - } - } - + 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() { return mIsDefaultObject; }; + bool isDefault() const { return mIsDefaultObject; }; void setAsDefault(bool isdef = true) { mIsDefaultObject = isdef; }; private: 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/ROFRecord.cxx b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx index 83b46f8798fc9..8dbde0d580efc 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/ROFRecord.cxx @@ -9,20 +9,22 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "DataFormatsITSMFT/ROFRecord.h" #include -#include "fmt/format.h" +#include + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "Framework/Logger.h" using namespace o2::itsmft; std::string ROFRecord::asString() const { - return fmt::format("ROF: {} | {} entries starting from {}", mROFrame, getNEntries(), getFirstEntry()); + return std::format("ROF: {} | {} entries starting from {} | IR: {}", mROFrame, getNEntries(), getFirstEntry(), mBCData.asString()); } void ROFRecord::print() const { - std::cout << this << "\n\t" << mBCData << std::endl; + LOG(info) << asString(); } std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) @@ -33,12 +35,12 @@ std::ostream& operator<<(std::ostream& stream, ROFRecord const& rec) std::string MC2ROFRecord::asString() const { - return fmt::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); + return std::format("MCEventID: {} ROFs: {}-{} Entry in ROFRecords: {}", eventRecordID, minROF, maxROF, rofRecordID); } void MC2ROFRecord::print() const { - std::cout << this << std::endl; + LOG(info) << asString(); } std::ostream& operator<<(std::ostream& stream, MC2ROFRecord const& rec) 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/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/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/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/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index b2f9eb9e53e85..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 @@ -63,7 +65,8 @@ o2_target_root_dictionary( include/DataFormatsTPC/VDriftCorrFact.h include/DataFormatsTPC/CalibdEdxCorrection.h include/DataFormatsTPC/BetheBlochAleph.h - include/DataFormatsTPC/PIDResponse.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 e8fe7457f3091..28b224298f36f 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/BetheBlochAleph.h @@ -12,27 +12,17 @@ #ifndef AliceO2_TPC_BETHEBLOCH_H_ #define AliceO2_TPC_BETHEBLOCH_H_ -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" +#include "MathUtils/BetheBlochAleph.h" -namespace o2 -{ -namespace tpc +namespace o2::tpc { 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; + 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/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index ace488b652559..024d6189593e9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -49,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 { @@ -91,8 +91,8 @@ 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; @@ -109,6 +109,15 @@ class CalibdEdxCorrection /// 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 a996f59f51f9e..f3070d456afb1 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ClusterNative.h @@ -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); 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 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); } @@ -169,6 +186,45 @@ const T getAverageValueForTime(const std::vector>& dpVec 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; @@ -210,6 +266,12 @@ 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 @@ -262,6 +324,9 @@ struct Temperature { doAppend(raw, other.raw); } + private: + bool makeFit(TLinearFitter& fitter, const int nDim, std::vector& xVals, std::vector& temperatures); + ClassDefNV(Temperature, 1); }; @@ -270,8 +335,7 @@ struct Temperature { /// struct HV { - HV() - noexcept; + HV() noexcept; // Exmple strings // TPC_HV_A03_I_G1B_I @@ -483,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{}; ///(static_cast(a) & static_cast(b)); } -inline PadFlags operator~(PadFlags a) { return static_cast(~static_cast(a)); } -inline PadFlags operator|(PadFlags a, PadFlags b) { return static_cast(static_cast(a) | static_cast(b)); } - // default point definitions for PointND, PointNDlocal, PointNDglobal are in // MathUtils/CartesianND.h diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h index 2b816569e2fbe..7806e8b210aed 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/Digit.h @@ -36,7 +36,7 @@ class Digit : public DigitBase { public: /// Default constructor - GPUdDefault() Digit() CON_DEFAULT; + GPUdDefault() Digit() = default; /// Constructor, initializing values for position, charge, time and common mode /// \param cru CRU of the Digit @@ -46,7 +46,7 @@ class Digit : public DigitBase GPUdi() Digit(int cru, float charge, int row, int pad, int time); /// Destructor - GPUdDefault() ~Digit() CON_DEFAULT; + GPUdDefault() ~Digit() = default; /// Get the accumulated charged of the Digit in ADC counts. /// The conversion is such that the decimals are simply stripped diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h index e410cd00dd3f6..e5e9b41229d50 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h @@ -42,6 +42,7 @@ struct LtrCalibData { 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 bool isValid() const { @@ -138,7 +139,7 @@ struct LtrCalibData { dEdx.clear(); } - ClassDefNV(LtrCalibData, 4); + ClassDefNV(LtrCalibData, 5); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h index e0c293b8afd8f..277011a260631 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/PIDResponse.h @@ -39,10 +39,10 @@ class PIDResponse { public: /// default constructor - PIDResponse() CON_DEFAULT; + PIDResponse() = default; /// default destructor - ~PIDResponse() CON_DEFAULT; + ~PIDResponse() = default; /// setters GPUd() void setBetheBlochParams(const float betheBlochParams[5]); @@ -65,9 +65,7 @@ class PIDResponse float mMIP = 50.f; float mChargeFactor = 2.299999952316284f; -#ifndef GPUCA_ALIROOT_LIB ClassDefNV(PIDResponse, 1); -#endif }; GPUd() void PIDResponse::setBetheBlochParams(const float betheBlochParams[5]) 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/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/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index 28a44f15cf18c..152feacb41937 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -15,6 +15,7 @@ #include // o2 includes +#include "Framework/Logger.h" #include "DataFormatsTPC/Defs.h" #include "CommonUtils/TreeStreamRedirector.h" @@ -36,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()); } } @@ -135,3 +148,36 @@ float CalibdEdxCorrection::getMeanParam(const GEMstack stack, ChargeType charge, 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::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/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h index f90101e7a4f21..b4e64db094a5c 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -33,12 +33,52 @@ class CalGain void setMPVdEdx(int iDet, float mpv) { mMPVdEdx[iDet] = mpv; } - float getMPVdEdx(int iDet) const { return mMPVdEdx[iDet]; } + 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, 1); + ClassDefNV(CalGain, 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 7a650cf3699cf..9a4da1024e251 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -75,7 +75,11 @@ 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 diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h index 28ec6c76f4bef..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,9 +74,11 @@ 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); } diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h index b8873a5247d03..fc46ca0207993 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h @@ -61,6 +61,66 @@ class PHData 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/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/Tracklet64.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h index f0af50a3c5a64..e63d8fbb5f277 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Tracklet64.h @@ -144,7 +144,7 @@ class Tracklet64 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 GPUCA_NAMESPACE::gpu::CAMath::Float2IntRn(getPadColFloat(applyShift)); } + 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(); } diff --git a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h index 250a33b2c98e2..c6d36a7aee495 100644 --- a/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h +++ b/DataFormats/Detectors/TRD/src/DataFormatsTRDLinkDef.h @@ -43,6 +43,7 @@ #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> + ; @@ -56,6 +57,7 @@ #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> + ; diff --git a/DataFormats/Detectors/TRD/src/Digit.cxx b/DataFormats/Detectors/TRD/src/Digit.cxx index 9e94fe22068bb..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 diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx index 9245165709979..d7b63cae45354 100644 --- a/DataFormats/Detectors/TRD/src/Tracklet64.cxx +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -40,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/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index c37eff9b34f20..b44f41c5d3cb3 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -373,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 }; }; @@ -587,6 +588,7 @@ 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"}; diff --git a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h index f264460890494..4f7e49acb4d98 100644 --- a/DataFormats/Headers/include/Headers/DataHeaderHelpers.h +++ b/DataFormats/Headers/include/Headers/DataHeaderHelpers.h @@ -38,7 +38,7 @@ struct fmt::formatter::value, c } template - auto format(const T& p, FormatContext& ctx) + auto format(const T& p, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}", p.template as()); } @@ -67,7 +67,7 @@ struct fmt::formatter { } template - auto format(const o2::header::DataHeader& h, FormatContext& ctx) + auto format(const o2::header::DataHeader& h, FormatContext& ctx) const { if (presentation == 's') { auto res = fmt::format("Data header version {}, flags: {}\n", h.headerVersion, h.flags) + @@ -79,7 +79,8 @@ struct fmt::formatter { 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(" runNumber : {}\n", h.runNumber) + + fmt::format(" split : {}/{}\n", h.splitPayloadIndex, h.splitPayloadParts); return fmt::format_to(ctx.out(), "{}", res); } else { auto res = fmt::format("{}/{}/{}", diff --git a/DataFormats/Headers/include/Headers/Stack.h b/DataFormats/Headers/include/Headers/Stack.h index 259a445f18cf8..9770df9fa54ef 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -14,10 +14,7 @@ #include "MemoryResources/MemoryResources.h" #include "Headers/DataHeader.h" -namespace o2 -{ - -namespace header +namespace o2::header { //__________________________________________________________________________________________________ /// @struct Stack @@ -39,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) { @@ -90,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)...) { } @@ -102,7 +100,7 @@ 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}} { if constexpr (sizeof...(headers) > 1) { injectAll(buffer.get(), std::forward(headers)...); @@ -122,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) { @@ -133,19 +131,19 @@ 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 @@ -231,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/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 264fe59b1caac..a49cd00d75255 100644 --- a/DataFormats/MemoryResources/test/testMemoryResources.cxx +++ b/DataFormats/MemoryResources/test/testMemoryResources.cxx @@ -60,7 +60,7 @@ 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) { @@ -88,15 +88,6 @@ 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); } @@ -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{(size_t)getpid() * 1000 + 3}; - 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", "adoptVector_test", &config); - 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/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 8e779ef452191..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}}; //_______________________________________________ diff --git a/DataFormats/Parameters/src/GRPTool.cxx b/DataFormats/Parameters/src/GRPTool.cxx index 903d659940558..e7561e6fc1ef6 100644 --- a/DataFormats/Parameters/src/GRPTool.cxx +++ b/DataFormats/Parameters/src/GRPTool.cxx @@ -312,7 +312,7 @@ 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{}; if (!o2::conf::SimConfig::determineActiveModulesList(opts.detectorList, opts.readout, std::vector(), modules)) { 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/README.md b/DataFormats/QualityControl/README.md index 486856c983306..33821319b7316 100644 --- a/DataFormats/QualityControl/README.md +++ b/DataFormats/QualityControl/README.md @@ -15,7 +15,7 @@ Data quality is determined through two methods: 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 (**not ready yet**, to be done in the scope of QC-978). +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 @@ -49,12 +49,13 @@ Each Flag Type has the following attributes: #### Creating and Managing Flag Types * **FlagTypeFactory** ensures a centralized and consistent list of available Flag Types. - New types can only be created through this factory. + 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. diff --git a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h index 16123b74895ab..9c38952562483 100644 --- a/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h +++ b/DataFormats/QualityControl/include/DataFormatsQualityControl/QualityControlFlag.h @@ -49,6 +49,7 @@ class QualityControlFlag 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; } diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 86c0831d2134e..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 @@ -73,6 +74,7 @@ o2_target_root_dictionary( 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/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 index eb7229252b586..f7f18976fa508 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Decay3Body.h @@ -29,7 +29,7 @@ class Decay3Body : public o2::track::TrackParCov /// TO BE DONE: extend to gener using PID = o2::track::PID; Decay3Body() = default; - Decay3Body(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2); + 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]; } diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h index 31a4b8ebc44b3..5a5a8a9e64cca 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/DecayNBodyIndex.h @@ -55,14 +55,19 @@ class DecayNBodyIndex 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(0); } - bool isPhotonOnly() const { return testBit(1); } - bool isCollinear() const { return testBit(2); } - void setStandaloneV0() { setBit(0); } - void setPhotonOnly() { setBit(1); } - void setCollinear() { setBit(2); } + 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); }; 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/MatchInfoTOF.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOF.h index c3cdae54823e6..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; } @@ -41,6 +41,12 @@ class MatchInfoTOF 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,6 +59,8 @@ 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; } @@ -62,6 +70,29 @@ class MatchInfoTOF 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 @@ -72,12 +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, 6); + ClassDefNV(MatchInfoTOF, 9); }; } // namespace dataformats } // namespace o2 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h index 188b7b3ab121a..f1b555301bf80 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/MatchInfoTOFReco.h @@ -35,7 +35,7 @@ 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; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h index e01ce253156a7..ce70e69aa6ddd 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h @@ -33,19 +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, @@ -64,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, @@ -73,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 @@ -110,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!"); 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 76ca8473553cd..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); 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 2ef5577ec6bf2..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 @@ -119,6 +119,9 @@ constexpr float MaxPT = 100000.; // do not allow pTs exceeding 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_ @@ -128,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; @@ -160,7 +163,7 @@ 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; @@ -191,6 +194,7 @@ 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; @@ -199,16 +203,16 @@ class TrackParametrization GPUd() value_t getPt() const; GPUd() value_t getE2() const; GPUd() value_t getE() const; - GPUd() static inline value_t getdEdxBB(value_t betagamma) { return BetheBlochSolid(betagamma); } - GPUd() static inline value_t getdEdxBBOpt(value_t betagamma) { return BetheBlochSolidOpt(betagamma); } - GPUd() static inline value_t getBetheBlochSolidDerivativeApprox(value_T dedx, value_T bg) { return BetheBlochSolidDerivative(dedx, bg); } + 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; @@ -225,6 +229,7 @@ class TrackParametrization // parameters manipulation 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(); @@ -247,6 +252,8 @@ class TrackParametrization #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); @@ -274,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]; } @@ -292,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]; @@ -357,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]; } @@ -427,6 +436,7 @@ template GPUdi() void TrackParametrization::setAlpha(value_t v) { mAlpha = v; + math_utils::detail::bringToPMPi(mAlpha); } //____________________________________________________________ @@ -555,6 +565,18 @@ 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 @@ -739,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 0f01713d50beb..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,14 +38,14 @@ 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); GPUhdDefault() TrackParametrizationWithError(const TrackParametrizationWithError& src) = default; GPUdDefault() TrackParametrizationWithError(TrackParametrizationWithError&& src) = default; @@ -56,7 +57,7 @@ class TrackParametrizationWithError : public 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,7 +77,7 @@ 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(); @@ -88,23 +89,31 @@ class TrackParametrizationWithError : public TrackParametrization // 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 getPredictedChi2Unchecked(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 getPredictedChi2Unchecked(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; + 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; - bool update(const TrackParametrizationWithError& rhs, const MatrixDSym5& covInv); - bool update(const TrackParametrizationWithError& rhs); + 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); @@ -114,7 +123,7 @@ class TrackParametrizationWithError : public TrackParametrization GPUd() bool update(const BaseCluster& p); 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(); @@ -143,7 +152,7 @@ class TrackParametrizationWithError : public TrackParametrization //__________________________________________________________________________ template -GPUdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{} +GPUhdi() TrackParametrizationWithError::TrackParametrizationWithError() : TrackParametrization{} { } @@ -313,6 +322,16 @@ GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const Base 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 @@ -322,9 +341,9 @@ GPUdi() auto TrackParametrizationWithError::getPredictedChi2(const dim2 //______________________________________________ template -GPUdi() auto TrackParametrizationWithError::getPredictedChi2Unchecked(const dim2_t& p, const dim3_t& cov) const -> value_t +GPUdi() auto TrackParametrizationWithError::getPredictedChi2Quiet(const dim2_t& p, const dim3_t& cov) const -> value_t { - return getPredictedChi2Unchecked(p.data(), cov.data()); + return getPredictedChi2Quiet(p.data(), cov.data()); } //______________________________________________ diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackUtils.h index 96ab53fde5e5c..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 @@ -39,11 +39,11 @@ 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) { /****************************************************************** * * @@ -190,7 +190,7 @@ GPUd() value_T BetheBlochSolidOpt(value_T bg) //____________________________________________________ template -GPUd() value_T inline BetheBlochSolidDerivative(value_T dedx, value_T bg) +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 diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index bbc2c01359276..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 diff --git a/DataFormats/Reconstruction/src/Decay3Body.cxx b/DataFormats/Reconstruction/src/Decay3Body.cxx index 46f5c9447c2fb..aa071cea675cd 100644 --- a/DataFormats/Reconstruction/src/Decay3Body.cxx +++ b/DataFormats/Reconstruction/src/Decay3Body.cxx @@ -13,7 +13,7 @@ using namespace o2::dataformats; -Decay3Body::Decay3Body(PID pid, const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2) +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{}; diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h index 6cd72e8668cc1..b386830d9872d 100644 --- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h +++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h @@ -117,4 +117,7 @@ #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 3c45a8ecb6ec2..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 { @@ -503,5 +504,73 @@ bool TrackParCovFwd::getCovXYZPxPyPzGlo(std::array& cv) const 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/TrackLTIntegral.cxx b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx index 3efddff00f512..426c3da04726c 100644 --- a/DataFormats/Reconstruction/src/TrackLTIntegral.cxx +++ b/DataFormats/Reconstruction/src/TrackLTIntegral.cxx @@ -39,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; @@ -49,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 d79b6dcb7b474..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,7 +130,7 @@ 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 = getPtInv(); @@ -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; @@ -603,7 +632,7 @@ std::string TrackParametrization::asStringHexadecimal() 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}", + 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), @@ -624,7 +653,7 @@ GPUd() void TrackParametrization::printParam() const #ifndef GPUCA_ALIGPUCODE printf("%s\n", asString().c_str()); #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", + 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 } @@ -637,7 +666,7 @@ GPUd() void TrackParametrization::printParamHexadecimal() #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", + 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()), @@ -662,7 +691,7 @@ 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) { @@ -681,18 +710,18 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val 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); @@ -702,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; @@ -728,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) { @@ -750,11 +781,11 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val return false; } } - return x; + return true; } // this is a straight track if (gpu::CAMath::Abs(sn) >= constants::math::Almost1) { // || to Y axis - value_t det = (r - mX) * (r + mX); + double det = (r - mX) * (r + mX); if (det < 0.f) { return false; // does not reach raduis r } @@ -764,7 +795,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } det = gpu::CAMath::Sqrt(det); if (dir == DirOutward) { // along the track direction - if (sn > 0.f) { + if (sn > 0.) { if (fy > det) { return false; // track is along Y axis and above the circle } @@ -774,7 +805,7 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } else if (dir == DirInward) { // against track direction - if (sn > 0.f) { + if (sn > 0.) { if (fy < -det) { return false; // track is along Y axis } @@ -783,13 +814,13 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } else if (gpu::CAMath::Abs(sn) <= constants::math::Almost0) { // || to X axis - value_t det = (r - fy) * (r + fy); - if (det < 0.f) { + 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.f ? det : -det; // choose the solution requiring the smalest step + 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) { @@ -805,17 +836,17 @@ GPUd() bool TrackParametrization::getXatLabR(value_t r, value_t& x, val } } } 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) { + 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); - value_t xcys = mX * cs + fy * sn; - value_t t = -xcys; + auto xcys = mX * cs + fy * sn; + auto t = -xcys; if (dir == DirAuto) { - t += t > 0.f ? -det : det; // chose the solution requiring the smalest step + 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 @@ -956,8 +987,10 @@ GPUd() typename TrackParametrization::value_t 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 dfe4d7c31b2ab..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,110 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, valu &c44 = mC[kSigQ2Pt2]; // evaluate matrix in double prec. - double rinv = 1. / r1; - double r3inv = rinv * rinv * rinv; - double f24 = dx * b * constants::math::B2C; // x2r/mP[kQ2Pt]; - double f02 = dx * r3inv; - double f04 = 0.5 * f24 * f02; - double f12 = f02 * this->getTgl() * f1; - double f14 = 0.5 * f24 * f12; // 0.5*f24*f02*getTgl()*f1; - double f13 = dx * rinv; + value_t kb = bz * constants::math::B2C; + double r2inv = 1. / r2, r1inv = 1. / r1; + double dx2r1pr2 = dx * r1pr2Inv; + + double hh = dx2r1pr2 * r2inv * (1. + r1 * r2 + f1 * f2), jj = dx * (dy2dx - f2 * r2inv); + double f02 = hh * r1inv; + double f04 = hh * dx2r1pr2 * kb; + double f24 = dx * kb; // x2r/mP[kQ2Pt]; + double f12 = this->getTgl() * (f02 * f2 + jj); + double f13 = dx * (r2 + f2 * dy2dx); + double f14 = this->getTgl() * (f04 * f2 + jj * f24); + + // b = C*ft + double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30; + double b02 = f24 * c40; + double b10 = f02 * c21 + f04 * c41, b11 = f12 * c21 + f14 * c41 + f13 * c31; + double b12 = f24 * c41; + double b20 = f02 * c22 + f04 * c42, b21 = f12 * c22 + f14 * c42 + f13 * c32; + double b22 = f24 * c42; + double b40 = f02 * c42 + f04 * c44, b41 = f12 * c42 + f14 * c44 + f13 * c43; + double b42 = f24 * c44; + double b30 = f02 * c32 + f04 * c43, b31 = f12 * c32 + f14 * c43 + f13 * c33; + double b32 = f24 * c43; + + // a = f*b = f*C*ft + double a00 = f02 * b20 + f04 * b40, a01 = f02 * b21 + f04 * b41, a02 = f02 * b22 + f04 * b42; + double a11 = f12 * b21 + f14 * b41 + f13 * b31, a12 = f12 * b22 + f14 * b42 + f13 * b32; + double a22 = f24 * b42; + + // F*C*Ft = C + (b + bt + a) + c00 += b00 + b00 + a00; + c10 += b10 + b01 + a01; + c20 += b20 + b02 + a02; + c30 += b30; + c40 += b40; + c11 += b11 + b11 + a11; + c21 += b21 + b12 + a12; + c31 += b31; + c41 += b41; + c22 += b22 + b22 + a22; + c32 += b32; + c42 += b42; + + checkCovariance(); + + return true; +} + +//______________________________________________________________ +template +GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, TrackParametrization& linRef0, value_t bz) +{ + //---------------------------------------------------------------- + // propagate this track to the plane X=xk (cm) in the field "b" (kG), using linRef as linearization point + //---------------------------------------------------------------- + if (this->getAbsCharge() == 0) { + bz = 0; + } + value_t dx = xk - this->getX(); + if (gpu::CAMath::Abs(dx) < constants::math::Almost0) { + this->setX(xk); + linRef0.setX(xk); + return true; + } + // propagate reference track + TrackParametrization linRef1 = linRef0; + if (!linRef1.propagateTo(xk, bz)) { + return false; + } + value_t kb = bz * constants::math::B2C; + // evaluate in double prec. + double snpRef0 = linRef0.getSnp(), cspRef0 = gpu::CAMath::Sqrt((1 - snpRef0) * (1 + snpRef0)); + double snpRef1 = linRef1.getSnp(), cspRef1 = gpu::CAMath::Sqrt((1 - snpRef1) * (1 + snpRef1)); + double cspRef0Inv = 1 / cspRef0, cspRef1Inv = 1 / cspRef1, cc = cspRef0 + cspRef1, ccInv = 1 / cc, dy2dx = (snpRef0 + snpRef1) * ccInv; + double dxccInv = dx * ccInv, hh = dxccInv * cspRef1Inv * (1 + cspRef0 * cspRef1 + snpRef0 * snpRef1), jj = dx * (dy2dx - snpRef1 * cspRef1Inv); + + double f02 = hh * cspRef0Inv; + double f04 = hh * dxccInv * kb; + double f24 = dx * kb; + double f12 = linRef0.getTgl() * (f02 * snpRef1 + jj); + double f13 = dx * (cspRef1 + snpRef1 * dy2dx); // dS + double f14 = linRef0.getTgl() * (f04 * snpRef1 + jj * f24); + + // difference between the current and reference state + value_t diff[5]; + for (int i = 0; i < 5; i++) { + diff[i] = this->getParam(i) - linRef0.getParam(i); + } + value_t snpUpd = snpRef1 + diff[kSnp] + f24 * diff[kQ2Pt]; + if (gpu::CAMath::Abs(snpUpd) > constants::math::Almost1) { + return false; + } + linRef0 = linRef1; // update reference track + this->setX(xk); + this->setY(linRef1.getY() + diff[kY] + f02 * diff[kSnp] + f04 * diff[kQ2Pt]); + this->setZ(linRef1.getZ() + diff[kZ] + f13 * diff[kTgl] + f14 * diff[kQ2Pt]); + this->setSnp(snpUpd); + this->setTgl(linRef1.getTgl() + diff[kTgl]); + this->setQ2Pt(linRef1.getQ2Pt() + diff[kQ2Pt]); + + value_t &c00 = mC[kSigY2], &c10 = mC[kSigZY], &c11 = mC[kSigZ2], &c20 = mC[kSigSnpY], &c21 = mC[kSigSnpZ], + &c22 = mC[kSigSnp2], &c30 = mC[kSigTglY], &c31 = mC[kSigTglZ], &c32 = mC[kSigTglSnp], &c33 = mC[kSigTgl2], + &c40 = mC[kSigQ2PtY], &c41 = mC[kSigQ2PtZ], &c42 = mC[kSigQ2PtSnp], &c43 = mC[kSigQ2PtTgl], + &c44 = mC[kSigQ2Pt2]; // b = C*ft double b00 = f02 * c20 + f04 * c40, b01 = f12 * c20 + f14 * c40 + f13 * c30; @@ -160,6 +255,7 @@ GPUd() bool TrackParametrizationWithError::testRotate(value_t) const // no ops return true; } + //______________________________________________________________ template GPUd() bool TrackParametrizationWithError::rotate(value_t alpha) @@ -215,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) @@ -229,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); @@ -247,6 +442,10 @@ GPUd() bool TrackParametrizationWithError::propagateToDCA(const o2::dat #if !defined(GPUCA_ALIGPUCODE) LOG(debug) << "failed to propagate to alpha=" << alp << " X=" << xv << vtx << " | Track is: " << tmpT.asString(); #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; @@ -261,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); @@ -270,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 @@ -470,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; } @@ -487,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; @@ -544,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], @@ -598,6 +798,198 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons 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; +} + //______________________________________________ template GPUd() void TrackParametrizationWithError::checkCorrelations() @@ -736,7 +1128,7 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2(const value //______________________________________________ template -GPUd() auto TrackParametrizationWithError::getPredictedChi2Unchecked(const value_t* p, const value_t* cov) const -> value_t +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]); @@ -754,11 +1146,17 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2Unchecked(co return (d * (szz * d - sdz * z) + z * (sdd * z - d * sdz)) / det; } -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // Disable function relying on ROOT SMatrix on GPU +//______________________________________________ +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()); @@ -778,14 +1176,6 @@ void TrackParametrizationWithError::buildCombinedCovMatrix(const TrackP cov(kQ2Pt, kQ2Pt) = static_cast(getSigma1Pt2()) + static_cast(rhs.getSigma1Pt2()); } -//______________________________________________ -template -GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs) const -> value_t -{ - MatrixDSym5 cov; // perform matrix operations in double! - return getPredictedChi2(rhs, cov); -} - //______________________________________________ template GPUd() auto TrackParametrizationWithError::getPredictedChi2(const TrackParametrizationWithError& rhs, MatrixDSym5& covToSet) const -> value_t @@ -793,11 +1183,11 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2(const Track // 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; } @@ -826,11 +1216,11 @@ GPUd() bool TrackParametrizationWithError::update(const TrackParametriz // 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; } @@ -867,7 +1257,7 @@ GPUd() bool TrackParametrizationWithError::update(const TrackParametriz } // 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); @@ -901,8 +1291,6 @@ GPUd() bool TrackParametrizationWithError::update(const TrackParametriz return update(rhs, covI); } -#endif - //______________________________________________ template GPUd() bool TrackParametrizationWithError::update(const value_t* p, const value_t* cov) @@ -1119,9 +1507,146 @@ GPUd() bool TrackParametrizationWithError::correctForMaterial(value_t x 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; + } + } + + 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(); + } + if (theta2 > constants::math::PI * constants::math::PI) { + return false; + } + 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; + auto pscale = p0 / p; + linRef.setQ2Pt(linRef.getQ2Pt() * pscale); + this->setQ2Pt(this->getQ2Pt() * pscale); + + checkCovariance(); + + return true; +} + //______________________________________________________________ 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 @@ -1217,7 +1742,7 @@ GPUd() void TrackParametrizationWithError::print() const #elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT)) TrackParametrization::printParam(); printf( - "\nCov: [%+.3e] [%+.3e %+.3e] [%+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e] [%+.3e %+.3e %+.3e %+.3e %+.3e]", + " 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]); @@ -1234,7 +1759,7 @@ GPUd() void TrackParametrizationWithError::printHexadecimal() #elif !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT)) TrackParametrization::printParamHexadecimal(); printf( - "\nCov: [%x] [%x %x] [%x %x %x] [%x %x %x %x] [%x %x %x %x %x]", + " 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]), @@ -1245,8 +1770,10 @@ GPUd() void TrackParametrizationWithError::printHexadecimal() 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/Vertex.cxx b/DataFormats/Reconstruction/src/Vertex.cxx index b902e9972a13d..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 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/include/CommonDataFormat/AbstractRef.h b/DataFormats/common/include/CommonDataFormat/AbstractRef.h index 8dce94d502ffb..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: @@ -87,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/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 7aa3ccdd5d12c..e99f338a16343 100644 --- a/DataFormats/common/include/CommonDataFormat/InteractionRecord.h +++ b/DataFormats/common/include/CommonDataFormat/InteractionRecord.h @@ -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 90019de5122be..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; } 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/simulation/CMakeLists.txt b/DataFormats/simulation/CMakeLists.txt index fac67cc927562..33c91337c77e9 100644 --- a/DataFormats/simulation/CMakeLists.txt +++ b/DataFormats/simulation/CMakeLists.txt @@ -55,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 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/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index f531cf6e7f870..0dc3806e52cf2 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -82,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 @@ -110,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 @@ -122,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; @@ -150,6 +165,9 @@ class DigitizationContext } } + void setDigitizerInteractionRate(float intRate) { mDigitizerInteractionRate = intRate; } + float getDigitizerInteractionRate() const { return mDigitizerInteractionRate; } + std::vector const* getCTPDigits() const { return mCTPTrigger; } bool hasTriggerInput() const { return mHasTrigger; } @@ -164,17 +182,13 @@ 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 @@ -184,7 +198,13 @@ class DigitizationContext mutable std::vector const* mCTPTrigger = nullptr; // CTP trigger info associated to this digitization context mutable bool mHasTrigger = false; // - ClassDefNV(DigitizationContext, 5); + // 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 @@ -195,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/MCTrack.h b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h index a5322b2f53dbf..b4cf26b11f82e 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h @@ -139,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); diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index d89443150e199..23dc30119aa7a 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -235,62 +235,38 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) //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, - 2.5e-15, 6, "Ion", ionCode); - } - //hyper helium 4 excited state - ionCode = 1010020041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhelium4*", "Hyperhelium4*", 3.9231, kFALSE, - 2.5e-15, 6, "Ion", ionCode); - } - //anti hyper helium 4 excited state - ionCode = -1010020041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhelium4*", "AntiHyperhelium4*", 3.9231, kFALSE, + db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.921728, kFALSE, 2.5e-15, 6, "Ion", ionCode); } @@ -309,13 +285,13 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) 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); } @@ -331,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, @@ -467,6 +470,15 @@ 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)) { @@ -487,6 +499,14 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) } // 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); @@ -629,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 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/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/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/Detectors/AOD/CMakeLists.txt b/Detectors/AOD/CMakeLists.txt index acd703dcc6be7..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 @@ -42,14 +43,14 @@ o2_add_executable( COMPONENT_NAME aod-producer TARGETVARNAME targetName SOURCES src/aod-producer-workflow.cxx src/AODProducerWorkflowSpec.cxx src/AODMcProducerHelpers.cxx - PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version + 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 src/AODMcProducerHelpers.cxx - PUBLIC_LINK_LIBRARIES internal::AODProducerWorkflow O2::Version + 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/include/AODProducerWorkflow/AODMcProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h index 42431d19cb210..5e9cd445b576b 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODMcProducerHelpers.h @@ -315,7 +315,8 @@ uint32_t updateParticles(const ParticleCursor& cursor, bool background = false, uint32_t weightMask = 0xFFFFFFF0, uint32_t momentumMask = 0xFFFFFFF0, - uint32_t positionMask = 0xFFFFFFF0); + uint32_t positionMask = 0xFFFFFFF0, + bool signalFilter = false); } // namespace o2::aodmchelpers #endif /* O2_AODMCPRODUCER_HELPERS */ diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h index dc6f589977cda..5351504443269 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include namespace o2::aodhelpers @@ -50,12 +48,12 @@ struct TripletEqualTo { typedef boost::unordered_map TripletsMap_t; template -framework::Produces createTableCursor(framework::ProcessingContext& pc) +auto createTableCursor(framework::ProcessingContext& pc) { framework::Produces c; c.resetCursor(pc.outputs() .make(framework::OutputForTable::ref())); - c.setLabel(o2::aod::MetadataTrait::metadata::tableLabel()); + c.setLabel(aod::label()); return c; } } // namespace o2::aodhelpers diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 1468ea8f7fa27..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -21,7 +21,6 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" -#include "Framework/AnalysisHelpers.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -30,9 +29,12 @@ #include "TStopwatch.h" #include "ZDCBase/Constants.h" #include "GlobalTracking/MatchGlobalFwd.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "CommonUtils/EnumFlags.h" +#include +#include #include -#include #include #include using namespace o2::framework; @@ -203,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) : mUseMC(useMC), mEnableSV(enableSV), mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr) {} + 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; @@ -230,20 +237,30 @@ class AODProducerWorkflowDPL : public Task 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 mMinPropR{o2::constants::geom::XTPCInnerRef + 0.1f}; + 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 @@ -257,6 +274,7 @@ 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; @@ -339,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 @@ -359,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 @@ -374,6 +396,7 @@ class AODProducerWorkflowDPL : public Task 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; @@ -396,18 +419,31 @@ class AODProducerWorkflowDPL : public Task struct TrackQA { GID trackID; - float tpcTime0; - 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; + 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() @@ -458,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; }; @@ -496,8 +530,8 @@ class AODProducerWorkflowDPL : public Task 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); @@ -511,7 +545,7 @@ class AODProducerWorkflowDPL : public Task // * fills tables collision by collision // * interaction time is for TOF information template void fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, @@ -524,6 +558,7 @@ class AODProducerWorkflowDPL : public Task TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, @@ -624,6 +659,8 @@ class AODProducerWorkflowDPL : public Task 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); @@ -643,7 +680,7 @@ class AODProducerWorkflowDPL : public Task }; /// 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 diff --git a/Detectors/AOD/src/AODMcProducerHelpers.cxx b/Detectors/AOD/src/AODMcProducerHelpers.cxx index 3ce07a312713f..a7093e0048c25 100644 --- a/Detectors/AOD/src/AODMcProducerHelpers.cxx +++ b/Detectors/AOD/src/AODMcProducerHelpers.cxx @@ -65,9 +65,8 @@ short updateMCCollisions(const CollisionCursor& cursor, truncateFloatFraction(header.GetZ(), mask), truncateFloatFraction(time, mask), truncateFloatFraction(weight, mask), - header.GetB() /*, - getEventInfo(header, Key::planeAngle, header.GetRotZ())*/ - ); + header.GetB(), + getEventInfo(header, Key::planeAngle, header.GetRotZ())); return encodedGeneratorId; } //-------------------------------------------------------------------- @@ -306,7 +305,8 @@ uint32_t updateParticles(const ParticleCursor& cursor, bool background, uint32_t weightMask, uint32_t momentumMask, - uint32_t positionMask) + uint32_t positionMask, + bool signalFilter) { using o2::mcutils::MCTrackNavigator; using namespace o2::aod::mcparticle::enums; @@ -355,6 +355,9 @@ uint32_t updateParticles(const ParticleCursor& cursor, 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 diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 9c67212cb1a1f..852419a9895eb 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -51,6 +51,7 @@ #include "Framework/DataTypes.h" #include "Framework/TableBuilder.h" #include "Framework/CCDBParamSpec.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "FT0Base/Geometry.h" #include "GlobalTracking/MatchTOF.h" #include "ReconstructionDataFormats/Cascade.h" @@ -85,8 +86,10 @@ #include "MathUtils/Utils.h" #include "Math/SMatrix.h" #include "TString.h" +#include #include #include +#include #include #include #include @@ -99,6 +102,8 @@ #ifdef WITH_OPENMP #include #endif +#include +#include using namespace o2::framework; using namespace o2::math_utils::detail; @@ -106,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 @@ -334,6 +340,7 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks extraInfoHolder.itsClusterSizes, extraInfoHolder.tpcNClsFindable, extraInfoHolder.tpcNClsFindableMinusFound, + extraInfoHolder.tpcNClsFindableMinusPID, extraInfoHolder.tpcNClsFindableMinusCrossedRows, extraInfoHolder.tpcNClsShared, extraInfoHolder.trdPattern, @@ -354,16 +361,12 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks template void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) { - - // trackQA tracksQACursor( - - // truncateFloatFraction(trackQAInfoHolder.tpcdcaR, mTrackChi2), - // truncateFloatFraction(trackQAInfoHolder.tpcdcaZ, mTrackChi2), trackQAInfoHolder.trackID, - trackQAInfoHolder.tpcTime0, - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, + 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, @@ -372,7 +375,19 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs trackQAInfoHolder.tpcdEdxTot0R, trackQAInfoHolder.tpcdEdxTot1R, trackQAInfoHolder.tpcdEdxTot2R, - trackQAInfoHolder.tpcdEdxTot3R); + 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 @@ -417,7 +432,7 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC } template void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, std::uint64_t collisionBC, @@ -430,6 +445,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, TracksQACursorType& tracksQACursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, + MFTTracksCovCursorType& mftTracksCovCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, FwdTracksCursorType& fwdTracksCursor, FwdTracksCovCursorType& fwdTracksCovCursor, @@ -449,6 +465,9 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } 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()); @@ -468,7 +487,7 @@ 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++; @@ -480,7 +499,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, 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)); + 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 (writeQAData) { @@ -496,6 +515,19 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } } + // 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; @@ -508,7 +540,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, } const auto& trOrig = data.getTrackParam(trackIndex); bool isProp = false; - if (mPropTracks && trOrig.getX() < mMinPropR && + 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); @@ -545,6 +577,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, 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; @@ -588,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) { @@ -732,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()); } @@ -771,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; @@ -892,13 +947,17 @@ void clearMCKeepStore(std::vector>>& st } // 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 (track < 0) { LOG(warn) << "trackID is smaller than 0. Neglecting"; return; } - store[source][event][track] = value; + if (useSigFilt && source == 0) { + store[source][event][track] = -1; + } else { + store[source][event][track] = value; + } } void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& mcReader, @@ -927,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 @@ -942,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()) { @@ -957,7 +1016,7 @@ 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); } } } @@ -971,7 +1030,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); } } if (mInputSources[GIndex::PHS]) { @@ -980,7 +1039,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); } } using namespace aodmchelpers; @@ -1004,7 +1063,8 @@ void AODProducerWorkflowDPL::fillMCParticlesTable(o2::steer::MCKinematicsReader& source == 0, // background mMcParticleW, mMcParticleMom, - mMcParticlePos); + mMcParticlePos, + mUseSigFiltMC); mcReader.releaseTracksForSourceAndEvent(source, event); } @@ -1020,9 +1080,9 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr 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--;) { @@ -1046,7 +1106,7 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr 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; @@ -1071,52 +1131,38 @@ void AODProducerWorkflowDPL::fillMCTrackLabelsTable(MCTrackLabelCursorType& mcTr 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(labelHolder.labelID, - labelHolder.labelMask); + mcTrackLabelCursor(labelHolder.labelID, labelHolder.labelMask); } } } @@ -1378,7 +1424,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack sTrk.mMasses[1], sTrk.mMatchChi2, sTrk.mTopoChi2, - sTrk.mITSClusSize); + sTrk.getAverageClusterSize()); } else if (sTrk.mPartType == dataformats::kStrkCascade) { cascCurs(mStrTrkIndices[sTrkID++], itsTableIdx, @@ -1390,7 +1436,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack sTrk.mMasses[1], sTrk.mMatchChi2, sTrk.mTopoChi2, - sTrk.mITSClusSize); + sTrk.getAverageClusterSize()); } else { d3BodyCurs(mStrTrkIndices[sTrkID++], itsTableIdx, @@ -1402,7 +1448,7 @@ void AODProducerWorkflowDPL::fillStrangenessTrackingTables(const o2::globaltrack sTrk.mMasses[1], sTrk.mMatchChi2, sTrk.mTopoChi2, - sTrk.mITSClusSize); + sTrk.getAverageClusterSize()); } } } @@ -1432,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++; } @@ -1648,10 +1694,11 @@ 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"); @@ -1661,9 +1708,24 @@ void AODProducerWorkflowDPL::init(InitContext& ic) 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; @@ -1688,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!"; @@ -1703,6 +1767,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mTrackCovOffDiag = 0xFFFFFFFF; mTrackSignal = 0xFFFFFFFF; mTrackTime = 0xFFFFFFFF; + mTPCTime0 = 0xFFFFFFFF; mTrackTimeError = 0xFFFFFFFF; mTrackPosEMCAL = 0xFFFFFFFF; mTracklets = 0xFFFFFFFF; @@ -1723,8 +1788,11 @@ 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; @@ -1746,7 +1814,43 @@ void AODProducerWorkflowDPL::init(InitContext& ic) 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) { @@ -1797,6 +1901,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) using namespace o2::aodhelpers; auto bcCursor = createTableCursor(pc); + auto bcFlagsCursor = createTableCursor(pc); auto cascadesCursor = createTableCursor(pc); auto collisionsCursor = createTableCursor(pc); auto decay3BodyCursor = createTableCursor(pc); @@ -1804,16 +1909,20 @@ void AODProducerWorkflowDPL::run(ProcessingContext& 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 tracksQACursor = createTableCursor(pc); auto ambigTracksCursor = createTableCursor(pc); auto ambigMFTTracksCursor = createTableCursor(pc); auto ambigFwdTracksCursor = createTableCursor(pc); @@ -1871,16 +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); } } @@ -1897,6 +2008,11 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) aChannels, truncateFloatFraction(fv0RecPoint.getCollisionGlobalMeanTime() * 1E-3, mV0Time), // ps to ns fv0RecPoint.getTrigger().getTriggersignals()); + + if (mEnableFITextra) { + fv0aExtraCursor(bcID, + aTimes); + } } std::vector zdcEnergy, zdcAmplitudes, zdcTime; @@ -1950,6 +2066,28 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 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++) { @@ -2000,25 +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); @@ -2028,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; } + 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); @@ -2053,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)); } } } @@ -2076,6 +2226,11 @@ 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) { @@ -2144,7 +2299,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 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, tracksQACursor, - ambigTracksCursor, mftTracksCursor, ambigMFTTracksCursor, + ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); // filling collisions and tracks into tables @@ -2186,7 +2341,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs[collisionID]; // passing interaction time in [ps] fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor, - mftTracksCursor, ambigMFTTracksCursor, + mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); collisionID++; } @@ -2234,6 +2389,13 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) bcToClassMask.clear(); + // 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; @@ -2318,8 +2480,10 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) 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}; + 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); @@ -2479,13 +2643,25 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac if (contributorsGID[GIndex::Source::TPC].isIndexSet()) { const auto& tpcOrig = data.getTPCTrack(contributorsGID[GIndex::TPC]); const auto& tpcClData = mTPCCounters[contributorsGID[GIndex::TPC]]; + 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; + 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 @@ -2522,19 +2698,29 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int TrackQA trackQAHolder; auto contributorsGID = data.getTPCContributorGID(trackIndex); const auto& trackPar = data.getTrackParam(trackIndex); - // auto src = trackIndex.getSource(); 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 - // LOGP(info, "GloIdx: {} TPCIdx: {}, NTPCTracks: {}", trackIndex.asString(), contributorsGID.asString(), data.getTPCTracks().size()); - o2::track::TrackParametrization tpcTMP = tpcOrig; /// get backup of the track - o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// should be parameterized - o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); - o2::gpu::gpustd::array dcaInfo{-999., -999.}; - if (o2::base::Propagator::Instance()->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) { + 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}; { @@ -2555,17 +2741,117 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int } trackQAHolder.tpcTime0 = tpcOrig.getTime0(); trackQAHolder.tpcClusterByteMask = byteMask; - float dEdxNorm = (tpcOrig.getdEdx().dEdxTotTPC > 0) ? 100. / tpcOrig.getdEdx().dEdxTotTPC : 0; - trackQAHolder.tpcdEdxMax0R = uint8_t(tpcOrig.getdEdx().dEdxMaxIROC * dEdxNorm); - trackQAHolder.tpcdEdxMax1R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC1 * dEdxNorm); - trackQAHolder.tpcdEdxMax2R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC2 * dEdxNorm); - trackQAHolder.tpcdEdxMax3R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC3 * dEdxNorm); + 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 = uint8_t(tpcOrig.getdEdx().dEdxTotIROC * dEdxNorm); - trackQAHolder.tpcdEdxTot1R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC1 * dEdxNorm); - trackQAHolder.tpcdEdxTot2R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC2 * dEdxNorm); - trackQAHolder.tpcdEdxTot3R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC3 * 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; @@ -2898,13 +3184,45 @@ std::uint64_t AODProducerWorkflowDPL::fillBCSlice(int (&slice)[2], double tmin, return bcOfTimeRef; } +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) { auto dataRequest = std::make_shared(); dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun)); @@ -2959,15 +3277,20 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo 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(), @@ -2985,7 +3308,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo OutputForTable::spec(), OutputForTable::spec(), OutputForTable::spec(), - OutputForTable::spec(), + OutputForTable::spec(), OutputSpec{"TFN", "TFNumber"}, OutputSpec{"TFF", "TFFilename"}, OutputSpec{"AMD", "AODMetadataKeys"}, @@ -3013,7 +3336,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo "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"}}, @@ -3023,18 +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{"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 fe9b147804c03..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() /*, 0.0*/); + 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-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index 1f39f11218be3..81e178642e403 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -37,6 +37,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, + {"enable-FIT-extra", o2::framework::VariantType::Bool, false, {"enable FIT extra output"}}, {"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"}}, @@ -54,6 +55,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) bool enableSV = !configcontext.options().get("disable-secondary-vertices"); 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,HMP"); GID::mask_t src = allowedSrc & GID::getSourcesMask(configcontext.options().get("info-sources")); @@ -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/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 72e78bfd4b40c..d4ab53c8181ce 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -37,7 +37,7 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "GPUO2InterfaceUtils.h" #include "GPUParam.h" #include "Headers/DataHeader.h" @@ -92,6 +92,7 @@ class BarrelAlignmentSpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(tpcOpt.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(tpcOpt.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(tpcOpt.checkCTPIDCconsistency); } ~BarrelAlignmentSpec() override = default; void init(InitContext& ic) final; @@ -147,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; @@ -244,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); @@ -266,7 +266,7 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) prevField = newField; if (mDetMask[DetID::TPC]) { mTPCParam.reset(new o2::gpu::GPUParam); - mTPCParam->SetDefaults(o2::base::Propagator::Instance()->getNominalBz()); + mTPCParam->SetDefaults(o2::base::Propagator::Instance()->getNominalBz(), false); mController->setTPCParam(mTPCParam.get()); } } diff --git a/Detectors/Align/include/Align/AlignConfig.h b/Detectors/Align/include/Align/AlignConfig.h index 91b503c2c923e..e72d436a14e3b 100644 --- a/Detectors/Align/include/Align/AlignConfig.h +++ b/Detectors/Align/include/Align/AlignConfig.h @@ -85,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/AlignableDetectorTRD.h b/Detectors/Align/include/Align/AlignableDetectorTRD.h index a73b0f76902d2..4e7577b11055c 100644 --- a/Detectors/Align/include/Align/AlignableDetectorTRD.h +++ b/Detectors/Align/include/Align/AlignableDetectorTRD.h @@ -18,7 +18,7 @@ #define ALIGNABLEDETECTORTRD_H #include "Align/AlignableDetector.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" namespace o2 { @@ -64,7 +64,7 @@ class AlignableDetectorTRD final : public AlignableDetector 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/AlignableSensorITS.h b/Detectors/Align/include/Align/AlignableSensorITS.h index 8070869bd8c7b..7769eb6aa22f3 100644 --- a/Detectors/Align/include/Align/AlignableSensorITS.h +++ b/Detectors/Align/include/Align/AlignableSensorITS.h @@ -36,6 +36,8 @@ class AlignableSensorITS final : public AlignableSensor 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/AlignmentTrack.h b/Detectors/Align/include/Align/AlignmentTrack.h index ef4552cb9a37d..cb69f11cbf85c 100644 --- a/Detectors/Align/include/Align/AlignmentTrack.h +++ b/Detectors/Align/include/Align/AlignmentTrack.h @@ -39,6 +39,7 @@ 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; @@ -83,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); @@ -119,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(); } @@ -179,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; @@ -205,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 @@ -224,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) { @@ -240,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; @@ -248,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 abbb051be138a..90abf2025d1c3 100644 --- a/Detectors/Align/include/Align/Controller.h +++ b/Detectors/Align/include/Align/Controller.h @@ -54,7 +54,7 @@ #include #include #include "Align/Mille.h" -// #include "GPUO2Interface.h" +// #include "GPUO2ExternalUser.h" // #include "DataFormatsTPC/WorkflowHelper.h" namespace o2 @@ -282,6 +282,7 @@ class Controller final : public TObject 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; } diff --git a/Detectors/Align/src/AlignableDetectorTPC.cxx b/Detectors/Align/src/AlignableDetectorTPC.cxx index f66d9e3f3ab95..980ded2d8ff2f 100644 --- a/Detectors/Align/src/AlignableDetectorTPC.cxx +++ b/Detectors/Align/src/AlignableDetectorTPC.cxx @@ -24,7 +24,7 @@ #include "DataFormatsTPC/WorkflowHelper.h" #include #include -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "GPUParam.inc" @@ -214,8 +214,8 @@ int AlignableDetectorTPC::processPoints(GIndex gid, int npntCut, bool inv) auto* sectSensor = (AlignableSensorTPC*)getSensor(currentSector); const auto* sysE = sectSensor->getAddError(); // additional syst error - gpu::gpustd::array p = {y, z}; - gpu::gpustd::array c = {0, 0, 0}; + 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; diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index d752553bf6ead..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 @@ -175,10 +176,12 @@ int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) 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; } const auto* transformer = mController->getTRDTransformer(); auto algTrack = mController->getAlgTrack(); 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/AlignmentTrack.cxx b/Detectors/Align/src/AlignmentTrack.cxx index 554d30e246e29..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] @@ -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; } // @@ -1024,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) // @@ -1042,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(); @@ -1139,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()) { @@ -1178,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()) { @@ -1335,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(); @@ -1346,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(); diff --git a/Detectors/Align/src/Controller.cxx b/Detectors/Align/src/Controller.cxx index a45314b2285c0..5cfbbf9f3a4ae 100644 --- a/Detectors/Align/src/Controller.cxx +++ b/Detectors/Align/src/Controller.cxx @@ -44,7 +44,7 @@ #include #include #include -#include "GPUO2Interface.h" +#include "GPUO2ExternalUser.h" #include "DataFormatsTPC/WorkflowHelper.h" #include #include "CommonUtils/NameConf.h" diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 952dc9e865693..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 @@ -27,6 +29,8 @@ o2_add_library(DetectorsBase src/Stack.cxx src/VMCSeederService.cxx src/GlobalParams.cxx + src/O2Tessellated.cxx + src/TGeoGeometryUtils.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -41,9 +45,10 @@ o2_add_library(DetectorsBase O2::SimulationDataFormat O2::SimConfig O2::CCDB - O2::GPUDataTypeHeaders + O2::GPUDataTypes MC::VMC TBB::tbb + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -51,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 @@ -59,7 +65,9 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/Aligner.h include/DetectorsBase/Stack.h include/DetectorsBase/SimFieldUtils.h - include/DetectorsBase/GlobalParams.h) + include/DetectorsBase/GlobalParams.h + include/DetectorsBase/O2Tessellated.h + ) if(BUILD_SIMULATION) if (NOT APPLE) @@ -85,6 +93,7 @@ endif() install(FILES test/buildMatBudLUT.C test/extractLUTLayers.C + test/rescaleLUT.C DESTINATION share/macro/) o2_add_test_root_macro(test/buildMatBudLUT.C @@ -94,3 +103,7 @@ o2_add_test_root_macro(test/buildMatBudLUT.C 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 bf4f37ecbeff5..e94123bb2b7ff 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -58,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; @@ -189,6 +189,7 @@ class CTFCoderBase std::vector loadDictionaryFromTree(TTree* tree); std::vector mCoders; // encoders/decoders DetID mDet; + std::string mDictOpt{}; std::string mDictBinding{"ctfdict"}; std::string mTrigOffsBinding{"trigoffset"}; CTFDictHeader mExtHeader; // external dictionary header @@ -313,6 +314,7 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (ic.options().hasOption("irframe-shift")) { mIRFrameSelShift = (long)ic.options().get("irframe-shift"); } + bool ansVersionProvided = false; if (ic.options().hasOption("ans-version")) { if (ic.options().isSet("ans-version")) { const std::string ansVersionString = ic.options().get("ans-version"); @@ -322,18 +324,21 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (mANSVersion == ANSVersionUnspecified) { throw std::invalid_argument(fmt::format("Invalid ANS Version {}", ansVersionString)); } + ansVersionProvided = true; } } } - auto dict = ic.options().get("ctf-dict"); - if (dict.empty() || dict == "ccdb") { // load from CCDB + 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 } @@ -368,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)))) { diff --git a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h index 6249b9c693b8c..ba3dd821fa118 100644 --- a/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h +++ b/Detectors/Base/include/DetectorsBase/DPLWorkflowUtils.h @@ -25,6 +25,7 @@ #include "Framework/CompletionPolicy.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DeviceSpec.h" +#include "Framework/Task.h" #include "Framework/DataSpecUtils.h" #include #include diff --git a/Detectors/Base/include/DetectorsBase/Detector.h b/Detectors/Base/include/DetectorsBase/Detector.h index 1432d93c53821..f1744086d6a05 100644 --- a/Detectors/Base/include/DetectorsBase/Detector.h +++ b/Detectors/Base/include/DetectorsBase/Detector.h @@ -106,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); diff --git a/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h b/Detectors/Base/include/DetectorsBase/GRPGeomHelper.h index 663f09702c638..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; @@ -105,6 +107,7 @@ struct GRPGeomRequest { GRPGeomRequest() = delete; 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/MatCell.h b/Detectors/Base/include/DetectorsBase/MatCell.h index 88143ddf44b03..40c5fd3db1f69 100644 --- a/Detectors/Base/include/DetectorsBase/MatCell.h +++ b/Detectors/Base/include/DetectorsBase/MatCell.h @@ -31,7 +31,7 @@ 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) { @@ -55,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 229bc327d3039..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); @@ -91,10 +93,12 @@ class MatLayerCyl : public o2::gpu::FlatObject GPUd() const MatCell& getCell(int iphiSlice, int iz) const { return mCells[getCellID(iphiSlice, iz)]; } #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - GPUd() MatCell& getCellPhiBin(int iphi, int iz) + 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 diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index 3ffa7424ee61e..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); } @@ -85,7 +87,7 @@ class MatLayerCylSet : public o2::gpu::FlatObject void flatten(); MatLayerCyl& getLayer(int i) { return get()->mLayers[i]; } - MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3) const; + MatLayerCylSet* extractCopy(float rmin, float rmax, float tol = 1e-3, const MatLayerCylSet* toAdd = nullptr) const; void finalizeStructures(); #endif // !GPUCA_ALIGPUCODE @@ -96,6 +98,10 @@ 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; 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 a9e2ce6e0383d..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 @@ -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; @@ -157,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); @@ -165,6 +204,8 @@ 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 diff --git a/Detectors/Base/include/DetectorsBase/Ray.h b/Detectors/Base/include/DetectorsBase/Ray.h index 304ad5f00b03f..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); 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/src/Detector.cxx b/Detectors/Base/src/Detector.cxx index f2b790ffccd5b..d2be9237f6f13 100644 --- a/Detectors/Base/src/Detector.cxx +++ b/Detectors/Base/src/Detector.cxx @@ -171,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 diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index bb1aa42c66718..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 + ; @@ -41,4 +42,6 @@ #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 3e949412f8aa2..e7e5248493548 100644 --- a/Detectors/Base/src/GRPGeomHelper.cxx +++ b/Detectors/Base/src/GRPGeomHelper.cxx @@ -63,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); @@ -73,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()) { @@ -124,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)) { @@ -159,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 @@ -219,12 +235,20 @@ void GRPGeomHelper::checkUpdates(ProcessingContext& pc) const for (auto id = DetID::First; id <= DetID::Last; id++) { std::string binding = fmt::format("align{}", DetID::getName(id)); if (pc.inputs().getPos(binding.c_str()) < 0) { - return; + continue; } else { 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::vectormNLayers - 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 @@ -581,8 +608,12 @@ void MatLayerCylSet::fixPointers(char* oldPtr, char* newPtr, bool newPtrValid) #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version -MatLayerCylSet* MatLayerCylSet::extractCopy(float rmin, float rmax, float tolerance) const +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)) { @@ -591,23 +622,37 @@ MatLayerCylSet* MatLayerCylSet::extractCopy(float rmin, float rmax, float tolera } LOGP(info, "Will extract layers {}:{} (out of {} layers) for {} < r < {}", lmin, lmax, getNLayers(), rmin, rmax); MatLayerCylSet* copy = new MatLayerCylSet(); - int lrCount = 0; - for (int il = lmin; il <= lmax; il++) { - const auto& lr = getLayer(il); + 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(lrCount); + 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 0d1b53b695536..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; @@ -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) { @@ -189,14 +190,14 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va res = false; } if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length + 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.getP2Inv()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); } return res; }; @@ -217,6 +218,75 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, va return true; } +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // taking into account all the three components of the magnetic field + // and correcting for the crossed material. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + std::array b{}; + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); + getFieldXYZ(xyz0, &b[0]); + + 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()); + } + 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(); + } + track.setX(xToGo); + return true; +} + //_______________________________________________________________________ template GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value_type xToGo, value_type maxSnp, value_type maxStep, @@ -239,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) { @@ -258,14 +328,14 @@ GPUd() bool PropagatorImpl::PropagateToXBxByBz(TrackPar_t& track, value res = false; } if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length + 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.getP2Inv()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); } return res; }; @@ -294,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 @@ -324,14 +393,14 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty res = false; } if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length + 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.getP2Inv()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); } return res; }; @@ -351,6 +420,72 @@ GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, value_ty return true; } +//_______________________________________________________________________ +template +GPUd() bool PropagatorImpl::propagateToX(TrackParCov_t& track, TrackPar_t& linRef, value_type xToGo, value_type bZ, value_type maxSnp, value_type maxStep, + PropagatorImpl::MatCorrType matCorr, track::TrackLTIntegral* tofInfo, int signCorr) const +{ + //---------------------------------------------------------------- + // + // Propagates the track to the plane X=xk (cm), using linRef as a Kalman linearisation point. + // Use bz only and correct for the crossed material if requested. + // + // maxStep - maximal step for propagation + // tofInfo - optional container for track length and PID-dependent TOF integration + // + // matCorr - material correction type, it is up to the user to make sure the pointer is attached (if LUT is requested) + //---------------------------------------------------------------- + auto dx = xToGo - track.getX(); + int dir = dx > 0.f ? 1 : -1; + if (!signCorr) { + signCorr = -dir; // sign of eloss correction is not imposed + } + + while (math_utils::detail::abs(dx) > Epsilon) { + auto step = math_utils::detail::min(math_utils::detail::abs(dx), maxStep); + if (dir < 0) { + step = -step; + } + auto x = track.getX() + step; + auto xyz0 = linRef.getXYZGlo(); + + 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()); + } + 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(); + } + track.setX(xToGo); + return true; +} + //_______________________________________________________________________ template GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type xToGo, value_type bZ, value_type maxSnp, value_type maxStep, @@ -390,14 +525,14 @@ GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type res = false; } if (tofInfo) { - tofInfo->addStep(mb.length, track.getP2Inv()); // fill L,ToF info using already calculated step length + 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.getP2Inv()); + tofInfo->addStep(stepV.R(), track.getQ2P2()); } return res; }; @@ -418,6 +553,118 @@ GPUd() bool PropagatorImpl::propagateToX(TrackPar_t& track, value_type 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; + } + 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 + + // 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; + } + deltaPhi -= tgtPhiLoc - phiLoc; + phiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; + continue; // should be of for the case 1 now. + } + bz = getBz(math_utils::Point3D{value_type(cross.xDCA[sel]), value_type(cross.yDCA[sel]), value_type(track.getZ())}); + } + // 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; +} + //_______________________________________________________________________ template template @@ -468,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); @@ -488,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; @@ -517,6 +772,10 @@ 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(mNominalBz); @@ -537,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; @@ -553,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 @@ -566,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); @@ -587,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 @@ -614,6 +885,10 @@ 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(mNominalBz); @@ -635,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; @@ -717,7 +996,7 @@ GPUd() value_T PropagatorImpl::estimateLTFast(o2::track::TrackLTIntegra // since we assume the track or its parent comes from the beam-line or decay, add XY(?) distance to it value_T dcaT = math_utils::detail::sqrt(xdca * xdca + ydca * ydca); length += dcaT; - lt.addStep(length, trc.getP2Inv()); + lt.addStep(length, trc.getQ2P2()); return dcaT; } @@ -772,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 { @@ -784,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/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/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/buildMatBudLUT.C b/Detectors/Base/test/buildMatBudLUT.C index 800597d6166fd..85f8343a2d35d 100644 --- a/Detectors/Base/test/buildMatBudLUT.C +++ b/Detectors/Base/test/buildMatBudLUT.C @@ -23,7 +23,7 @@ #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; @@ -249,7 +249,9 @@ void configLayers() // 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 @@ -259,14 +261,14 @@ 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); 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); @@ -274,14 +276,14 @@ void configLayers() 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); 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); @@ -301,15 +303,20 @@ void configLayers() } while (lrData.back().rMax < 55. - kToler); zSpanH = 120.f; - lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH)); - lrData.emplace_back(LrData(lrData.back().rMax, 60.5, zSpanH)); - lrData.emplace_back(LrData(lrData.back().rMax, 61.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 = 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); @@ -335,7 +342,7 @@ void configLayers() 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/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/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/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index e9bd0f7249ef1..4e259c24f44a6 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -32,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 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/EntropyDecoderSpec.cxx b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx index 7c14dc70dd430..518a646e23cb9 100644 --- a/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CPV/workflow/src/EntropyDecoderSpec.cxx @@ -25,7 +25,7 @@ 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(); @@ -74,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}, @@ -83,16 +83,17 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_CPV", "CPV", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_CPV", "CPV", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CPV/Calib/CTFDictionaryTree")); + 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"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + 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 31ed720e66335..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(); @@ -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/CTFDictionaryTree")); + 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,9 +86,8 @@ 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"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; 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 48fa78c896a86..47ce765de289a 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -95,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) ``` @@ -136,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 ``` @@ -143,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 @@ -196,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 e4b91569d1df3..34a383a6875a0 100644 --- a/Detectors/CTF/test/test_ctf_io_cpv.cxx +++ b/Detectors/CTF/test/test_ctf_io_cpv.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include using namespace o2::cpv; diff --git a/Detectors/CTF/workflow/CMakeLists.txt b/Detectors/CTF/workflow/CMakeLists.txt index b4fefa894263c..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 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 681547f9a814c..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,16 +100,21 @@ 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; @@ -129,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, spent {:.2} s in {} data waiting states", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); + 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(); @@ -145,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); @@ -159,9 +180,116 @@ 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(); +} + ///_______________________________________ void CTFReaderSpec::openCTFFile(const std::string& flname) { @@ -178,6 +306,27 @@ void CTFReaderSpec::openCTFFile(const std::string& 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(); mCTFTree.reset(); @@ -201,9 +350,12 @@ void CTFReaderSpec::run(ProcessingContext& pc) 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; @@ -252,6 +404,17 @@ void CTFReaderSpec::run(ProcessingContext& pc) 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); + } } } @@ -274,7 +437,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) } if (mUseLocalTFCounter) { - ctfHeader.tfCounter = mCTFCounter; + ctfHeader.tfCounter = mCTFCounterAcc; } LOG(info) << ctfHeader; @@ -285,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); @@ -325,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) { @@ -359,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(); @@ -462,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/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index ef3a0f8d3c2c4..fc50c971c5d20 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -52,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 @@ -67,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"}}); @@ -116,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"); @@ -128,7 +130,10 @@ 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")); @@ -136,6 +141,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (rateLimitingIPCID > -1 && !chanFmt.empty()) { 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)); @@ -172,52 +183,52 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // add decoders for all allowed detectors. if (ctfInput.detMask[DetID::ITS]) { - addSpecs(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]) { - addSpecs(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]) { - addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TRD]) { - addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::trd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TOF]) { - addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::tof::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FT0]) { - addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ft0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FV0]) { - addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fv0::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::FDD]) { - addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::fdd::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MID]) { - addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::mid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::MCH]) { - addSpecs(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]) { - addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC)); + addSpecs(o2::emcal::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.decSSpecEMC, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::PHS]) { - addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::phos::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CPV]) { - addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::cpv::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::ZDC]) { - addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::zdc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::HMP]) { - addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::hmpid::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::CTP]) { - addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec)); + addSpecs(o2::ctp::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); } bool combine = configcontext.options().get("combine-devices"); diff --git a/Detectors/CTP/macro/CMakeLists.txt b/Detectors/CTP/macro/CMakeLists.txt index b4f1dce887d35..8608c1a8b7846 100644 --- a/Detectors/CTP/macro/CMakeLists.txt +++ b/Detectors/CTP/macro/CMakeLists.txt @@ -65,4 +65,23 @@ 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/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 bed4469dd566d..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 @@ -79,7 +80,7 @@ ferst 1 \n\ std::cout << "CTP config done" << std::endl; ctpcfg.checkConfigConsistency(); if (0) { - CTPRunManager* man = new CTPRunManager; + o2::ctp::ctpCCDBManager* man = new ctpCCDBManager; man->setCCDBHost("http://ccdb-test.cern.ch:8080"); man->saveRunConfigToCCDB(&ctpcfg, 1665784953); // uint64_t classmask = ctpcfg.getClassMaskForInputMask(0x4); diff --git a/Detectors/CTP/macro/GetAndSave.C b/Detectors/CTP/macro/GetAndSave.C index 1824726749575..ff70a3055c957 100644 --- a/Detectors/CTP/macro/GetAndSave.C +++ b/Detectors/CTP/macro/GetAndSave.C @@ -19,6 +19,7 @@ #include "CCDB/BasicCCDBManager.h" #include "DataFormatsCTP/Scalers.h" #include "DataFormatsCTP/Configuration.h" +#include "CTPWorkflowScalers/ctpCCDBManager.h" #include "TFile.h" #include "TString.h" #include @@ -36,14 +37,14 @@ void GetAndSave(std::string ccdbHost = "http://ccdb-test.cern.ch:8080") // std::vector runs = {"518543"}; // std::vector timestamps = {1655118513690}; int i = 0; - CTPRunManager mng; + ctpCCDBManager mng; // mng.setCCDBHost(ccdbHost); auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(ccdbHost); for (auto const& run : runs) { CTPConfiguration ctpcfg; CTPRunScalers scl; - map metadata; // can be empty + std::map metadata; // can be empty metadata["runNumber"] = run; CTPRunScalers* ctpscalers = mgr.getSpecific(CCDBPathCTPScalers, timestamps[i], metadata); if (ctpscalers == nullptr) { 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 05fce0b657f5a..1f104850c8c39 100644 --- a/Detectors/CTP/macro/GetScalers.C +++ b/Detectors/CTP/macro/GetScalers.C @@ -20,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 @@ -34,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); diff --git a/Detectors/CTP/macro/PlotPbLumi.C b/Detectors/CTP/macro/PlotPbLumi.C index 6ffa1dd4cee2b..4bda8d25e006e 100644 --- a/Detectors/CTP/macro/PlotPbLumi.C +++ b/Detectors/CTP/macro/PlotPbLumi.C @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file TestCTPScalers.C +/// \file PlotPbLumi.C /// \brief create CTP scalers, test it and add to database /// \author Roman Lietava -// root -b -q "GetScalers.C(\"519499\", 1656286373953)" +// root "PLotPbLumi.C(519499)" #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -20,17 +20,31 @@ #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; -void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-test.cern.ch:8080") +// +// 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.) { // - // what = 1: znc rate - // what = 2: (TCE+TSC)/ZNC - // what = 3: TCE/ZNC - std::string mCCDBPathCTPScalers = "CTP/Calib/Scalers"; + // 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 @@ -38,20 +52,26 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te uint64_t timeStamp = (soreor.second - soreor.first) / 2 + soreor.first; std::cout << "Timestamp:" << timeStamp << std::endl; // Filling - std::string sfill = std::to_string(fillN); - std::map metadata; - metadata["fillNumber"] = sfill; - auto lhcifdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + 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); - metadata.clear(); // can be empty + std::map metadata; metadata["runNumber"] = srun; - ccdbMgr.setURL("http://ccdb-test.cern.ch:8080"); - auto scl = ccdbMgr.getSpecific(mCCDBPathCTPScalers, timeStamp, metadata); + 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; @@ -60,6 +80,7 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te 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; @@ -80,6 +101,7 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te 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(); @@ -99,6 +121,12 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te // 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; @@ -115,11 +143,39 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te double_t orbit0 = recs[0].intRecord.orbit; int n = recs.size() - 1; std::cout << " Run duration:" << Trun << " Scalers size:" << n + 1 << std::endl; - Double_t x[n], znc[n], zncpp[n]; - Double_t tcetsctoznc[n], tcetoznc[n], vchtoznc[n]; - for (int i = 0; i < n; i++) { - x[i] = (double_t)(recs[i + 1].intRecord.orbit + recs[i].intRecord.orbit) / 2. - orbit0; - x[i] *= 88e-6; + // + 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; @@ -128,33 +184,53 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te 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[i] = zncipp / 28.; - znc[i] = znci / 28. / tt; + 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; // - auto had = recs[i + 1].scalers[tce].lmBefore - recs[i].scalers[tce].lmBefore; + 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[i] = (double_t)(had) / zncpp[i] / tt; + 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[i] = (double_t)(had) / zncpp[i] / tt; + 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[i] = (double_t)(had) / zncpp[i] / tt; + 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); - gr1->SetTitle("R=ZNC/28 rate [Hz]; time[sec]; R"); - gr2->SetTitle("R=(TSC+TCE)*TVTX*B*28/ZNC; time[sec]; R"); + 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"); @@ -163,10 +239,17 @@ void PlotPbLumi(int runNumber, int fillN, std::string ccdbHost = "http://ccdb-te 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); - gr1->Draw("AP"); + mg1->Draw("AP"); c1->cd(2); gr2->Draw("AP"); c1->cd(3); 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 48360996d3439..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,9 +23,9 @@ void TestConfig(bool test = 0) } uint64_t timestamp = 1660196771632; std::string run = "523148"; - o2::ctp::CTPRunManager::setCCDBHost("https://alice-ccdb.cern.ch"); + o2::ctp::ctpCCDBManager::setCCDBHost("https://alice-ccdb.cern.ch"); bool ok; - auto ctpcfg = o2::ctp::CTPRunManager::getConfigFromCCDB(timestamp, run, ok); + auto ctpcfg = o2::ctp::ctpCCDBManager::getConfigFromCCDB(timestamp, run, ok); if (ok == 0) { std::cout << "Can not get config for run:" << run << std::endl; } 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/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 6ffb3575207e5..8dbc5adadbfc5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -25,6 +25,7 @@ #include "DetectorsBase/CTFCoderBase.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 @@ -53,6 +54,9 @@ class CTFCoder : public o2::ctf::CTFCoderBase 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: @@ -62,6 +66,7 @@ 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; }; @@ -215,8 +220,13 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l } } if (mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMask(); + } + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; o2::pmr::vector digits; - o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit); + o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit, trgclassmask); for (auto const& dig : digits) { data.emplace_back(dig); } diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h index e38e1fb027362..53addf32c538f 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h @@ -22,6 +22,7 @@ #include "Framework/InputRecord.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" namespace o2 { @@ -35,6 +36,7 @@ class RawDataDecoder 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; } @@ -42,20 +44,31 @@ class RawDataDecoder 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); + 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; @@ -68,16 +81,23 @@ class RawDataDecoder gbtword80_t mTVXMask = 0x4; // TVX is 3rd input gbtword80_t mVBAMask = 0x20; // VBA is 6 th input bool mVerbose = false; - uint32_t mIRRejected = 0; - uint32_t mTCRRejected = 0; + 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 = 3; + 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 diff --git a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx index 88bf5ecf798af..a062a262acf62 100644 --- a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx +++ b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx @@ -17,6 +17,8 @@ #include "DataFormatsCTP/TriggerOffsetsParam.h" #include "CTPReconstruction/RawDataDecoder.h" #include "DataFormatsCTP/Configuration.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include using namespace o2::ctp; @@ -77,17 +79,17 @@ int RawDataDecoder::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& d if (mErrorIR < mErrorMax) { LOG(error) << "Two CTP IRs with the same timestamp:" << ir.bc << " " << ir.orbit << " pld:" << pld << " dig:" << digits[ir]; } - ret = 2; + ret = 4; mErrorIR++; mStickyError = true; } } else { LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; - ret = 2; + 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 - 1; + 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; @@ -111,11 +113,11 @@ int RawDataDecoder::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& d mStickyError = true; } mErrorTCR++; - ret = 3; + ret = 16; } } else { LOG(error) << "Two digits with the same timestamp:" << ir.bc << " " << ir.orbit; - ret = 3; + ret = 32; } } else { LOG(error) << "Unxpected CTP CRU link:" << linkCRU; @@ -174,6 +176,7 @@ int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector> 8; + // LOG(info) << "CRU link:" << linkCRU; if (linkCRU == o2::ctp::GBTLinkIDIntRec) { payloadCTP = o2::ctp::NIntRecPayload; } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { @@ -290,7 +293,18 @@ int RawDataDecoder::decodeRaw(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; @@ -350,20 +524,18 @@ int RawDataDecoder::shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit digmap[ir] = digit; } } else { - LOG(info) << "LOST:" << irin << " shift:" << shift; + // LOG(info) << "LOST:" << irin << " shift:" << shift; + return 1; + ; } return 0; } // -int RawDataDecoder::shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit) +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 nLM = 0; - int nL0 = 0; - int nL1 = 0; - int nTwI = 0; - int nTwoI = 0; + int lost = 0; std::map digitsMapShifted; auto L0shift = o2::ctp::TriggerOffsetsParam::Instance().LM_L0; auto L1shift = L0shift + o2::ctp::TriggerOffsetsParam::Instance().L0_L1; @@ -382,7 +554,7 @@ int RawDataDecoder::shiftInputs(std::map& digit if (lut == 0 || lut == 1) { // no inps or LM digitsMapShifted[dig.first] = dig.second; } else if (lut == 2) { // L0 - shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + 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) @@ -390,30 +562,30 @@ int RawDataDecoder::shiftInputs(std::map& digit digitsMapShifted[dig.first] = digi; } } else if (lut == 4) { // L1 - shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, 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 == 6) { // L0 and L1 - shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); - shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + 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 - shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); + 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 - shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + 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 - shiftNew(dig.first, TFOrbit, inpmask, L0shift, 0, digitsMapShifted); - shiftNew(dig.first, TFOrbit, inpmask, L1shift, 1, digitsMapShifted); + 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 { @@ -421,29 +593,78 @@ int RawDataDecoder::shiftInputs(std::map& digit } } for (auto const& dig : digitsMapShifted) { - auto d = dig.second; - if ((d.CTPInputMask & LMMASKInputs).count()) { - nLM++; - } - if ((d.CTPInputMask & L0MASKInputs).count()) { - nL0++; - } - if ((d.CTPInputMask & L1MASKInputs).count()) { - nL1++; + 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 (d.CTPClassMask.count()) { - if (d.CTPInputMask.count()) { - nTwI++; - } else { - nTwoI++; + // 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++; + } + } + } } } - digits.push_back(dig.second); - } - if (nTwoI) { // Trigger class wo Input - LOG(error) << "LM:" << nLM << " L0:" << nL0 << " L1:" << nL1 << " TwI:" << nTwI << " Trigger classes wo input:" << nTwoI; } - return 0; + return ret; } // int RawDataDecoder::setLumiInp(int lumiinp, std::string inp) diff --git a/Detectors/CTP/simulation/src/Digitizer.cxx b/Detectors/CTP/simulation/src/Digitizer.cxx index 7a16483594400..b1d4ef40b7b0e 100644 --- a/Detectors/CTP/simulation/src/Digitizer.cxx +++ b/Detectors/CTP/simulation/src/Digitizer.cxx @@ -43,6 +43,7 @@ std::vector Digitizer::process(const gsl::span std::vector digits; for (auto const& hits : predigits) { std::bitset inpmaskcoll = 0; + auto currentIR = hits.first; for (auto const inp : hits.second) { switch (inp->detector) { case o2::detectors::DetID::FT0: { @@ -81,16 +82,16 @@ std::vector Digitizer::process(const gsl::span std::bitset emcMBaccept; emcMBaccept.set(CTP_NINPUTS - 1, 1); inpmaskcoll |= emcMBaccept; - } else { - for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::EMC]) { - uint64_t mask = inpmaskdebug & detInputName2Mask[ctpinp.name]; - // uint64_t mask = (inp->inputsMask).to_ullong() & detInputName2Mask[ctpinp.name]; - if (mask) { - inpmaskcoll |= std::bitset(ctpinp.inputMask); - } + } // else { // needs to be done always, remove else + for (auto const& ctpinp : det2ctpinp[o2::detectors::DetID::EMC]) { + 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; + // } + // LOG(info) << "EMC input mask:" << inpmaskcoll << " with IR = " << currentIR.bc << ", orbit = " << currentIR.orbit; break; } case o2::detectors::DetID::PHS: { @@ -132,7 +133,8 @@ std::vector Digitizer::process(const gsl::span data.CTPInputMask = inpmaskcoll; data.CTPClassMask = classmask; digits.emplace_back(data); - LOG(info) << "Trigger-Event " << data.intRecord.bc << " " << data.intRecord.orbit << " Input mask:" << inpmaskcoll; + 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; } } return std::move(digits); @@ -151,24 +153,23 @@ void Digitizer::calculateClassMask(const std::bitset ctpinpmask, st if (clustername == "emc") { tvxMBemc |= tcl.name.find("minbias_TVX_L0") != std::string::npos; // 2022 } - if (tvxMBemc || (ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask()) { - // 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 { - // 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 + // 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 ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask()) { + if (tcl.descriptor && ((ctpinpmask.to_ullong() & tcl.descriptor->getInputsMask()) == tcl.descriptor->getInputsMask())) { classmask |= tcl.classMask; } } @@ -193,7 +194,7 @@ o2::ctp::CTPConfiguration* Digitizer::getDefaultCTPConfiguration() } auto& mgr = o2::ccdb::BasicCCDBManager::instance(); mgr.setURL(mCCDBServer); - map metadata = {}; + std::map metadata = {}; long timestamp = 1546300800000; auto config = mgr.getSpecific(o2::ctp::CCDBPathCTPConfig, timestamp, metadata); 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 607491b5cb48a..3198e5c33e219 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h @@ -16,6 +16,7 @@ #include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/WorkflowSpec.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" #include "CTPReconstruction/RawDataDecoder.h" @@ -50,6 +51,7 @@ 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; + void updateTimeDependentParams(framework::ProcessingContext& pc); protected: private: @@ -68,9 +70,21 @@ class RawDecoderSpec : public framework::Task uint32_t mNTFToIntegrate = 1; uint32_t mNHBIntegratedT = 0; uint32_t mNHBIntegratedV = 0; + bool mDecodeinputs = 0; std::deque mHistoryT; std::deque mHistoryV; RawDataDecoder mDecoder; + // Errors + int mLostDueToShiftInps = 0; + int mErrorIR = 0; + int mErrorTCR = 0; + int mIRRejected = 0; + int mTCRRejected = 0; + std::array mClsEA{}; + std::array mClsEB{}; // from inputs + std::array mClsA{}; + std::array mClsB{}; // from inputs + bool mCheckConsistency = false; }; /// \brief Creating DataProcessorSpec for the CTP diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index 8f3da5f439f80..0fa8fb0004e4c 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ 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(); @@ -55,9 +54,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc, true); + updateTimeDependentParams(pc); auto buff = pc.inputs().get>("ctf_CTP"); - auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& lumi = pc.outputs().make(OutputRef{"CTPLumi"}); @@ -76,8 +74,22 @@ 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}, @@ -86,18 +98,19 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_CTP", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); - 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")); + 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"}}, - {"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, + 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 44e64d7505977..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(); @@ -77,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("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); } - inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -92,13 +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"}}, {"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 df0988c871196..041e6cb472ebb 100644 --- a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx @@ -13,20 +13,23 @@ #include #include "Framework/InputRecordWalker.h" #include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" #include "DetectorsRaw/RDHUtils.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) { - bool decodeinps = ctx.options().get("ctpinputs-decoding"); - mDecoder.setDecodeInps(decodeinps); + 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"); int maxerrors = ctx.options().get("print-errors-num"); @@ -42,7 +45,7 @@ void RawDecoderSpec::init(framework::InitContext& ctx) 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:" << decodeinps << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal; + 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) @@ -68,11 +71,27 @@ void RawDecoderSpec::endOfStream(framework::EndOfStreamContext& ec) o0 = TFOrbits[i]; } std::cout << std::endl; - std::cout << "Number of missing TF:" << nmiss << std::endl; - std::cout << "# of IR errors:" << mDecoder.getErrorIR() << " TCR errors:" << mDecoder.getErrorTCR() << 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; @@ -112,6 +131,7 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) // std::vector lumiPointsHBF1; 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)) { @@ -120,15 +140,22 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) } if (payloadSize > (size_t)mMaxInputSize) { if (mMaxInputSizeFatal) { - LOG(fatal) << "Input data size:" << payloadSize; + 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; + LOG(error) << "Input data size:" << payloadSize << " sending dummy output"; + dummyOutput(); + return; } - dummyOutput(); - return; } } - int ret = mDecoder.decodeRaw(inputs, filter, mOutputDigits, lumiPointsHBF1); + int ret = 0; + if (fatal_flag) { + ret = mDecoder.decodeRawFatal(inputs, filter); + } else { + ret = mDecoder.decodeRaw(inputs, filter, mOutputDigits, lumiPointsHBF1); + } if (ret == 1) { dummyOutput(); return; @@ -136,6 +163,21 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) if (mDoDigits) { 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]; + } } if (mDoLumi) { uint32_t tfCountsT = 0; @@ -168,6 +210,7 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) mOutputLumiInfo.orbit = lumiPointsHBF1[0].orbit; } mOutputLumiInfo.counts = mCountsT; + mOutputLumiInfo.countsFV0 = mCountsV; mOutputLumiInfo.nHBFCounted = mNHBIntegratedT; mOutputLumiInfo.nHBFCountedFV0 = mNHBIntegratedV; @@ -190,6 +233,8 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool } 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); } @@ -208,6 +253,20 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool {"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 on;y"}}, + {"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/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/src/DigitReaderSpec.cxx b/Detectors/CTP/workflowIO/src/DigitReaderSpec.cxx index ecf1c2e19b660..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}, 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); + 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/workflowScalers/CMakeLists.txt b/Detectors/CTP/workflowScalers/CMakeLists.txt index 672bf328212cb..f02a7f33e2abd 100644 --- a/Detectors/CTP/workflowScalers/CMakeLists.txt +++ b/Detectors/CTP/workflowScalers/CMakeLists.txt @@ -8,12 +8,20 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +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 @@ -26,3 +34,11 @@ o2_add_executable( 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/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/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 index 13c06730d18ce..aa953e89264ef 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-ccdb-orbit.cxx @@ -57,6 +57,7 @@ int main(int argc, char** argv) 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); @@ -75,11 +76,15 @@ int main(int argc, char** argv) 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; - auto now = std::chrono::system_clock::now(); - long tt = std::chrono::duration_cast(now.time_since_epoch()).count(); + 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 @@ -115,11 +120,16 @@ int main(int argc, char** argv) 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 << std::endl; - api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + 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 << std::endl; - api.storeAsTFileAny(&(vect), ccdbPath, metadata, tmin, tmax); + 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); } } // @@ -128,7 +138,7 @@ int main(int argc, char** argv) TFile* f = TFile::Open(file.c_str(), "RECREATE"); if (f == nullptr) { std::cout << "Error: File" << file << " could not be open for writing !!!" << std::endl; - return 1; + ret++; } else { std::cout << "File" << file << " being writen." << std::endl; f->WriteObject(&vect, "ccdb_object"); @@ -137,5 +147,5 @@ int main(int argc, char** argv) } else { std::cout << "No file created" << std::endl; } - return 0; + return ret; } diff --git a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx index 1b90eb8c23d9d..391d1b5ccf58b 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx @@ -40,18 +40,23 @@ #include #include #include "CommonUtils/StringUtils.h" -#include "DataFormatsCTP/RunManager.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(); + // 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 @@ -62,7 +67,15 @@ InjectorFunction dcs2dpl(std::string& ccdbhost) 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 << " # parts:" << parts.Size(); // << " 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; }; @@ -73,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" @@ -95,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"); } @@ -109,7 +130,7 @@ 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; 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). 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/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 f3ea99442efbf..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->requestPrimaryVertices(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 8a1a071a0a043..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}, collectedInfo); - output.snapshot(Output{o2::header::gDataOriginFT0, "ENTRIESCH", 0}, 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 96% rename from Detectors/FIT/FT0/calibration/testWorkflow/FT0TimeSpectraProcessor-Workflow.cxx rename to Detectors/FIT/FT0/calibration/workflow/FT0TimeSpectraProcessor-Workflow.cxx index f1dd64a250a3b..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); 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/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 4d749dbc90b42..5dc367204e1a3 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -34,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 diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h index ff3f8384f488d..9f6cd500b9e74 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h @@ -21,6 +21,7 @@ #include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" #include "DataFormatsFT0/SpectraInfoObject.h" #include "DataFormatsFT0/SlewingCoef.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include #include #include @@ -57,10 +58,16 @@ class CollisionTimeRecoTask 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; + const o2::fit::DeadChannelMap* mDeadChannelMap = nullptr; typename o2::ft0::SlewingCoef::SlewingPlots_t mCalibSlew{}; }; } // namespace ft0 diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index 2610131ff51a7..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -58,25 +58,41 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, constexpr int nMCPsA = 4 * Geometry::NCellsA; 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++; } + const bool isOkForTimeCalc = TimeFilterParam::Instance().checkAll(channelData); // only signals which satisfy conditions may participate in time calculation - if (TimeFilterParam::Instance().checkAll(channelData)) { - if (channelData.ChId < nMCPsA) { + if (channelData.ChId < nMCPsA) { + // A-side + if (isOkForTimeCalc) { sideAtime += timeInPS; ndigitsA++; - } else { + } + 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}; @@ -90,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() diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx index a261475df31f5..aca012f1bc5a9 100644 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -220,7 +220,7 @@ void Digitizer::process(const std::vector* hits, // 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; + Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC(); if (hit_time > 150) { continue; // not collect very slow particles @@ -285,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/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/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 9074f4f7f0f34..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::Timeframe}}; - 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 f7729394db652..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}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, 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}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h index 3c6e4599a250c..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 useTimeOffsetCalib, bool useSlewingCalib, 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 1c671352e6ba7..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, bool useTimeOffsetCalib, bool useSlewingCalib) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib) {} + 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; @@ -46,6 +46,8 @@ class ReconstructionDPL : public Task 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; @@ -55,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", bool useTimeOffsetCalib = true, bool useSlewingCalib = true); +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/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 65d3585350888..066c5cc547c2e 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ 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(); @@ -73,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}, @@ -82,16 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FT0", "FT0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FT0", "FT0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/CTFDictionaryTree")); + + 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"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + 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 81bdc2e729bb4..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(); @@ -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/CTFDictionaryTree")); + + 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,13 +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"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace ft0 } // namespace o2 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/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx index 247158164ac3b..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 useTimeOffsetCalib, bool useSlewingCalib, 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, useTimeOffsetCalib, useSlewingCalib)); + 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 40bc96ebca58e..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) @@ -69,6 +70,12 @@ void ReconstructionDPL::run(ProcessingContext& pc) 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); @@ -91,6 +98,11 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) 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; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -99,12 +111,13 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib) +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); @@ -121,6 +134,11 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, 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); @@ -128,7 +146,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, "ft0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib)}, + 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 3e6a6bf5da090..ab39068aedb38 100644 --- a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -41,7 +41,8 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, {"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"}}}; + {"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); } @@ -64,9 +65,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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, useTimeOffsetCalib, useSlewingCalib, 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 d323b4135d7ea..b1d824e10687e 100644 --- a/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/recpoints-reader-workflow.cxx @@ -9,23 +9,24 @@ // 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/CallbacksPolicy.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) +void customize(std::vector& policies) { o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); } @@ -33,12 +34,9 @@ void customize(std::vector& 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); } @@ -47,10 +45,13 @@ 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/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/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/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 cbec444ef11be..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 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/simulation/include/FV0Simulation/Digitizer.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/Digitizer.h index 6956d8126ce53..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, @@ -132,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/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 1c94b14f029cf..8c1d2dc8824e2 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -98,6 +98,11 @@ void Digitizer::process(const std::vector& hits, for (auto ids : hitIdx) { const auto& hit = hits[ids]; Int_t detId = hit.GetDetectorID(); + + 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 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/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 9310905ad41b9..7babe9fdea6ed 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -24,8 +24,7 @@ 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(); @@ -73,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}, @@ -82,17 +81,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_FV0", "FV0", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_FV0", "FV0", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/CTFDictionaryTree")); + + 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"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + 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 a25c16a5d697c..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(); @@ -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/CTFDictionaryTree")); + + 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,13 +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"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace fv0 } // namespace o2 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 520ac4dbaa563..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,18 +55,19 @@ 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"; @@ -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/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 9d366cf04e297..a6bf1799a5dde 100644 --- a/Detectors/FIT/macros/CMakeLists.txt +++ b/Detectors/FIT/macros/CMakeLists.txt @@ -40,4 +40,14 @@ o2_add_test_root_macro(readFITDCSdata.C O2::CCDB LABELS fit) -o2_data_file(COPY readFITDCSdata.C DESTINATION Detectors/FIT/macros/readFITDCSdata.C) \ No newline at end of file +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/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/FOCAL/base/include/FOCALBase/Geometry.h b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h index 4938ebb1925dd..3414d84b5298f 100644 --- a/Detectors/FOCAL/base/include/FOCALBase/Geometry.h +++ b/Detectors/FOCAL/base/include/FOCALBase/Geometry.h @@ -59,6 +59,12 @@ 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; @@ -134,6 +140,11 @@ class Geometry 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; @@ -155,7 +166,7 @@ class Geometry void setUpLayerSegmentMap(); void setUpTowerWaferSize(); - bool getUseHCALSandwich() { return mUseSandwichHCAL; } + HCALDesgin getHCALDesign() const { return mHCALDesign; } protected: std::vector mGeometryComposition; @@ -175,6 +186,9 @@ class Geometry 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 @@ -200,9 +214,11 @@ class Geometry 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 - bool mUseSandwichHCAL = false; + 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; diff --git a/Detectors/FOCAL/base/include/FOCALBase/Hit.h b/Detectors/FOCAL/base/include/FOCALBase/Hit.h index 31d6054386b36..e24c48ac60284 100644 --- a/Detectors/FOCAL/base/include/FOCALBase/Hit.h +++ b/Detectors/FOCAL/base/include/FOCALBase/Hit.h @@ -14,6 +14,7 @@ #include #include "SimulationDataFormat/BaseHits.h" #include "CommonUtils/ShmAllocator.h" +#include namespace o2::focal { @@ -35,6 +36,39 @@ class Hit : public o2::BasicXYZEHit 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; diff --git a/Detectors/FOCAL/base/src/Geometry.cxx b/Detectors/FOCAL/base/src/Geometry.cxx index 94d8c2cee049c..2699ab5c7d602 100644 --- a/Detectors/FOCAL/base/src/Geometry.cxx +++ b/Detectors/FOCAL/base/src/Geometry.cxx @@ -351,11 +351,26 @@ void Geometry::setParameters(std::string geometryfile) 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; @@ -366,6 +381,11 @@ void Geometry::setParameters(std::string geometryfile) 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; @@ -419,11 +439,6 @@ void Geometry::setParameters(std::string geometryfile) if (command.find("NUMBER_OF_HCAL_LAYERS") != std::string::npos) { mNHCalLayers = std::stoi(tokens[1]); LOG(debug) << "Number of HCAL layers " << mNHCalLayers; - if (mNHCalLayers == 1) { - mUseSandwichHCAL = false; - } else { - mUseSandwichHCAL = true; - } } if (command.find("NUMBER_OF_SEGMENTS") != std::string::npos) { @@ -578,8 +593,8 @@ void Geometry::setParameters(std::string geometryfile) } } } // end for itowerY - } // end for itowerX - } // end else + } // 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)"; @@ -630,6 +645,14 @@ void Geometry::setParameters(std::string geometryfile) 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 "; } @@ -692,27 +715,34 @@ std::tuple Geometry::getGeoTowerCenter(int tower, int se int ix = id % nCols; int iy = id / nRows; - if (mUseSandwichHCAL) { - 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; - } else { - nCols = std::floor(getFOCALSizeX() / getHCALTowerSize() + 0.001) + 1; - nRows = std::floor(getFOCALSizeY() / getHCALTowerSize() + 0.001); - ix = id % nCols; - iy = id / nRows; - 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; - - 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; - if (y < minRadius && y > -minRadius) { - x = int(x) <= 0 ? x - (minRadius - towerSize) : x + (minRadius - towerSize); + 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; } } @@ -1108,12 +1138,41 @@ std::tuple Geometry::getVirtualInfo(double x, double y x = x < 0 ? x - 0.001 : x + 0.001; y = y < 0 ? y - 0.001 : y + 0.001; } - if (!mUseSandwichHCAL) { - row = (int)((y + hCALsizeY / 2) / (towerSize / 7)); - col = (int)((x + hCALsizeX / 2) / (towerSize / 7)); - } else { - row = (int)((y + hCALsizeY / 2) / (towerSize)); - col = (int)((x + hCALsizeX / 2) / (towerSize)); + + 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); @@ -1140,12 +1199,29 @@ std::tuple Geometry::getXYZFromColRowSeg(int col, double hCALsizeX = getHCALTowersInX() * towerSize; double hCALsizeY = getHCALTowersInY() * towerSize; - if (!mUseSandwichHCAL) { - y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize / 7); - x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (towerSize / 7); - } else { - y = -1 * hCALsizeY / 2 + ((float)row + 0.5) * (towerSize); - x = -1 * hCALsizeX / 2 + ((float)col + 0.5) * (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; @@ -1181,12 +1257,24 @@ std::tuple Geometry::getVirtualNColRow(int segment) const nCol = (int)(getFOCALSizeX() / mVirtualSegmentComposition[segment].mPadSize + 0.001); nRow = (int)(getFOCALSizeY() / mVirtualSegmentComposition[segment].mPadSize + 0.001); if (getVirtualIsHCal(segment)) { - if (!mUseSandwichHCAL) { - 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) - } else { - nCol = getHCALTowersInX(); - nRow = getHCALTowersInY(); + 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}; diff --git a/Detectors/FOCAL/simulation/data/simcuts.dat b/Detectors/FOCAL/simulation/data/simcuts.dat index 1ef7260bc34be..870e38182f01c 100644 --- a/Detectors/FOCAL/simulation/data/simcuts.dat +++ b/Detectors/FOCAL/simulation/data/simcuts.dat @@ -6,11 +6,13 @@ 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 2 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 +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 5 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 +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 10 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 -* G10 plate -FOC 12 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 +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_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 index ff24f0bfec34a..31f6940224337 100644 --- a/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt +++ b/Detectors/FOCAL/simulation/geometryFiles/geometry_Spaghetti.txt @@ -58,7 +58,11 @@ COMMAND_INSERT_PIX_AT_L9 GLOBAL_TOWER_TOL 0. Air GLOBAL_TOWER_TOLX 0.02 Air GLOBAL_TOWER_TOLY 0.8 Al - GLOBAL_FOCAL_Z 764.47 + 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 diff --git a/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h index 1a66435fea9a2..5f6bed3a037b0 100644 --- a/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h +++ b/Detectors/FOCAL/simulation/include/FOCALSimulation/Detector.h @@ -17,6 +17,7 @@ #include "DetectorsBase/Detector.h" #include "FOCALBase/Hit.h" #include "FOCALBase/Geometry.h" +#include "TGeoManager.h" class FairVolume; @@ -54,18 +55,19 @@ class Detector : public o2::base::DetImpl { public: enum MediumType_t { ID_TUNGSTEN = 0, - ID_SILICON = 1, - ID_G10 = 2, - ID_COPPER = 3, - ID_STEEL = 4, - ID_ALLOY = 5, - ID_CERAMIC = 6, - ID_PB = 7, - ID_SC = 8, - ID_SIINSENS = 9, - ID_ALUMINIUM = 10, - ID_VAC = 11, - ID_AIR = 12 }; + 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; @@ -156,6 +158,17 @@ class Detector : public o2::base::DetImpl 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(); @@ -184,13 +197,14 @@ class Detector : public o2::base::DetImpl Geometry* mGeometry; //! mGeoCompositions; //!* mHits; ///< Container with hits + std::vector* mHits; ///< Container with hits + std::unordered_map mHitIndexMapping; ///< Mapping the hits to a cell in the detector std::unordered_map mSuperParentsIndices; //! mSuperParents; //!("FOC", active), mHits(o2::utils::createSimVector()), + mHitIndexMapping(), mGeometry(nullptr), mMedSensHCal(-1), mMedSensECalPad(-1), @@ -100,11 +101,8 @@ void Detector::InitializeO2Detector() } mMedSensHCal = getMediumID(ID_SC); - mMedSensECalPad = getMediumID(ID_SILICON); - - // - // TODO for Pixels - // + mMedSensECalPad = getMediumID(ID_SIPAD); + mMedSensECalPix = getMediumID(ID_SIPIX); } Bool_t Detector::ProcessHits(FairVolume* v) @@ -147,16 +145,18 @@ Bool_t Detector::ProcessHits(FairVolume* v) } // Processing ECAL Pad hits - bool flagECAL = true; + bool flagECALPad = true; if (TVirtualMC::GetMC()->CurrentMedium() == mMedSensECalPad) { - flagECAL = ProcessHitsEPad(v); + flagECALPad = ProcessHitsEPad(v); } - // - // TODO for Pixels - // + // Processing ECAL Pixel hits + bool flagECALPix = true; + if (fMC->CurrentMedium() == mMedSensECalPix) { + flagECALPix = ProcessHitsEPix(v); + } - return (flagHCAL || flagECAL); + return (flagHCAL || flagECALPad || flagECALPix); // return true; } @@ -166,23 +166,23 @@ Hit* Detector::AddHit(int trackID, int primary, double initialEnergy, int detID, 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); - auto HitComparison = [&](const Hit& hit) { - auto information = mGeometry->getVirtualInfo(hit.GetX(), hit.GetY(), hit.GetZ()); - // FIXME Should we compare segments instead of layers ??? - return hit.GetTrackID() == parentID && col == std::get<1>(information) && row == std::get<2>(information) && layer == std::get<3>(information); - }; - - auto result = std::find_if(mHits->begin(), mHits->end(), HitComparison); - if (result == mHits->end()) { + if (found == mHitIndexMapping.end()) { return nullptr; } - return &(*result); + + return &((*mHits)[found->second]); } Parent* Detector::AddSuperparent(int trackID, int pdg, double energy) @@ -205,6 +205,7 @@ void Detector::Reset() if (!o2::utils::ShmManager::Instance().isOperational()) { mHits->clear(); } + mHitIndexMapping.clear(); mSuperParentsIndices.clear(); mSuperParents.clear(); @@ -339,7 +340,10 @@ AliMixture( 8, "Ceramic $", acer, zcer, denscer, 2, wcer); 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_SILICON, "Si sens$", 1, 0, + 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]; @@ -402,7 +406,7 @@ void Detector::addAlignableVolumes() const //____________________________________________________________________________ void Detector::addAlignableVolumesHCAL() const { - const std::string vpsector = "/cave_1/caveRB24_1/FOCAL_1/HCAL_1"; + 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())) { @@ -413,7 +417,7 @@ void Detector::addAlignableVolumesHCAL() const //____________________________________________________________________________ void Detector::addAlignableVolumesECAL() const { - const std::string vpsector = "/cave_1/caveRB24_1/FOCAL_1/ECAL_1"; + 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())) { @@ -447,7 +451,7 @@ void Detector::ConstructGeometry() } float pars[4]; - pars[0] = (mGeometry->getFOCALSizeX() + 2 * mGeometry->getMiddleTowerOffset()) / 2; + 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 @@ -476,13 +480,25 @@ void Detector::ConstructGeometry() CreateECALGeometry(); // HCAL part - if (mGeometry->getUseHCALSandwich()) { - CreateHCALSandwich(); - } else { - CreateHCALSpaghetti(); + 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 caveRB24 mother volume - TVirtualMC::GetMC()->Gspos("FOCAL", 1, "caveRB24", 0, 0, mGeometry->getFOCALZ0() - (mGeometry->getInsertFrontPadLayers() ? 2.0 : 0.0) + (mGeometry->getInsertHCalReadoutMaterial() ? 1.5 : 0.0) - z0, 0, "ONLY"); + + // 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() @@ -535,6 +551,8 @@ void Detector::CreateHCALSpaghetti() } } + 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 @@ -594,25 +612,57 @@ void Detector::CreateHCALSpaghetti() Columns = 0; RowPos = 0.; Int_t NumTowers = 1; - 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.); + if (splitDet) { + SizeXHCAL = SizeXHCAL / 2; - // Remove the Towers that overlaps with the beam pipe - Double_t RadialDistance = TMath::Power(trans->GetTranslation()[0], 2) + TMath::Power(trans->GetTranslation()[1], 2); + TGeoVolumeAssembly* volHalfHCAL = new TGeoVolumeAssembly("HalfHCAL"); - if (RadialDistance < MinRadius * MinRadius || TMath::Abs(trans->GetTranslation()[0]) > SizeXHCAL / 2) { - continue; + 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++) { - // Adding the Tower to the HCAL - volHCAL->AddNode(volTowerHCAL, NumTowers, trans); + 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.); - NumTowers++; + // 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++; + } } } @@ -643,6 +693,219 @@ void Detector::CreateHCALSpaghetti() 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() { @@ -781,18 +1044,20 @@ void Detector::CreateECALGeometry() 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] = fGeom->GetFOCALSizeZ() / 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", ID_AIR, pars, 4); // Left towers (pixels shifted right) - TVirtualMC::GetMC()->Gsvolu("EMSC2", "BOX", ID_AIR, 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"); @@ -812,7 +1077,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "PureW") { // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3599], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", ID_TUNGSTEN, 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); @@ -823,7 +1088,7 @@ void Detector::CreateECALGeometry() } if (icomp->material() == "Alloy") { // TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", idtmed[3604], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EW1", "BOX", ID_ALLOY, 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", @@ -834,7 +1099,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "G10") { // TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", idtmed[3601], pars, 4); - TVirtualMC::GetMC()->Gsvolu("G10RO1", "BOX", ID_G10, 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); @@ -846,7 +1111,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Cu") { // TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", idtmed[3602], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWCU", "BOX", ID_COPPER, 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); @@ -858,7 +1123,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Air") { // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3698], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", ID_AIR, 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); @@ -870,7 +1135,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "Ceramic") { // TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", idtmed[3607], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWAIR1", "BOX", ID_CERAMIC, 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", @@ -881,7 +1146,7 @@ void Detector::CreateECALGeometry() if (icomp->material() == "SiPad") { // TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", idtmed[3600], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSIPAD1", "BOX", ID_SILICON, 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); @@ -896,7 +1161,7 @@ void Detector::CreateECALGeometry() // Pixels (sensitive layer) if (icomp->material() == "SiPix") { // TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", idtmed[3600], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSIPIX1", "BOX", ID_SILICON, 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); @@ -911,7 +1176,7 @@ void Detector::CreateECALGeometry() // Passive silicon if (icomp->material() == "Si") { // TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", idtmed[3610], pars, 4); - TVirtualMC::GetMC()->Gsvolu("EWSI1", "BOX", ID_SIINSENS, 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); @@ -928,12 +1193,12 @@ void Detector::CreateECALGeometry() if (geom->getTowerGapMaterial() == "Cu") { // Copper // if (contains(geom->getTowerGapMaterial(), "Cu")) { // Copper - volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium("FOCAL_Cu$")); + 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("FOCAL_AlPlate")); + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_ALUMINIUM))); } else { - volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium("FOCAL_AirGaps$")); + volumeColdPlate = new TGeoVolume("volColdPlate", coldPlateBox, gGeoManager->GetMedium(getMediumID(ID_AIR))); } // mSensitiveECALPad.push_back(volumeColdPlate->GetName()); mSensitive.push_back(volumeColdPlate->GetName()); @@ -944,23 +1209,25 @@ void Detector::CreateECALGeometry() // Place the towers in the ECAL // --- Place the ECAL in FOCAL float fcal_pars[4]; - fcal_pars[0] = (geom->getFOCALSizeX() + 2. * geom->getMiddleTowerOffset()) / 2.; + 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", ID_AIR, 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("FOCAL_SiSens$")); + TGeoVolume* volumeSiPad = new TGeoVolume("volSiPad", siPadBox, gGeoManager->GetMedium(getMediumID(ID_SIPAD))); volumeSiPad->SetLineColor(kOrange + 7); // mSensitiveECALPad.push_back(volumeSiPad->GetName()); - mSensitive.push_back(volumeSiPad->GetName()); + if (geom->getInsertFrontPadLayers()) { + mSensitive.push_back(volumeSiPad->GetName()); + } double xp, yp, zp; int itowerx, itowery; @@ -971,9 +1238,13 @@ void Detector::CreateECALGeometry() // 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); - const auto [xp, yp, zp] = geom->getGeoTowerCenter(number); // only ECAL part, second parameter = -1 by default + 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()) { @@ -986,6 +1257,10 @@ void Detector::CreateECALGeometry() } } 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()) { @@ -1115,9 +1390,49 @@ bool Detector::ProcessHitsHCAL(FairVolume* v) bool Detector::ProcessHitsEPix(FairVolume* v) { - LOG(debug) << "We are in sensitive volume " << v->GetName() << ": " << TVirtualMC::GetMC()->CurrentVolPath(); - // - // TODO: to be filled with body - // + 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/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index 847fa2cf7e1e5..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" diff --git a/Detectors/GLOQC/CMakeLists.txt b/Detectors/GLOQC/CMakeLists.txt index cdb307a6515c8..9d2db85460b69 100644 --- a/Detectors/GLOQC/CMakeLists.txt +++ b/Detectors/GLOQC/CMakeLists.txt @@ -15,7 +15,9 @@ o2_add_library(GLOQC SOURCES src/MatchITSTPCQC.cxx src/ITSTPCMatchingQCParams.cxx - PUBLIC_LINK_LIBRARIES O2::DetectorsVertexing) + PUBLIC_LINK_LIBRARIES O2::DetectorsVertexing + PRIVATE_LINK_LIBRARIES O2::GPUO2Interface + O2::GPUTracking) o2_target_root_dictionary(GLOQC HEADERS include/GLOQC/MatchITSTPCQC.h diff --git a/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h index 799f9c526bec6..024497b1b918e 100644 --- a/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h +++ b/Detectors/GLOQC/include/GLOQC/ITSTPCMatchingQCParams.h @@ -17,33 +17,42 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" -namespace o2 -{ -namespace gloqc +namespace o2::gloqc { // There are configurable params for TPC-ITS matching struct ITSTPCMatchingQCParams : public o2::conf::ConfigurableParamHelper { - float minPtITSCut = 0.f; - float etaITSCut = 1e10f; + int nBinsPt = 100; + float minPtITSCut = 0.1; + float etaITSCut = 1.4; int32_t minNITSClustersCut = 0; - int32_t maxChi2PerClusterITS = 100000; - float minPtTPCCut = 0.1f; - float etaTPCCut = 0.9f; + float maxChi2PerClusterITS = 1e10; + float minPtTPCCut = 0.1; + float etaTPCCut = 1.4; int32_t minNTPCClustersCut = 60; - float minDCACut = 100.f; - float minDCACutY = 10.f; - float minPtCut = 0.f; - float maxPtCut = 1e10f; - float etaCut = 1.e10f; + 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 gloqc -} // end namespace o2 +} // namespace o2::gloqc + // end namespace o2 #endif diff --git a/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h index 035aada3a765f..356d3e80d210e 100644 --- a/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h +++ b/Detectors/GLOQC/include/GLOQC/MatchITSTPCQC.h @@ -19,15 +19,19 @@ #include #include #include +#include #include #include #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/ProcessingContext.h" #include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTrack.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 @@ -50,11 +54,15 @@ struct LblInfo { class MatchITSTPCQC { public: - enum matchType : int { TPC = 0, - ITS, - SIZE }; + 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(); @@ -63,7 +71,7 @@ class MatchITSTPCQC void setDataRequest(const std::shared_ptr& dr) { mDataRequest = dr; } void finalize(); void reset(); - bool processV0(int iv, o2::globaltracking::RecoContainer& recoData); + 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]; } @@ -130,7 +138,11 @@ class MatchITSTPCQC TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } - TH2F* getHistoK0MassVsPt() const { return mK0MassVsPt; } + 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); @@ -183,6 +195,10 @@ class MatchITSTPCQC 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 @@ -228,7 +244,8 @@ class MatchITSTPCQC publisher->startPublishing(mDCArVsPtDen); publisher->startPublishing(mFractionITSTPCmatchDCArVsPt); if (mDoK0QC) { - publisher->startPublishing(mK0MassVsPt); + publisher->startPublishing(mK0MassVsPtVsOccpp); + publisher->startPublishing(mK0MassVsPtVsOccPbPb); } } @@ -245,14 +262,14 @@ class MatchITSTPCQC // 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 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(float v) { mNTPCClustersCut = v; } + void setMinNTPCClustersCut(int v) { mNTPCClustersCut = v; } void setMinDCAtoBeamPipeCut(std::array v) { setMinDCAtoBeamPipeDistanceCut(v[0]); @@ -261,32 +278,30 @@ class MatchITSTPCQC 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; } // TODO: define 2 different values for min and max (*) + 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 printParams() - { - 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) << "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; - } + 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; @@ -380,8 +395,12 @@ class MatchITSTPCQC 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] = {}; - void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D = false); + template + void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden); int mNTPCSelectedTracks = 0; int mNITSSelectedTracks = 0; @@ -389,11 +408,11 @@ class MatchITSTPCQC // 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) + 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 (***) @@ -401,21 +420,44 @@ class MatchITSTPCQC 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 = 1e10f; - float mEtaCut = 1e10f; // 1e10f as defaults of Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h - // TODO: define 2 different values for min and max (*) + 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; - TH2F* mK0MassVsPt = nullptr; + 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) - - ClassDefNV(MatchITSTPCQC, 3); + 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 diff --git a/Detectors/GLOQC/src/MatchITSTPCQC.cxx b/Detectors/GLOQC/src/MatchITSTPCQC.cxx index a0ae896105d2d..e1832056f072c 100644 --- a/Detectors/GLOQC/src/MatchITSTPCQC.cxx +++ b/Detectors/GLOQC/src/MatchITSTPCQC.cxx @@ -9,31 +9,33 @@ // 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 "Framework/InputSpec.h" -#include "ReconstructionDataFormats/TrackParametrization.h" #include "DetectorsBase/Propagator.h" #include "SimulationDataFormat/MCUtils.h" -#include -#include "TGraphAsymmErrors.h" #include "GlobalTracking/TrackCuts.h" #include +#include #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/V0.h" -// #include "GlobalTrackingStudy/V0Ext.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(); } @@ -42,6 +44,7 @@ MatchITSTPCQC::~MatchITSTPCQC() void MatchITSTPCQC::deleteHistograms() { + LOG(debug) << "Deleting histos..."; for (int i = 0; i < matchType::SIZE; ++i) { // Pt delete mPtNum[i]; @@ -108,6 +111,10 @@ void MatchITSTPCQC::deleteHistograms() delete m1OverPtPhysPrimNum[i]; delete m1OverPtPhysPrimDen[i]; delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + + // 3D eta/phi/pt + delete mEtaPhiPtNum[i]; + delete mEtaPhiPtDen[i]; } // Residuals @@ -124,7 +131,8 @@ void MatchITSTPCQC::deleteHistograms() delete mFractionITSTPCmatchDCArVsPt; // K0 - delete mK0MassVsPt; + delete mK0MassVsPtVsOccpp; + delete mK0MassVsPtVsOccPbPb; } //__________________________________________________________ @@ -162,6 +170,10 @@ void MatchITSTPCQC::reset() 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 @@ -205,7 +217,8 @@ void MatchITSTPCQC::reset() // K0 if (mDoK0QC) { - mK0MassVsPt->Reset(); + mK0MassVsPtVsOccpp->Reset(); + mK0MassVsPtVsOccPbPb->Reset(); } } @@ -214,12 +227,12 @@ bool MatchITSTPCQC::init() { LOGP(debug, "Creating Variable Binning"); std::array title{"TPC", "ITS"}; - std::array etaSel{"", ", |eta| < 0.9"}; + std::array etaSel{Form(", |#eta| < %.1f", mEtaTPCCut), Form(", |#eta| < %.1f", mEtaCut)}; std::array maxNCls{156, 7}; // log binning for pT - const Int_t nbinsPt = 100; - const Double_t xminPt = 0.01; - const Double_t xmaxPt = 20; + 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); @@ -233,24 +246,24 @@ bool MatchITSTPCQC::init() // 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] = 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()), 100, 0.f, 20.f); + 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()), 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); + 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| < 0.05 %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + 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| < 0.05 %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + 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()); @@ -258,11 +271,11 @@ bool MatchITSTPCQC::init() 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] = 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()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + 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()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + 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); @@ -272,25 +285,25 @@ bool MatchITSTPCQC::init() 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] = 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()), 100, 0.f, 20.f, 100, -2.f, 2.f); + 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()), 100, 0.f, 20.f, 100, -2.f, 2.f); + 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()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + 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()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + 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()), 100, 0.f, 20.f, maxNCls[i], 0, maxNCls[i]); + 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()), 100, 0.f, 20.f, 200, 0, 300); + 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()), 100, 0.f, 20.f, 200, 0, 300); + 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()), 100, 0.f, 20.f, 200, 0, 300); + 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); @@ -299,14 +312,20 @@ bool MatchITSTPCQC::init() 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), 100, 0.f, 20.f); + 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), 100, 0.f, 20.f); + 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), 100, 0.f, 20.f); + 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()); @@ -325,7 +344,7 @@ bool MatchITSTPCQC::init() } } - 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); + 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); @@ -334,10 +353,10 @@ bool MatchITSTPCQC::init() 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); + 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., 200, -30, 30); + 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); @@ -367,22 +386,69 @@ bool MatchITSTPCQC::init() 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] = 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()), 100, -20.f, 20.f); + 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()), 100, -20.f, 20.f); + 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 - mK0MassVsPt = new TH2F("mK0MassVsPt", "K0 invariant mass vs Pt; Pt [GeV/c]; K0s mass [GeV/c^2]", 100, 0.f, 20.f, 100, 0.3, 0.7); + 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; } @@ -395,7 +461,7 @@ void MatchITSTPCQC::initDataRequest() mSrc &= mAllowedSources; - if (mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0 || mSrc[GID::Source::ITS] == 0) { + 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; } @@ -404,6 +470,7 @@ void MatchITSTPCQC::initDataRequest() if (mDoK0QC) { mDataRequest->requestPrimaryVertices(mUseMC); mDataRequest->requestSecondaryVertices(mUseMC); + mDataRequest->requestTPCOccMap(); } } @@ -411,7 +478,6 @@ void MatchITSTPCQC::initDataRequest() void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) { - // Getting the B field mBz = o2::base::Propagator::Instance()->getNominalBz(); @@ -419,18 +485,40 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) 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.get()); + mRecoCont.collectData(ctx, *mDataRequest); 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(); + 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); @@ -486,7 +574,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) if (trk.getRefITS().getSource() != GID::ITS) { continue; } - if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { + if (isTPCTrackSelectedEntry[idxTrkTpc]) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); if (!lbl.isValid()) { continue; @@ -497,9 +585,9 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) 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}}); + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); } else { - mMapLabels[matchType::TPC].insert({lbl, {itrk, false}}); + mMapLabels[matchType::TPC].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); } } else { // winner (if more tracks have the same label) has the highest pt @@ -509,7 +597,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } } auto idxTrkIts = trk.getRefITS().getIndex(); - if (isITSTrackSelectedEntry[idxTrkIts] == true) { + if (isITSTrackSelectedEntry[idxTrkIts]) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); if (!lbl.isValid()) { continue; @@ -520,9 +608,9 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) 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, {itrk, true}}); + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = true}}); } else { - mMapLabels[matchType::ITS].insert({lbl, {itrk, false}}); + mMapLabels[matchType::ITS].insert({lbl, {.mIdx = itrk, .mIsPhysicalPrimary = false}}); } } else { // winner (if more tracks have the same label) has the highest pt @@ -532,7 +620,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } } } - LOG(info) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); + 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) { @@ -544,27 +632,29 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) trkDen = mTPCTracks[trk.getRefTPC()]; } else { trkDen = mITSTracks[trk.getRefITS()]; - if (std::abs(trkDen.getEta()) > 0.9) { + 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()) > 0.05) { + 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()) > 0.05) { + 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()); @@ -628,13 +718,13 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) std::array title{"TPC", "ITS"}; for (int i = 0; i < matchType::SIZE; ++i) { o2::track::TrackParCov trkRef; - int idxTrkRef; + 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] == true) { + if (isTPCTrackSelectedEntry[idxTrkRef]) { fillHisto = true; ++mNITSTPCSelectedTracks[i]; } @@ -650,25 +740,25 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } trkRef = mITSTracks[trk.getRefITS()]; LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); - if (isITSTrackSelectedEntry[idxTrkRef] == true) { + 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()) > 0.9) { + 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 == true) { + 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()) > 0.05) { + if (std::abs(trkRef.getEta()) > mEtaNo0Cut) { mPtNum_noEta0[i]->Fill(trkRef.getPt()); } mPhiNum[i]->Fill(trkRef.getPhi()); @@ -678,6 +768,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } 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 @@ -702,7 +793,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) mChi2Refit->Fill(trk.getChi2Refit()); mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); math_utils::Point3D v{}; - std::array dca; + std::array dca{-999, -999}; if (trkRef.propagateParamToDCA(v, mBz, &dca)) { mDCAr->Fill(dca[0]); if (!mUseMC) { @@ -727,7 +818,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) // 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) { + if (isTPCTrackSelectedEntry[itrk]) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); if (!lbl.isValid()) { continue; @@ -759,7 +850,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) // 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) { + if (isITSTrackSelectedEntry[itrk]) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); if (!lbl.isValid()) { continue; @@ -786,18 +877,19 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } } } - 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(); + 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()) > 0.05) { + 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()); @@ -818,13 +910,14 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } for (auto const& el : mMapRefLabels[matchType::ITS]) { auto const& trk = mITSTracks[el.second.mIdx]; - if (std::abs(trk.getEta()) < 0.9) { + if (std::abs(trk.getEta()) < mEtaITSCut) { mPtDen[matchType::ITS]->Fill(trk.getPt()); - if (std::abs(trk.getEta()) > 0.05) { + 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()); @@ -832,7 +925,7 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) 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) { + 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()); @@ -845,16 +938,17 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) // 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) { + 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()) > 0.05) { + 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()); @@ -871,15 +965,16 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) 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) { + 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()) > 0.05) { + 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"; @@ -895,45 +990,117 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } } - if (mDoK0QC) { + if (mDoK0QC && mRecoCont.getPrimaryVertices().size() > 0) { // now doing K0S + mFitterV0.setBz(mBz); const auto pvertices = mRecoCont.getPrimaryVertices(); - LOG(info) << "Found " << pvertices.size() << " primary vertices"; + 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(info) << "Found " << mRecoCont.getV0s().size() << " V0s in reco container"; - LOG(info) << "Found " << nv0 << " V0s ids"; + 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]; - pv2sv[v0id.getVertexID()].push_back(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) ? 1 : 0; + nV0sOk += processV0(iv0, mRecoCont, mTBinClOcc, pvTime) ? 1 : 0; } + ++myCount; } - LOG(info) << "Processed " << nV0sOk << " V0s"; + LOG(debug) << "Processed " << nV0sOk << " V0s"; } evCount++; } //__________________________________________________________ -bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoData) +bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoData, std::vector& mTBinClOcc, float pvTime) { o2::dataformats::V0 v0; auto v0s = recoData.getV0s(); @@ -941,6 +1108,13 @@ bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoDat 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; } @@ -948,17 +1122,34 @@ bool MatchITSTPCQC::processV0(int iv, o2::globaltracking::RecoContainer& recoDat if (mMaxEtaK0 < std::abs(v0sel.getEta())) { return false; } - LOG(info) << "Find K0 with mass " << std::sqrt(v0sel.calcMass2AsK0()); - if (mCutK0Mass > 0 && std::abs(std::sqrt(v0sel.calcMass2AsK0()) - 0.497) > mCutK0Mass) { + + 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; } - mK0MassVsPt->Fill(v0sel.getPt(), std::sqrt(v0sel.calcMass2AsK0())); + // 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; } @@ -1012,39 +1203,17 @@ void MatchITSTPCQC::finalize() // first we use denominators and nominators to set the TEfficiency; later they are scaled - // some checks + // filling the efficiency 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 < 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 < 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); - } - } - - // 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<2>(mFractionITSTPCmatchPhiVsPt[ti], mPhiVsPtNum[ti], mPhiVsPtDen[ti]); + setEfficiency<2>(mFractionITSTPCmatchEtaVsPt[ti], mEtaVsPtNum[ti], mEtaVsPtDen[ti]); setEfficiency(mFractionITSTPCmatch1OverPt[ti], m1OverPtNum[ti], m1OverPtDen[ti]); - setEfficiency(mFractionITSTPCmatchClsVsPt[ti], mClsVsPtNum[ti], mClsVsPtDen[ti], true); - setEfficiency(mFractionITSTPCmatchChi2VsPt[ti], mChi2VsPtNum[ti], mChi2VsPtDen[ti], true); + 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]); @@ -1059,7 +1228,7 @@ void MatchITSTPCQC::finalize() setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[ti], m1OverPtPhysPrimNum[ti], m1OverPtPhysPrimDen[ti]); } } - setEfficiency(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen, true); + setEfficiency<2>(mFractionITSTPCmatchDCArVsPt, mDCArVsPtNum, mDCArVsPtDen); /* mPtTPC->Scale(scaleFactTPC); mPt->Scale(scaleFactITSTPC); @@ -1079,9 +1248,10 @@ void MatchITSTPCQC::finalize() } //__________________________________________________________ - -void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D) +template +void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden) { + // Trivial check if we initalized if (eff == nullptr) { LOG(fatal) << "Cannot get TEfficiency object "; } @@ -1093,7 +1263,7 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i } // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents - if constexpr (false) { // checking + 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"; @@ -1106,14 +1276,18 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i LOGP(warning, "Histograms do not have a compatible binning"); bad = true; } - if (!is2D) { + if constexpr (DIM == 3) { 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; + 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 { + } 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)) { @@ -1122,9 +1296,19 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i } } } + } 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 @@ -1134,7 +1318,9 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i if (!eff->SetPassedHistogram(*hnum, "")) { LOG(fatal) << "Something went wrong when defining the efficiency numerator " << eff->GetName() << " from " << hnum->GetName(); } - if (is2D) { + 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")); @@ -1215,6 +1401,9 @@ void MatchITSTPCQC::getHistos(TObjArray& objar) 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); @@ -1228,5 +1417,33 @@ void MatchITSTPCQC::getHistos(TObjArray& objar) objar.Add(mFractionITSTPCmatchDCArVsPt); // V0 - objar.Add(mK0MassVsPt); + 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 98075051356e7..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,11 +38,11 @@ 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; @@ -60,7 +61,7 @@ struct GRPEnvVariables { } } - ClassDefNV(GRPEnvVariables, 1); + ClassDefNV(GRPEnvVariables, 2); }; struct MagFieldHelper { @@ -122,7 +123,7 @@ struct MagFieldHelper { struct GRPCollimators { - std::unordered_map>> mCollimators; + std::unordered_map>> mCollimators; size_t totalEntries() const { size_t s = 0; @@ -141,7 +142,7 @@ struct GRPCollimators { } } - ClassDefNV(GRPCollimators, 1); + ClassDefNV(GRPCollimators, 2); }; struct GRPLHCInfo { @@ -191,19 +192,19 @@ struct GRPLHCInfo { static constexpr std::string_view lhcStringAliases[NLHCStringAliases] = {"ALI_Lumi_Source_Name", "BEAM_MODE", "MACHINE_MODE"}; 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) + 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()) { @@ -291,16 +292,16 @@ 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) { @@ -366,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 f3f96794095d7..aec4241f4f8db 100644 --- a/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx +++ b/Detectors/GRP/calibration/src/GRPDCSDPsProcessor.cxx @@ -185,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 @@ -207,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 @@ -237,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 @@ -277,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; @@ -318,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; } @@ -408,7 +408,7 @@ void GRPDCSDPsProcessor::updateCollimatorsCCDB() //______________________________________________________________________ -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"; @@ -422,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 ea56cf8270335..1097855a5d579 100644 --- a/Detectors/GRP/workflows/CMakeLists.txt +++ b/Detectors/GRP/workflows/CMakeLists.txt @@ -45,6 +45,7 @@ o2_add_executable(grp-create SOURCES src/create-grp-ecs.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsCommonDataFormats O2::DataFormatsParameters + O2::DataFormatsCTP O2::CommonUtils O2::CCDB Boost::program_options) diff --git a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx index 3f9b621f31abe..d738976ff4ffd 100644 --- a/Detectors/GRP/workflows/src/create-aligned-geometry.cxx +++ b/Detectors/GRP/workflows/src/create-aligned-geometry.cxx @@ -150,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 95bfb878cee9d..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" @@ -31,6 +33,7 @@ enum CCDBRefreshMode { NONE, int createGRPECSObject(const std::string& dataPeriod, int run, + int runOrig, // in case of replay int runTypeI, int nHBPerTF, const std::string& _detsReadout, @@ -44,13 +47,14 @@ int createGRPECSObject(const std::string& dataPeriod, 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")}; @@ -78,6 +82,8 @@ int 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); @@ -119,10 +125,32 @@ int 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"; @@ -181,13 +209,33 @@ int createGRPECSObject(const std::string& dataPeriod, } } } + + 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(); @@ -195,7 +243,7 @@ int 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) { + if (retValGLO != 0 || retValRCT != 0 || retValGLOmd != 0 || retValCTP != 0) { return 4; } return 0; @@ -220,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); @@ -253,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") { @@ -278,6 +328,7 @@ int main(int argc, char** argv) 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(), @@ -291,6 +342,7 @@ 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); diff --git a/Detectors/GlobalTracking/CMakeLists.txt b/Detectors/GlobalTracking/CMakeLists.txt index fce4a5370d612..ecf77ea741e21 100644 --- a/Detectors/GlobalTracking/CMakeLists.txt +++ b/Detectors/GlobalTracking/CMakeLists.txt @@ -22,6 +22,8 @@ o2_add_library(GlobalTracking src/MatchGlobalFwdAssessment.cxx src/MatchGlobalFwdParam.cxx src/MatchTOFParams.cxx + src/MatchITSTPCQC.cxx + src/ITSTPCMatchingQCParams.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC O2::DataFormatsITSMFT diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h index 164bdb8d9f1f4..3445123385cfa 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h @@ -333,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/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& 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(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(); diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h index 8a8dbcb8fa9ae..00f2fc157a5ec 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h @@ -50,6 +50,7 @@ #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" @@ -132,6 +133,8 @@ struct TrackLocTPC : public o2::track::TrackParCov { 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 + 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) @@ -143,7 +146,7 @@ struct TrackLocTPC : public o2::track::TrackParCov { return constraint == Constrained ? 0.f : (constraint == ASide ? dt : -dt); } - ClassDefNV(TrackLocTPC, 2); + ClassDefNV(TrackLocTPC, 3); }; ///< ITS track outward parameters propagated to reference X, with time bracket and index of @@ -738,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, diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h index 215e5e8a72f63..eaafcca527d7d 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackCuts.h @@ -56,7 +56,7 @@ class TrackCuts /// ITS void setMinPtITSCut(float value) { mPtITSCut = value; } void setEtaITSCut(float value) { mEtaITSCut = value; } - void setMinNClustersITS(float value) { mMinNClustersITS = value; } + void setMinNClustersITS(int32_t value) { mMinNClustersITS = value; } void setMaxChi2PerClusterITS(float value) { mMaxChi2PerClusterITS = value; } void setRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) { diff --git a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h index d87d68dd122b2..f65d9ffd260e6 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/TrackMethods.h @@ -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++; } diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx index 67f2ae2c8cb20..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); @@ -400,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++; + } + } + } } } @@ -409,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; @@ -434,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]; @@ -464,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++; diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index d67708608df55..6a3486dd12044 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -192,15 +192,15 @@ void MatchTOF::run(const o2::globaltracking::RecoContainer& inp, unsigned long f bct0--; } float tof = matchingPair.getSignal() - bct0 * Geo::BC_TIME_INPS; - if (abs(tof - matchingPair.getLTIntegralOut().getTOF(2)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(3)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(4)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(0)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(1)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(5)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(6)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(7)) < 600) { - } else if (abs(tof - matchingPair.getLTIntegralOut().getTOF(8)) < 600) { + 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(); } @@ -449,6 +449,7 @@ bool MatchTOF::prepareTPCData() 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); } } @@ -556,7 +557,7 @@ void MatchTOF::propagateTPCTracks(int sec) } if (trc.getX() < o2::constants::geom::XTPCOuterRef - 1.) { - if (!propagateToRefXWithoutCov(trc, o2::constants::geom::XTPCOuterRef, 10, mBz) || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagat> + if (!propagateToRefXWithoutCov(trc, o2::constants::geom::XTPCOuterRef, 10, mBz) || std::abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagat> mNotPropagatedToTOF[trkType::UNCONS]++; continue; } @@ -565,7 +566,7 @@ void MatchTOF::propagateTPCTracks(int sec) 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)) { // || TMath::Abs(trc.getZ()) > Geo::MAXHZTOF) { // we check that the propagation with the cov matrix w> + 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; } @@ -602,7 +603,7 @@ void MatchTOF::propagateConstrTracks(int sec) } // 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;> + 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; } @@ -705,7 +706,7 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT // compute track length up to now mLTinfos[sector][trkType::UNCONS].emplace_back(intLT0); float vz0 = _tr.getZAt(0, mBz); - if (abs(vz0) > 9000) { + if (std::abs(vz0) > 9000) { vz0 = _tr.getZ() - _tr.getX() * _tr.getTgl(); } mVZtpcOnly[sector].push_back(vz0); @@ -726,14 +727,14 @@ void MatchTOF::addTPCSeed(const o2::tpc::TrackTPC& _tr, o2::dataformats::GlobalT } 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 + 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; } } // 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> + 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; } @@ -931,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]; @@ -939,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++) { @@ -1047,6 +1088,7 @@ void MatchTOF::doMatching(int sec) 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); @@ -1084,7 +1126,7 @@ void MatchTOF::doMatching(int sec) foundCluster = true; // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - 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); // 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)); @@ -1289,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]; @@ -1298,17 +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++) { @@ -1410,10 +1494,10 @@ void MatchTOF::doMatchingForTPC(int sec) } if (mMatchParams->applyPIDcutTPConly) { // for TPC only tracks allowing possibility to apply a PID cut - if (abs(tof - trkLTInt[ibc][iPropagation].getTOF(2)) < 2000) { // pion hypotesis - } else if (abs(tof - trkLTInt[ibc][iPropagation].getTOF(3)) < 2000) { // kaon hypoteis - } else if (abs(tof - trkLTInt[ibc][iPropagation].getTOF(4)) < 2000) { // proton hypotesis - } else { // reject matching + 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; } } @@ -1435,6 +1519,7 @@ void MatchTOF::doMatchingForTPC(int sec) 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 { @@ -1479,7 +1564,7 @@ void MatchTOF::doMatchingForTPC(int sec) // set event indexes (to be checked) int eventIndexTOFCluster = mTOFClusSectIndexCache[indices[0]][itof]; - 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); // 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)); @@ -1496,6 +1581,8 @@ void MatchTOF::doMatchingForTPC(int sec) //______________________________________________ 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++) { @@ -1513,11 +1600,15 @@ int MatchTOF::findFITIndex(int bc, const gsl::span& FI 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(); if (mHasFillScheme && !mFillScheme[ir.bc]) { continue; } - bool quality = (fabs(FITRecPoints[i].getCollisionTime(0)) < 1000 && fabs(FITRecPoints[i].getVertex()) < 1000); + 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; } @@ -1555,7 +1646,7 @@ 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(); @@ -1595,9 +1686,13 @@ void MatchTOF::BestMatches(std::vector& match float timeNew = TOFClusWork[matchingPair.getTOFClIndex()].getTime() - deltaT; float timeOld = TOFClusWork[prevMatching.getTOFClIndex()].getTime(); - if (fabs(timeNew - timeOld) < 200) { + 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 } @@ -1609,6 +1704,40 @@ void MatchTOF::BestMatches(std::vector& match 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 @@ -1819,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()); @@ -1838,7 +1967,6 @@ 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(const o2::track::TrackParCov& trc, float xRef, float stepInCm, float bzField) { @@ -1859,7 +1987,7 @@ bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, floa 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()); @@ -1876,6 +2004,61 @@ bool MatchTOF::propagateToRefXWithoutCov(const o2::track::TrackParCov& trc, floa // 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 } @@ -1899,7 +2082,7 @@ 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(); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index eb2a2212edb30..5f99ad2202073 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -9,6 +9,13 @@ // 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 @@ -50,13 +57,6 @@ #include "ITS3Reconstruction/IOUtils.h" #endif -#include "GPUO2Interface.h" // Needed for propper settings in GPUParam.h -#include "GPUParam.h" -#include "GPUParam.inc" -#ifdef WITH_OPENMP -#include -#endif - using namespace o2::globaltracking; using MatrixDSym4 = ROOT::Math::SMatrix>; @@ -68,6 +68,8 @@ constexpr float MatchTPCITS::Tan70, MatchTPCITS::Cos70I2, MatchTPCITS::MaxSnp, M LinksPoolMT* TPCABSeed::gLinksPool = nullptr; +const o2::gpu::GPUTPCGeometry MatchTPCITS::TPCGeometry{}; + //______________________________________________ MatchTPCITS::MatchTPCITS() = default; @@ -99,7 +101,7 @@ 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 - calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); + calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, mTPCDrift.refTP); calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); } @@ -243,8 +245,8 @@ void MatchTPCITS::init() } #endif - if (mParams->runAfterBurner) { // only used in AfterBurner - 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(); @@ -428,6 +430,12 @@ int MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float t 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) { @@ -442,6 +450,8 @@ int MatchTPCITS::addTPCSeed(const o2::track::TrackParCov& _tr, float t0, float t tpcID, srcGID, MinusOne, + clRow, + padFromEdge, (extConstrained || tpcOrig.hasBothSidesClusters()) ? TrackLocTPC::Constrained : (tpcOrig.hasASideClustersOnly() ? TrackLocTPC::ASide : TrackLocTPC::CSide)}); // propagate to matching Xref const auto& trackTune = TrackTuneParams::Instance(); @@ -661,7 +671,8 @@ bool MatchTPCITS::prepareITSData() auto pattID = clus.getPatternID(); unsigned int npix; #ifdef ENABLE_UPGRADES - if ((pattID == o2::itsmft::CompCluster::InvalidPatternID) || ((withITS3) ? mIT3Dict->isGroup(pattID) : mITSDict->isGroup(pattID))) { // braces guarantee evaluation order + 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 @@ -671,7 +682,7 @@ bool MatchTPCITS::prepareITSData() } else { #ifdef ENABLE_UPGRADES if (withITS3) { - npix = mIT3Dict->getNpixels(pattID); + npix = mIT3Dict->getNpixels(pattID, ib); } else { npix = mITSDict->getNpixels(pattID); } @@ -1434,8 +1445,7 @@ void MatchTPCITS::refitWinners(pmr::vector& matche #ifdef WITH_OPENMP #pragma omp parallel for schedule(dynamic) num_threads(mNThreads) \ - reduction(+ \ - : nFailedRefit) + reduction(+ : nFailedRefit) #endif for (int ifit = 0; ifit < nToFit; ifit++) { int iTPC = tpcToFit[ifit], iITS; @@ -1704,7 +1714,7 @@ bool MatchTPCITS::refitTrackTPCITS(int slot, int iTPC, int& iITS, pmr::vectorestimateLTIncrement(tracOut, posStart, posEnd); - tofL.addStep(lInt, tracOut.getP2Inv()); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); @@ -1744,21 +1754,21 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vectorestimateLTFast(tofL, winLink); // guess about initial value for the track integral from the origin // refit track outward in the ITS 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[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); @@ -1779,7 +1789,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vectorPropagateToXBxByBz(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(); matchedTracks.pop_back(); // destroy failed track return false; @@ -1794,7 +1804,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vectorestimateLTIncrement(tracOut, posStart, posEnd); - tofL.addStep(lInt, tracOut.getP2Inv()); + tofL.addStep(lInt, tracOut.getQ2P2()); tofL.addX2X0(lInt * mTPCmeanX0Inv); propagator->PropagateToXBxByBz(tracOut, o2::constants::geom::XTPCOuterRef, MaxSnp, 10., mUseMatCorrFlag, &tofL); const auto& trackTune = TrackTuneParams::Instance(); @@ -2872,7 +2882,7 @@ 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; + uint8_t clSect = 0, clRow = 0, prevRow = 0xff, padFromEdge = -1; uint32_t clIdx = 0; int nshared = 0; std::array shMap{}; @@ -2880,7 +2890,7 @@ void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex) 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] & GPUCA_NAMESPACE::gpu::GPUTPCGMMergedTrackHit::flagShared) { + if (mTPCRefitterShMap[absoluteIndex] & o2::gpu::GPUTPCGMMergedTrackHit::flagShared) { if (!(prevRow == clRow && prevRawShared)) { nshared++; } @@ -2888,6 +2898,11 @@ void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex) 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" @@ -2900,6 +2915,7 @@ void MatchTPCITS::dumpTPCOrig(bool acc, int tpcIndex) << "time0=" << tpcOrig.getTime0() << "trc=" << ((o2::track::TrackParCov&)tpcOrig) << "minRow=" << clRow + << "padFromEdge=" << padFromEdge << "multTPC=" << mltTPC; if (mMCTruthON) { (*mDBGOut) << "tpcOrig" diff --git a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx index fad4f9d36fb31..db61300c4cf60 100644 --- a/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/qc/src/ITSTPCMatchingQCSpec.cxx @@ -49,8 +49,18 @@ void ITSTPCMatchingQCDevice::init(InitContext& /*ic*/) 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); @@ -127,10 +137,11 @@ DataProcessorSpec getITSTPCMatchingQCDevice(bool useMC, bool doK0QC, std::string 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 diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index 8a7611e3380a4..34c41ec234dc5 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -66,6 +66,7 @@ class CosmicsMatchingSpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~CosmicsMatchingSpec() override = default; void init(InitContext& ic) final; diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index 6b83f787be17d..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" @@ -113,7 +114,8 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) return true; // just in case this selection was not done on RecoContainer filling level } auto itsID = recoData.getITSContributorGID(_origID); - if (!itsID.isSourceSet() || o2::math_utils::numberOfBitsSet(recoData.getITSTrack(itsID).getPattern() & 7) < minIBHits) { + 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()) { @@ -150,6 +152,34 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) } } 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}, vertices); diff --git a/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx index 0ab7a69dc5363..d53bbf9a30690 100644 --- a/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/ReaderDriverSpec.cxx @@ -23,6 +23,7 @@ #include "GlobalTrackingWorkflow/ReaderDriverSpec.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "CommonUtils/StringUtils.h" +#include "CommonDataFormat/IRFrame.h" #include "TFile.h" #include "TTree.h" @@ -32,6 +33,7 @@ namespace o2 { namespace globaltracking { +using HBFINI = o2::raw::HBFUtilsInitializer; class ReadeDriverSpec : public o2::framework::Task { @@ -60,16 +62,20 @@ void ReadeDriverSpec::run(ProcessingContext& pc) static RateLimiter limiter; static int count = 0; if (!count) { - if (o2::raw::HBFUtilsInitializer::NTFs < 0) { + if (HBFINI::NTFs < 0) { LOGP(fatal, "Number of TFs to process was not initizalized in the HBFUtilsInitializer"); } - mNTF = (mNTF > 0 && mNTF < o2::raw::HBFUtilsInitializer::NTFs) ? mNTF : o2::raw::HBFUtilsInitializer::NTFs; + mNTF = (mNTF > 0 && mNTF < HBFINI::NTFs) ? mNTF : HBFINI::NTFs; } else { // check only for count > 0 limiter.check(pc, mTFRateLimit, mMinSHM); } - std::vector v{}; - pc.outputs().snapshot(Output{"GLO", "READER_DRIVER", 0}, v); - if (++count >= mNTF) { + 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); } @@ -84,7 +90,7 @@ DataProcessorSpec getReaderDriverSpec(const std::string& metricChannel, size_t m options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, metricChannel, {"Out-of-band channel config for TF throttling"}}); } return DataProcessorSpec{ - o2::raw::HBFUtilsInitializer::ReaderDriverDevice, + HBFINI::ReaderDriverDevice, Inputs{}, Outputs{{"GLO", "READER_DRIVER", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask(minSHM)}, diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 80ba5f94280a0..ea566f15a0b59 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -62,6 +62,7 @@ class SecondaryVertexingSpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; @@ -254,7 +255,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas src |= (srcClus = GTrackID::getSourceMask(GTrackID::ITS)); } if (GTrackID::includesDet(o2::detectors::DetID::TPC, src) && !src[GTrackID::TPC]) { - throw std::runtime_error("Tracks involving TPC were requested w/o requesting TPC-only tracks"); + 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); diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx index 849964aeaf871..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" 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 4710302e4e91e..3f6e79e433635 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -62,6 +62,7 @@ class TOFMatcherSpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TOFMatcherSpec() override = default; void init(InitContext& ic) final; diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 1368bf6f34fe4..14af8c12794cc 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -75,6 +75,7 @@ class TPCITSMatchingDPL : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; diff --git a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx index 8dc56794817a5..9a95c83617210 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx @@ -114,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"); diff --git a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index 17bad37b3c14e..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -9,9 +9,10 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -# add_compile_options(-O0 -g -fPIC) +#add_compile_options(-O0 -g -fPIC) o2_add_library(GlobalTrackingStudy + TARGETVARNAME targetName SOURCES src/TPCTrackStudy.cxx src/TrackingStudy.cxx src/SVStudy.cxx @@ -21,6 +22,11 @@ o2_add_library(GlobalTrackingStudy 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 @@ -29,10 +35,14 @@ o2_add_library(GlobalTrackingStudy O2::TPCWorkflow O2::SimulationDataFormat) -o2_target_root_dictionary( - GlobalTrackingStudy - HEADERS include/GlobalTrackingStudy/V0Ext.h - include/GlobalTrackingStudy/TrackInfoExt.h +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 @@ -69,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 index 9c9453215c9a0..d54513cb07a60 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/SVStudy.h @@ -22,7 +22,7 @@ namespace o2::svstudy { /// create a processor spec -o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, bool useMC); +o2::framework::DataProcessorSpec getSVStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcCls, bool useMC); } // namespace o2::svstudy 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/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h index 90db0ca4ee37c..e33a0def63842 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -25,22 +25,56 @@ 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; - int nClTPC = 0; - int nClITS = 0; - int pattITS = 0; - ClassDefNV(TrackInfoExt, 1); + 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 diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h index 3dd315909ed1e..d1326a47ac909 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h @@ -12,17 +12,16 @@ #ifndef O2_TRACKING_STUDY_H #define O2_TRACKING_STUDY_H -#include "ReconstructionDataFormats/GlobalTrackID.h" -#include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" -#include "ReconstructionDataFormats/Track.h" -#include "MathUtils/detail/Bracket.h" -#include "DataFormatsTPC/ClusterNative.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, bool checkMatching); +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 diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h new file mode 100644 index 0000000000000..ed78ba2a710ec --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -0,0 +1,43 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_CONFIG_H +#define O2_TRACKING_STUDY_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::trackstudy +{ +struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper { + float minPt = 0.05; + float maxTgl = 1.5; + float minPtMC = 0.05; + float maxTglMC = 1.5; + float maxRMC = 33.; + float maxPosTglMC = 2.; + float maxPVZOffset = 15.; + float decayMotherMaxT = 1.0f; // max TOF in ns for mother particles to study + bool requireITSorTPCTrackRefs = true; + bool requireTopBottomRefs = false; + bool storeTPCTrackRefs = false; + bool storeITSInfo = true; + int minTPCRefsToExtractClRes = 2; + int nOccBinsDrift = 10; // number of bins for TPC max drift time, where we integrate the occupancies + int nTBPerOccBin = 48; // number of TB per occ bin + float rejectClustersResStat = 0.1; + float maxTPCRefExtrap = 2; // max dX to extrapolate the track ref when extrapolating track true posions + int minITSClForITSoutput = 7; // create special ITS otput only for long enough tracks + int decayPDG[5] = {310, 3122, 411, 421, -1}; // decays to study, must end by -1 + O2ParamDef(TrackMCStudyConfig, "trmcconf"); +}; +} // namespace o2::trackstudy + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h new file mode 100644 index 0000000000000..f5846bcce5a49 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyTypes.h @@ -0,0 +1,324 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRACKING_STUDY_TYPES_H +#define O2_TRACKING_STUDY_TYPES_H +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/Track.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/TimeStamp.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "SimulationDataFormat/TrackReference.h" +#include +#include + +namespace o2::trackstudy +{ +struct MCTrackInfo { + + inline float getMCTimeMUS() const { return bcInTF * o2::constants::lhc::LHCBunchSpacingMUS; } + inline bool hasITSHitOnLr(int i) const { return (pattITSCl & ((0x1 << i) & 0x7f)) != 0; } + int getNITSClusCont() const; + int getNITSClusForAB() const; + int getLowestITSLayer() const; + int getHighestITSLayer() const; + std::vector occTPCV{}; + std::vector trackRefsTPC{}; + o2::track::TrackPar track{}; + o2::MCCompLabel label{}; + float occTPC = -1.f; + int occITS = -1.f; + int bcInTF = -1; + int pdg = 0; + int pdgParent = 0; + int parentEntry = -1; + int16_t nTPCCl = 0; + int16_t nTPCClShared = 0; + int8_t parentDecID = -1; + uint8_t minTPCRow = -1; + uint8_t maxTPCRow = 0; + uint8_t nUsedPadRows = 0; + uint8_t maxTPCRowInner = 0; // highest row in the sector containing the lowest one + uint8_t minTPCRowSect = -1; + uint8_t maxTPCRowSect = -1; + int8_t nITSCl = 0; + int8_t pattITSCl = 0; + uint8_t flags = 0; + + enum Flags : uint32_t { Primary = 0, + AddedAtRecStage = 2, + BitMask = 0xff }; + + bool isPrimary() const { return isBitSet(Primary); } + bool isAddedAtRecStage() const { return isBitSet(AddedAtRecStage); } + void setPrimary() { setBit(Primary); } + void setAddedAtRecStage() { setBit(AddedAtRecStage); } + + uint8_t getBits() const { return flags; } + bool isBitSet(int bit) const { return flags & (0xff & (0x1 << bit)); } + void setBits(std::uint8_t b) { flags = b; } + void setBit(int bit) { flags |= BitMask & (0x1 << bit); } + void resetBit(int bit) { flags &= ~(BitMask & (0x1 << bit)); } + + o2::track::TrackPar getTrackParTPC(float b, float x = 90) const; + float getTrackParTPCPar(int i, float b, float x = 90) const; + float getTrackParTPCPhiSec(float b, float x = 90) const; + + ClassDefNV(MCTrackInfo, 8); +}; + +struct RecTrack { + enum FakeFlag { + FakeITS = 0x1 << 0, + FakeTPC = 0x1 << 1, + FakeTRD = 0x1 << 2, + FakeTOF = 0x1 << 3, + FakeITSTPC = 0x1 << 4, + FakeITSTPCTRD = 0x1 << 5, + HASACSides = 0x1 << 6, + FakeGLO = 0x1 << 7 + }; + o2::track::TrackParCov track{}; + o2::dataformats::VtxTrackIndex gid{}; + o2::dataformats::TimeStampWithError ts{}; + o2::MCEventLabel pvLabel{}; + short pvID = -1; + uint8_t nClTPCShared = 0; + uint8_t flags = 0; + uint8_t nClITS = 0; + uint8_t nClTPC = 0; + uint8_t pattITS = 0; + int8_t lowestPadRow = -1; + int8_t padFromEdge = -1; + uint8_t rowMaxTPC = 0; + uint8_t rowCountTPC = 0; + + bool isFakeGLO() const { return flags & FakeGLO; } + bool isFakeITS() const { return flags & FakeITS; } + bool isFakeTPC() const { return flags & FakeTPC; } + bool isFakeTRD() const { return flags & FakeTRD; } + bool isFakeTOF() const { return flags & FakeTOF; } + bool isFakeITSTPC() const { return flags & FakeITSTPC; } + bool hasACSides() const { return flags & HASACSides; } + + ClassDefNV(RecTrack, 3); +}; + +struct TrackPairInfo { + RecTrack tr0; + RecTrack tr1; + uint8_t nshTPC = 0; + uint8_t nshTPCRow = 0; + + int getComb() const { return tr0.track.getSign() != tr1.track.getSign() ? 0 : (tr0.track.getSign() > 0 ? 1 : 2); } + float getDPhi() const + { + float dphi = tr0.track.getPhi() - tr1.track.getPhi(); + if (dphi < -o2::constants::math::PI) { + dphi += o2::constants::math::TwoPI; + } else if (dphi > o2::constants::math::PI) { + dphi -= o2::constants::math::TwoPI; + } + return dphi; + } + float getDTgl() const { return tr0.track.getTgl() - tr1.track.getTgl(); } + + ClassDefNV(TrackPairInfo, 1) +}; + +struct TrackFamily { // set of tracks related to the same MC label + MCTrackInfo mcTrackInfo{}; + std::vector recTracks{}; + o2::track::TrackParCov trackITSProp{}; + o2::track::TrackParCov trackTPCProp{}; + int8_t entITS = -1; + int8_t entTPC = -1; + int8_t entITSTPC = -1; + int8_t entITSFound = -1; // ITS track for this MC track, regardless if it was matched to TPC of another track + int8_t flags = 0; + float tpcT0 = -999.; + + bool contains(const o2::dataformats::VtxTrackIndex& ref) const + { + for (const auto& tr : recTracks) { + if (ref == tr.gid) { + return true; + } + } + return false; + } + const RecTrack& getTrackWithITS() const { return entITS < 0 ? dummyRecTrack : recTracks[entITS]; } + const RecTrack& getTrackWithTPC() const { return entTPC < 0 ? dummyRecTrack : recTracks[entTPC]; } + const RecTrack& getTrackWithITSTPC() const { return entITSTPC < 0 ? dummyRecTrack : recTracks[entITSTPC]; } + const RecTrack& getTrackWithITSFound() const { return entITSFound < 0 ? dummyRecTrack : recTracks[entITSFound]; } + const RecTrack& getLongestTPCTrack() const + { + int n = getLongestTPCTrackEntry(); + return n < 0 ? dummyRecTrack : recTracks[n]; + } + int getLongestTPCTrackEntry() const; + int getNTPCClones() const; + static RecTrack dummyRecTrack; // + + ClassDefNV(TrackFamily, 1); +}; + +struct ClResTPCCont { + // contributor to TPC Cluster + std::array xyz{}; + std::array below{}; + std::array above{}; + float snp = 0.; + float tgl = 0.; + float q2pt = 0.; + bool corrAttach = false; + + int getNExt() const { return (below[0] > 1.) + (above[0] > 1.); } + + float getClX() const { return xyz[0]; } + float getClY() const { return xyz[1]; } + float getClZ() const { return xyz[2]; } + + float getDY() const { return xyz[1] - getYRef(); } + float getDZ() const { return xyz[2] - getZRef(); } + + float getYRef() const + { + float y = 0; + int n = 0; + if (below[0] > 1.) { + y += below[1]; + n++; + } + if (above[0] > 1.) { + y += above[1]; + n++; + } + return n == 1 ? y : 0.5 * y; + } + + float getZRef() const + { + float z = 0; + int n = 0; + if (below[0] > 1.) { + z += below[2]; + n++; + } + if (above[0] > 1.) { + z += above[2]; + n++; + } + return n == 1 ? z : 0.5 * z; + } + + float getDXMin() const + { + float adxA = 1e9, adxB = 1e9; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[1] - below[0]; + } + return std::abs(adxA) < std::abs(adxB) ? adxA : adxB; + } + + float getDXMax() const + { + float adxA = 0, adxB = 0; + if (above[0] > 1.) { + adxA = xyz[0] - above[0]; + } + if (below[0] > 1.) { + adxB = xyz[0] - below[0]; + } + return std::abs(adxA) > std::abs(adxB) ? adxA : adxB; + } + + float getEY() const { return getNExt() > 1 ? below[1] - above[1] : -999; } + float getEZ() const { return getNExt() > 1 ? below[2] - above[2] : -999; } + + ClassDefNV(ClResTPCCont, 1); +}; + +struct ClResTPC { + uint8_t sect = 0; + uint8_t row = 0; + uint8_t ncont = 0; + uint8_t flags = 0; + uint8_t sigmaTimePacked; + uint8_t sigmaPadPacked; + float qmax = 0; + float qtot = 0; + float occ = 0; + float occBin = 0; + float getSigmaPad() const { return float(sigmaPadPacked) * (1.f / 32); } + float getSigmaTime() const { return float(sigmaTimePacked) * (1.f / 32); } + + std::vector contTracks; + int getNCont() const { return contTracks.size(); } + + float getDY(int i) const { return i < getNCont() ? contTracks[i].getDY() : -999.; } + float getDZ(int i) const { return i < getNCont() ? contTracks[i].getDZ() : -999.; } + float getYRef(int i) const { return i < getNCont() ? contTracks[i].getYRef() : -999.; } + float getZRef(int i) const { return i < getNCont() ? contTracks[i].getZRef() : -999.; } + float getDXMin(int i) const { return i < getNCont() ? contTracks[i].getDXMin() : -999.; } + float getDXMax(int i) const { return i < getNCont() ? contTracks[i].getDXMax() : -999.; } + float getEY(int i) const { return i < getNCont() ? contTracks[i].getEY() : -999.; } + float getEZ(int i) const { return i < getNCont() ? contTracks[i].getEZ() : -999.; } + + void sortCont() + { + std::sort(contTracks.begin(), contTracks.end(), [](const ClResTPCCont& a, const ClResTPCCont& b) { + float dya = a.getDY(), dyb = b.getDY(), dza = a.getDZ(), dzb = b.getDZ(); + return dya * dya + dza * dza < dyb * dyb + dzb * dzb; + }); + } + + ClassDefNV(ClResTPC, 2); +}; + +struct ITSHitInfo { + o2::BaseCluster clus{}; + o2::TrackReference tref{}; + float trefXT = 0; // track ref tracking frame coordinates + float trefYT = 0; + float chipX = 0; + float chipAlpha = 0; + ClassDefNV(ITSHitInfo, 1); +}; + +struct RecPV { + o2::dataformats::PrimaryVertex pv{}; + o2::MCEventLabel mcEvLbl{}; + ClassDefNV(RecPV, 1); +}; + +struct MCVertex { + float getX() const { return pos[0]; } + float getY() const { return pos[1]; } + float getZ() const { return pos[2]; } + + std::array pos{0., 0., -1999.f}; + float ts = 0; + int nTrackSel = 0; // number of selected MC charged tracks + int ID = -1; + std::vector recVtx{}; + std::vector occTPCV{}; + ClassDefNV(MCVertex, 2); +}; + +} // namespace o2::trackstudy +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h index 99b35247081e6..b1a9f6923f04d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h @@ -17,9 +17,7 @@ #include "ReconstructionDataFormats/V0.h" #include "SimulationDataFormat/MCCompLabel.h" -namespace o2 -{ -namespace dataformats +namespace o2::dataformats { struct ProngInfoExt { @@ -28,11 +26,13 @@ struct ProngInfoExt { 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, 2); + ClassDefNV(ProngInfoExt, 3); }; struct V0Ext { @@ -40,10 +40,10 @@ struct V0Ext { V0Index v0ID; std::array prInfo{}; const ProngInfoExt& getPrInfo(int i) const { return prInfo[i]; } - ClassDefNV(V0Ext, 1); + int mcPID = -1; + ClassDefNV(V0Ext, 2); }; -} // namespace dataformats -} // namespace o2 +} // 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 dbf34b8eb14ad..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) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index 9b14e24d03cb4..416820fc9aebb 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -21,5 +21,32 @@ #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/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx index 8ce1c1cec3e01..0129d19b02346 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -22,6 +22,7 @@ #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" @@ -43,6 +44,13 @@ #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 { @@ -60,11 +68,11 @@ using V0ID = o2::dataformats::V0Index; using timeEst = o2::dataformats::TimeStampWithError; -class SVStudySpec : public Task +class SVStudySpec final : public Task { public: - SVStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} + 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; @@ -82,11 +90,18 @@ class SVStudySpec : public Task 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; - o2::steer::MCKinematicsReader mcReader; // reader of MC information + std::vector mTBinClOccAft, mTBinClOccBef; + std::unique_ptr mcReader; // reader of MC information + std::shared_ptr mParam = nullptr; }; void SVStudySpec::init(InitContext& ic) @@ -96,6 +111,9 @@ void SVStudySpec::init(InitContext& ic) 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) @@ -103,6 +121,48 @@ 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); } @@ -129,6 +189,14 @@ void SVStudySpec::updateTimeDependentParams(ProcessingContext& pc) 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); @@ -161,23 +229,37 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo v0ext.v0 = v0sel; } v0ext.v0ID = v0id; - o2::MCCompLabel lb; + 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 = recoData.getTrackMCLabel(gid); - if (lb.isValid()) { - prInfo.corrGlo = !lb.isFake(); + 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 = recoData.getTrackMCLabel(gidset[GTrackID::TPC]); - if (lb.isValid()) { - prInfo.corrTPC = !lb.isFake(); + 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 @@ -186,9 +268,9 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo if (gidset[GTrackID::ITS].isSourceSet()) { const auto& itsTr = recoData.getITSTrack(gidset[GTrackID::ITS]); prInfo.nClITS = itsTr.getNClusters(); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITS]); - if (lb.isValid()) { - prInfo.corrITS = !lb.isFake(); + 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)) { @@ -198,9 +280,9 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo } else { const auto& itsTrf = recoData.getITSABRefs()[gidset[GTrackID::ITSAB]]; prInfo.nClITS = itsTrf.getNClusters(); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITSAB]); - if (lb.isValid()) { - prInfo.corrITS = !lb.isFake(); + 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)) { @@ -211,13 +293,24 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo } if (gidset[GTrackID::ITSTPC].isSourceSet()) { auto mtc = recoData.getTPCITSTrack(gidset[GTrackID::ITSTPC]); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); prInfo.chi2ITSTPC = mtc.getChi2Match(); - if (lb.isValid()) { - prInfo.corrITSTPC = !lb.isFake(); + 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; } @@ -252,14 +345,19 @@ void SVStudySpec::process(o2::globaltracking::RecoContainer& recoData) } 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 + << "orbit=" << recoData.startIR.orbit << "tfID=" << tfID << "tpcOccBef=" << tpcOccBef << "tpcOccAft=" << tpcOccAft << "v0Ext=" << v0extVec << "pv=" << pv << "\n"; } - tfID++; } + tfID++; } bool SVStudySpec::refitV0(const V0ID& id, o2::dataformats::V0& v0, o2::globaltracking::RecoContainer& recoData) @@ -318,29 +416,30 @@ void SVStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -DataProcessorSpec getSVStudySpec(GTrackID::mask_t srcTracks, bool useMC) +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(false, // orbitResetTime - false, // GRPECS=true + 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, useMC)}, + 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"}}, 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/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index ca28aa24c9115..05e6a122adec9 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -47,7 +47,7 @@ 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, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool useMC) @@ -55,6 +55,7 @@ class TPCTrackStudySpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TPCTrackStudySpec() final = default; void init(InitContext& ic) final; @@ -184,9 +185,11 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) intRecs = digCont->getEventRecords(); mTPCTrkLabels = recoData.getTPCTracksMCLabels(); } - - 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 + 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; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 4a20e3d2e022d..8f6604b029605 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -13,14 +13,20 @@ #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 "ITSMFTBase/DPLAlpideParam.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" @@ -32,6 +38,8 @@ #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" @@ -41,10 +49,20 @@ #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 @@ -65,47 +83,83 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TrackMCStudy : public Task +class TrackMCStudy final : public Task { public: - TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool checkMatching) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckMatching(checkMatching) {} + 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(o2::globaltracking::RecoContainer& recoData); + void process(const o2::globaltracking::RecoContainer& recoData); private: - void prepareITSData(o2::globaltracking::RecoContainer& recoData); + 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; - bool mCheckMatching = false; float mITSTimeBiasMUS = 0.f; float mITSROFrameLengthMUS = 0.f; ///< ITS RO frame in mus float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds - float mTPCDCAYCut = 2.; - float mTPCDCAZCut = 2.; - float mMinX = 6.; - float mMaxEta = 0.8; - float mMinPt = 0.03; - int mMinTPCClusters = 10; - std::string mDCAYFormula = "0.0105 + 0.0350 / pow(x, 1.1)"; + 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 }; @@ -116,20 +170,31 @@ void TrackMCStudy::init(InitContext& ic) mDBGOut = std::make_unique("trackMCStudy.root", "recreate"); mVerbose = ic.options().get("device-verbosity"); - 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"); + + 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); } @@ -137,12 +202,22 @@ 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 @@ -153,25 +228,45 @@ void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc) 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(o2::globaltracking::RecoContainer& recoData) +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::LHCBunchSpacingNS * 1e-3; // ITS time is supplied in \mus as beginning of ROF - - if (mCheckMatching) { - prepareITSData(recoData); - } + 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) { - int16_t patt = 0; + int8_t patt = 0; if (gid.getSource() == VTIndex::ITSAB) { const auto& itsTrf = recoData.getITSABRefs()[gid]; ncl = itsTrf.getNClusters(); @@ -180,7 +275,7 @@ void TrackMCStudy::process(o2::globaltracking::RecoContainer& recoData) patt |= 0x1 << il; } } - patt = -patt; + patt |= 0x1 << 7; } else { const auto& itsTr = recoData.getITSTrack(gid); for (int il = 0; il < 7; il++) { @@ -193,23 +288,160 @@ void TrackMCStudy::process(o2::globaltracking::RecoContainer& recoData) return patt; }; - auto getLowestPadrow = [&recoData](const o2::tpc::TrackTPC& trc) { + auto fillTPCClusterInfo = [&recoData](const o2::tpc::TrackTPC& trc, RecTrack& tref) { if (recoData.inputsTPCclusters) { - uint8_t clSect = 0, clRow = 0; + uint8_t clSect = 0, clRow = 0, lowestR = -1; uint32_t clIdx = 0; const auto clRefs = recoData.getTPCTracksClusterRefs(); - trc.getClusterReference(clRefs, trc.getNClusterReferences() - 1, clSect, clRow, clIdx); - return int(clRow); + 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; } - return -1; }; - std::map> MCTRMap; + 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 > 0) { + 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); @@ -220,222 +452,526 @@ void TrackMCStudy::process(o2::globaltracking::RecoContainer& recoData) for (int i = idMin; i < idMax; i++) { auto vid = trackIndex[i]; const auto& trc = recoData.getTrackParam(vid); - if (trc.getPt() < mMinPt) { + if (trc.getPt() < params.minPt || std::abs(trc.getTgl()) > params.maxTgl) { continue; } auto lbl = recoData.getTrackMCLabel(vid); if (lbl.isValid()) { lbl.setFakeFlag(false); - auto& vvids = MCTRMap[lbl]; - if (vid.isAmbiguous() || vvids.empty()) { // do not repeat ambiguous tracks - bool skip = false; - for (const auto& va : vvids) { - if (va.second == vid) { - skip = true; - break; - } + 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 (skip) { + if (!acceptMCCharged((*mCurrMCTracks)[lbl.getTrackID()], lbl)) { continue; } + entry = mSelMCTracks.find(lbl); } - vvids.emplace_back(iv, vid); + 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; } } } } - o2::track::TrackParCov dummyTrackParCov; - o2::track::TrackPar dummyTrackPar; - dummyTrackParCov.invalidate(); - dummyTrackPar.invalidate(); - - const std::vector* mcTracks = nullptr; - o2::MCCompLabel prevLbl; - std::vector recTracks; - std::vector recGIDs; - std::vector recFakes; - std::vector lowestPadrows; - std::vector itsPatterns; - std::vector tpcNcls; - std::vector itsNcls; - LOGP(info, "Recorded {} reconstructed tracks", MCTRMap.size()); - size_t count = 0; - for (auto ent : MCTRMap) { - count++; - auto lbl = ent.first; - if (lbl.getEventID() != prevLbl.getEventID() || lbl.getSourceID() != prevLbl.getSourceID()) { - if (mVerbose > 0) { - LOGP(info, "Loading MC Event={} / Src={}", lbl.getEventID(), lbl.getSourceID()); - } - mcTracks = &mcReader.getTracks(lbl.getSourceID(), lbl.getEventID()); - prevLbl = lbl; - } - const auto& mcPart = (*mcTracks)[lbl.getTrackID()]; - int pdg = mcPart.GetPdgCode(), pdgParent = 0; - std::array xyz{(float)mcPart.GetStartVertexCoordinatesX(), (float)mcPart.GetStartVertexCoordinatesY(), (float)mcPart.GetStartVertexCoordinatesZ()}; - std::array pxyz{(float)mcPart.GetStartVertexMomentumX(), (float)mcPart.GetStartVertexMomentumY(), (float)mcPart.GetStartVertexMomentumZ()}; - TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(pdg); - if (!pPDG) { - LOGP(error, "Unknown particle {}, skip. Was at {} of {}", pdg, count, MCTRMap.size()); + + 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; } - o2::track::TrackPar mctrO2(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); - - bool primary = mcPart.isPrimary(); - auto parID = primary ? -1 : mcPart.getMotherTrackId(); - if (parID >= 0) { - const auto& mcPartPar = (*mcTracks)[parID]; - pdgParent = mcPartPar.GetPdgCode(); - } - auto& vgids = ent.second; - // make sure the more global tracks come 1st - if (vgids.size() > 1) { - std::sort(vgids.begin(), vgids.end(), [](VTIndexV& lhs, VTIndexV& rhs) { return lhs.second.getSource() > rhs.second.getSource(); }); - } - recTracks.clear(); - recGIDs.clear(); - recFakes.clear(); - lowestPadrows.clear(); - itsPatterns.clear(); - itsNcls.clear(); - tpcNcls.clear(); if (mVerbose > 1) { - LOGP(info, "[{}] Lbl:{} PDG:{:+5d} (par: {:+5d}) | MC: {}", vgids.size(), lbl.asString(), pdg, pdgParent, mctrO2.asString()); - } - int entITS = -1, entTPC = -1, entITSTPC = -1; - for (size_t i = 0; i < vgids.size(); i++) { - auto vid = vgids[i].second; - auto lbl = recoData.getTrackMCLabel(vid); - const auto& trc = recoData.getTrackParam(vid); - int16_t itsPatt = 0; - uint8_t nclITS = 0; - uint8_t nclTPC = 0; - if (mVerbose > 1) { - LOGP(info, " :{} {:22} | [{}] {}", lbl.asString(), vid.asString(), i, ((const o2::track::TrackPar&)trc).asString()); - } - recTracks.push_back(trc); - recGIDs.push_back(vid); - recFakes.push_back(recoData.getTrackMCLabel(vid).isFake()); - auto msk = vid.getSourceDetectorsMask(); - if (mCheckMatching) { - lowestPadrows.push_back(-1); - } - if (msk[DetID::ITS]) { - auto gidITS = recoData.getITSContributorGID(vid); - itsPatt = getITSPatt(gidITS, nclITS); - if (vid.getSource() == VTIndex::ITS) { - entITS = i; + 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]) { - entITSTPC = i; + 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()); } - if (msk[DetID::TPC]) { - if (vid.getSource() == VTIndex::TPC) { - entTPC = i; + 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 gidTPC = recoData.getTPCContributorGID(vid); - const auto& trtpc = recoData.getTPCTrack(gidTPC); - nclTPC = trtpc.getNClusters(); - if (mCheckMatching) { - auto& lr = lowestPadrows.back(); - lr = getLowestPadrow(recoData.getTPCTrack(recoData.getTPCContributorGID(vid))); + 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++; } - tpcNcls.push_back(nclTPC); - itsNcls.push_back(nclITS); - itsPatterns.push_back(itsPatt); - } - (*mDBGOut) << "tracks" - << "lbl=" << lbl - << "mcTr=" << mctrO2 - << "pdg=" << pdg - << "pdgPar=" << pdgParent - << "recTr=" << recTracks - << "recGID=" << recGIDs - << "recFake=" << recFakes - << "itsPatt=" << itsPatterns - << "nClITS=" << itsNcls - << "nClTPC=" << tpcNcls; - if (mCheckMatching) { - (*mDBGOut) << "tracks" - << "lowestPadRow=" << lowestPadrows; - } - (*mDBGOut) << "tracks" - << "\n"; - - // special ITS-TPC matching failure output - while (mCheckMatching) { - if (entITSTPC < 0 && entITS > -1 && entTPC > -1) { // ITS and TPC were found but matching failed - auto vidITS = vgids[entITS].second; - auto vidTPC = recoData.getTPCContributorGID(vgids[entTPC].second); // might be TPC match to outer detector, extract TPC - auto trcTPC = recoData.getTrackParam(vidTPC); - auto trcITS = recoData.getTrackParam(vidITS); - if (!propagateToRefX(trcTPC, trcITS)) { + 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; } - const auto& trcTPCOrig = recoData.getTPCTrack(vidTPC); - const auto& trcITSOrig = recoData.getITSTrack(vidITS); - int lowestTPCRow = lowestPadrows[entTPC]; - float tpcT0 = trcTPCOrig.getTime0(), tF = trcTPCOrig.getDeltaTFwd(), tB = trcTPCOrig.getDeltaTBwd(); - TBracket tpcBr((tpcT0 - tB) * mTPCTBinMUS, (tpcT0 + tF) * mTPCTBinMUS); - - (*mDBGOut) << "failMatch" - << "mcTr=" << mctrO2 - << "pdg=" << pdg - << "pdgPar=" << pdgParent - << "labelITS=" << recoData.getTrackMCLabel(vidITS) - << "labelTPC=" << recoData.getTrackMCLabel(vidTPC) - << "gidITS=" << vidITS - << "gidTPC=" << vidTPC - << "itsBracket=" << mITSROFBracket[mITSROF[vidITS.getIndex()]] - << "tpcBracket=" << tpcBr - << "itsRef=" << trcITS - << "tpcRef=" << trcTPC - << "itsOrig=" << trcITSOrig - << "tpcOrig=" << trcTPCOrig - << "itsPatt=" << itsPatterns[entITS] - << "tpcLowestRow=" << lowestTPCRow - << "\n"; - } else if (entITSTPC > -1) { // match was found - auto contribIDs = recoData.getSingleDetectorRefs(vgids[entITSTPC].second); - auto vidMatch = contribIDs[VTIndex::ITSTPC]; - auto vidTPC = contribIDs[VTIndex::TPC]; - auto vidITS = contribIDs[VTIndex::ITSAB].isSourceSet() ? contribIDs[VTIndex::ITSAB] : contribIDs[VTIndex::ITS]; - const auto& trcTPCOrig = recoData.getTPCTrack(vidTPC); - o2::MCCompLabel itsLb; - int nITScl = 0; - if (vidITS.getSource() == VTIndex::ITS) { - itsLb = recoData.getTrackMCLabel(vidITS); - nITScl = recoData.getITSTrack(vidITS).getNClusters(); - } else { - itsLb = recoData.getITSABMCLabels()[vidITS]; - nITScl = recoData.getITSABRefs()[vidITS].getNClusters(); + decFam.push_back(dtFamily); + } + if (!skip) { + o2::dataformats::V0 v0; + if (dec.foundSVID >= 0 && !refitV0(dec.foundSVID, v0, recoData)) { + v0.invalidate(); } - int lowestTPCRow = lowestPadrows[entITSTPC]; - const auto& trackITSTPC = recoData.getTPCITSTrack(vidMatch); - float timeTB = trackITSTPC.getTimeMUS().getTimeStamp() / o2::constants::lhc::LHCBunchSpacingMUS / 8; // ITS-TPC time in TPC timebins - - (*mDBGOut) << "match" - << "mcTr=" << mctrO2 - << "pdg=" << pdg - << "pdgPar=" << pdgParent - << "labelMatch=" << recoData.getTrackMCLabel(vidMatch) - << "labelTPC=" << recoData.getTrackMCLabel(vidTPC) - << "labelITS=" << itsLb - << "gidTPC=" << vidTPC - << "gidITS=" << vidITS - << "tpcOrig=" << trcTPCOrig - << "nClITS=" << itsNcls[entITSTPC] - << "itsPatt=" << itsPatterns[entITSTPC] - << "itstpc=" << ((o2::track::TrackParCov&)trackITSTPC) - << "matchChi2=" << trackITSTPC.getChi2Match() - << "refitChi2=" << trackITSTPC.getChi2Refit() - << "timeTB=" << timeTB - << "tpcLowestRow=" << lowestTPCRow - << "\n"; + (*mDBGOut) << decTreeName.c_str() << "pdgPar=" << dec.pdg << "trPar=" << dec.parent << "prod=" << decFam << "found=" << dec.foundSVID << "sv=" << v0 << "\n"; } - break; + } + } + + 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()); } } } @@ -487,6 +1023,9 @@ void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) 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(); @@ -495,13 +1034,18 @@ void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) 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(o2::globaltracking::RecoContainer& recoData) +void TrackMCStudy::prepareITSData(const o2::globaltracking::RecoContainer& recoData) { - auto ITSTracksArray = recoData.getITSTracks(); - auto ITSTrackROFRec = recoData.getITSTracksROFRecords(); + const auto ITSTracksArray = recoData.getITSTracks(); + const auto ITSTrackROFRec = recoData.getITSTracksROFRecords(); int nROFs = ITSTrackROFRec.size(); mITSROF.clear(); mITSROFBracket.clear(); @@ -518,23 +1062,335 @@ void TrackMCStudy::prepareITSData(o2::globaltracking::RecoContainer& recoData) } } } - +/* 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; +} -DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool checkMatching) +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 @@ -548,17 +1404,8 @@ DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask "track-mc-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, checkMatching)}, - Options{ - {"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"}}, - {"max-eta", VariantType::Float, 1.5f, {"Cut on track eta"}}, - {"min-pt", VariantType::Float, 0.02f, {"Cut on track pT"}}, - {"min-x-prop", VariantType::Float, 6.f, {"track should be propagated to this X at least"}}, - }}; + 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 fd90475a4c670..b8a8f97737b4d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -33,6 +33,7 @@ #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" @@ -43,11 +44,13 @@ #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "GPUO2InterfaceRefit.h" -#include "GPUO2Interface.h" // Needed for propper settings in GPUParam.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 { @@ -64,7 +67,7 @@ using TBracket = o2::math_utils::Bracketf_t; using timeEst = o2::dataformats::TimeStampWithError; -class TrackingStudySpec : public Task +class TrackingStudySpec final : public Task { public: TrackingStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) @@ -72,6 +75,7 @@ class TrackingStudySpec : public Task { mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } ~TrackingStudySpec() final = default; void init(InitContext& ic) final; @@ -92,7 +96,8 @@ class TrackingStudySpec : public Task std::unique_ptr mDBGOut; std::unique_ptr mDBGOutVtx; std::unique_ptr mTPCRefitter; ///< TPC refitter used for TPC tracks refit during the reconstruction - std::vector mTBinClOccAft, mTBinClOccBef; ///< TPC occupancy histo: i-th entry is the integrated occupancy for ~1 orbit starting/preceding from the TB = i*mNTPCOccBinLength + 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; @@ -103,11 +108,13 @@ class TrackingStudySpec : public Task 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{}; @@ -136,6 +143,15 @@ void TrackingStudySpec::init(InitContext& ic) 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) @@ -143,14 +159,16 @@ 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 - - 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(); + 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()) { @@ -159,24 +177,24 @@ void TrackingStudySpec::run(ProcessingContext& pc) int nTPCOccBins = nTPCBins * mNTPCOccBinLengthInv, sumBins = std::max(1, int(o2::constants::lhc::LHCMaxBunches / 8 * mNTPCOccBinLengthInv)); mTBinClOccAft.resize(nTPCOccBins); mTBinClOccBef.resize(nTPCOccBins); - std::vector mltHistTB(nTPCOccBins); float sm = 0., tb = 0.5 * mNTPCOccBinLength; + mMltHistTB.resize(nTPCOccBins); for (int i = 0; i < nTPCOccBins; i++) { - mltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); + mMltHistTB[i] = mTPCRefitter->getParam()->GetUnscaledMult(tb); tb += mNTPCOccBinLength; } for (int i = nTPCOccBins; i--;) { - sm += mltHistTB[i]; + sm += mMltHistTB[i]; if (i + sumBins < nTPCOccBins) { - sm -= mltHistTB[i + sumBins]; + sm -= mMltHistTB[i + sumBins]; } mTBinClOccAft[i] = sm; } sm = 0; for (int i = 0; i < nTPCOccBins; i++) { - sm += mltHistTB[i]; + sm += mMltHistTB[i]; if (i - sumBins > 0) { - sm -= mltHistTB[i - sumBins]; + sm -= mMltHistTB[i - sumBins]; } mTBinClOccBef[i] = sm; } @@ -240,21 +258,139 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) 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; - auto vdrit = mTPCVDriftHelper.getVDriftObject().getVDrift(); + 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); + for (int iv = 0; iv < nv; iv++) { LOGP(debug, "processing PV {} of {}", iv, nv); const auto& vtref = vtxRefs[iv]; 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; @@ -280,6 +416,7 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) 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); @@ -307,11 +444,11 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) } bool ambig = vid.isAmbiguous(); auto trc = recoData.getTrackParam(vid); - if (abs(trc.getEta()) > mMaxEta) { + 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 = vdrit * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + float corz = vdrift * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); if (tpcTr->hasASideClustersOnly()) { corz = -corz; // A-side } @@ -343,9 +480,39 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) 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]); @@ -388,11 +555,71 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) 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(); @@ -549,6 +776,9 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas {"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); 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 index fba5e67452f1f..7e104b82f4854 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/sv-study-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"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); @@ -61,10 +62,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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, useMC)); + 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); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 93e549dcc2ef3..7aa53e2190a9e 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -19,6 +19,8 @@ #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; @@ -39,9 +41,10 @@ void customize(std::vector& workflowOptions) {"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"}}, - {"check-its-tpc", VariantType::Bool, false, {"Special output for failed ITS-TPC matches"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -54,26 +57,34 @@ 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"); } - bool checkMatching = configcontext.options().get("check-its-tpc"); + 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(checkMatching ? "TPC" : "none"); + 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")); - if (checkMatching) { - srcCls |= GID::getSourcesMask("TPC"); + 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 - specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, checkMatching)); + 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); diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx deleted file mode 100644 index ab4f90464b31b..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}, mMatcher.getMatchedTrackVector()); - if (mUseMC) { - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MCMATCHTOF", 0}, mMatcher.getMatchedTOFLabelsVector()); - } - pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "CALIBDATA", 0}, 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/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 86064f84d881f..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, o2::dataformats::GlobalTrackID::mask_t src, o2::dataformats::GlobalTrackID::mask_t srcMap, std::shared_ptr gr, bool useMC, bool processITSTPConly, bool sendTrackData, bool debugOutput) : mDataRequest(dr), mSources(src), mSourcesMap(srcMap), 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; @@ -58,6 +59,7 @@ class TPCInterpolationDPL : public Task 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 @@ -65,7 +67,8 @@ class TPCInterpolationDPL : public Task }; /// create a processor spec -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); +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 4f1705533c965..99f20e390a09a 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/include/TPCInterpolationWorkflow/TPCResidualAggregatorSpec.h @@ -128,8 +128,9 @@ class ResidualAggregatorDevice : public o2::framework::Task updateTimeDependentParams(pc); std::chrono::duration ccdbUpdateTime = std::chrono::high_resolution_clock::now() - runStartTime; - // 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 @@ -145,14 +146,13 @@ 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(detail) << "Processing TF " << mAggregator->getCurrentTFInfo().tfCounter << " with " << trkData->size() << " tracks and " << residualsData.size() << " unbinned residuals associated to them"; - mAggregator->process(residualsData, trackRefs, trkDataPtr, lumi); + 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(), @@ -223,6 +223,7 @@ DataProcessorSpec getTPCResidualAggregatorSpec(bool trackInput, bool ctpInput, b 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"); 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 521a02cabcbee..4912a1df36a33 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -66,11 +66,12 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) initOnceDone = true; // other init-once stuff const auto& param = SpacePointsCalibConfParam::Instance(); + mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); + mInterpolation.setNHBPerTF(o2::base::GRPGeomHelper::getNHBFPerTF()); mInterpolation.init(mSources, mSourcesMap); if (mProcessITSTPConly) { mInterpolation.setProcessITSTPConly(); } - mInterpolation.setSqrtS(o2::base::GRPGeomHelper::instance().getGRPLHCIF()->getSqrtS()); 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; @@ -93,6 +94,11 @@ void TPCInterpolationDPL::updateTimeDependentParams(ProcessingContext& pc) 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()) { @@ -103,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) @@ -142,6 +144,7 @@ void TPCInterpolationDPL::run(ProcessingContext& pc) } } 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}, mInterpolation.getReferenceTracks()); @@ -158,7 +161,7 @@ void TPCInterpolationDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -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) +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; @@ -187,6 +190,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas } } 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); @@ -199,7 +203,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas "tpc-track-interpolation", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, 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)"}}, 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 55da5a5e71e44..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); } @@ -58,6 +63,7 @@ void TPCUnbinnedResidualReader::run(ProcessingContext& pc) LOG(info) << "Pushing " << mUnbinnedResid.size() << " unbinned residuals at entry " << currEntry; 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}, mTrackData); @@ -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 0905942c956a4..2f28fc5bb2d34 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -42,6 +42,7 @@ void customize(std::vector& workflowOptions) {"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); @@ -104,8 +105,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) useMC = false; // force disabling MC as long as it is not implemented auto sendTrackData = configcontext.options().get("send-track-data"); auto debugOutput = configcontext.options().get("debug-output"); + auto extDetResid = !configcontext.options().get("skip-ext-det-residuals"); - specs.emplace_back(o2::tpc::getTPCInterpolationSpec(srcClusters, srcVtx, srcTracks, srcTracksMap, 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)); } diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx index a127cf313d0e1..20e37c3bcc3b4 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-residual-aggregator.cxx @@ -14,10 +14,17 @@ #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) { @@ -27,6 +34,7 @@ void customize(std::vector& workflowOptions) {"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/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index da2461c2759ba..894c11864f061 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -32,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 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/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/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/src/EntropyDecoderSpec.cxx b/Detectors/HMPID/workflow/src/EntropyDecoderSpec.cxx index aa22979bc305f..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,7 +41,7 @@ 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(); @@ -91,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}, @@ -100,17 +99,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_HMP", "HMP", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_HMP", "HMP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("HMP/Calib/CTFDictionaryTree")); + + 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"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + 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 95723f42d0fd6..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(); @@ -89,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/CTFDictionaryTree")); + + 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); } @@ -103,13 +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"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace hmpid } // namespace o2 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/QC/TestDataReaderWorkflow/src/TestDataReader.cxx b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx index 964f342c58b15..90ed033ed67da 100644 --- a/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx +++ b/Detectors/ITSMFT/ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx @@ -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); } @@ -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/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 6172a02286abd..c8ef445e273d3 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -176,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; @@ -216,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 @@ -314,7 +314,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo 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); @@ -333,14 +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); - // 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 @@ -349,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. @@ -410,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 diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index b52fd8f58320f..5dc499d05037e 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -24,8 +24,6 @@ #ifdef ENABLE_UPGRADES #include "ITS3Base/SpecsV2.h" -#include "ITS3Base/SegmentationSuperAlpide.h" -using SuperSegmentation = o2::its3::SegmentationSuperAlpide; #endif #include // for TGeoBBox @@ -292,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(); } @@ -420,33 +412,20 @@ TGeoHMatrix* GeometryTGeo::extractMatrixSensor(int index) const static int chipInGlo{0}; // 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; -#ifdef ENABLE_UPGRADES - if (mIsLayerITS3[getLayer(index)]) { - delta = its3::SegmentationSuperAlpide::mSensorLayerThickness - its3::SegmentationSuperAlpide::mSensorLayerThicknessEff; - } -#endif - 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; } -//__________________________________________________________________________ -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 = getMatrixL2G(isn); - const auto& matL2Gi = matL2G.Inverse(); - t2l.MultiplyLeft(&matL2Gi); - return Mat3D(t2l); -} - //__________________________________________________________________________ void GeometryTGeo::Build(int loadTrans) { @@ -492,23 +471,6 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndex[i] = numberOfChips - 1; } - LOGP(debug, "Summary of extracted Geometry:"); - LOGP(debug, " There are {} Layers and {} HalfBarrels", mNumberOfLayers, mNumberOfHalfBarrels); - for (int i = 0; i < mNumberOfLayers; i++) { - LOGP(debug, " Layer {}: {:*^30}", i, "START"); - LOGP(debug, " - mNumberOfStaves={}", mNumberOfStaves[i]); - LOGP(debug, " - mNumberOfChipsPerStave={}", mNumberOfChipsPerStave[i]); - LOGP(debug, " - mNumberOfHalfStaves={}", mNumberOfHalfStaves[i]); - LOGP(debug, " - mNumberOfChipsPerHalfStave={}", mNumberOfChipsPerHalfStave[i]); - LOGP(debug, " - mNumberOfModules={}", mNumberOfModules[i]); - LOGP(debug, " - mNumberOfChipsPerModules={}", mNumberOfChipsPerModule[i]); - LOGP(debug, " - mNumberOfChipsPerLayer={}", mNumberOfChipsPerLayer[i]); - LOGP(debug, " - mNumberOfChipsPerHalfBarrel={}", mNumberOfChipsPerHalfBarrel[i]); - LOGP(debug, " - mLastChipIndex={}", mLastChipIndex[i]); - LOGP(debug, " Layer {}: {:*^30}", i, "END"); - } - LOGP(debug, "In total there {} chips registered", numberOfChips); - #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"); @@ -880,34 +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}; #ifdef ENABLE_UPGRADES - if (mIsLayerITS3[iLayer]) { - // We need to calcualte the line tangent at the mid-point in the geometry + 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 - const auto x = radius * std::cos(phi3); - const auto y = radius * std::sin(phi3); - // For the tangent we make the parametric line equation y = m * x - c - const auto m = x / y; - const auto c = y - m * x; - // Now we can given any x calulate points along this line, we pick points far away, - // the calculation of the normal should work then below. - locA[1] = m * locA[0] + c; - locB[1] = m * locB[0] + c; - } -#endif - + 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/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index 2ed11fc852c8b..dd6aacf65db99 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -113,3 +113,8 @@ 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/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/CheckSquasher.C b/Detectors/ITSMFT/ITS/macros/test/CheckSquasher.C index 70bdb46abfe37..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,17 +33,38 @@ #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 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"); @@ -78,55 +100,7 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi 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) { - LOGP(info, "Processing cluster {}", clusInd); - 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); - 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; - } - } - } - } - } + 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); @@ -141,7 +115,81 @@ void CheckSquasher(const uint chipId = 0, const uint startingROF = 0, const unsi canvasSuperimposition->cd(); gPad->SetGridx(); gPad->SetGridy(); - hHitMapSuperimposed->Draw("colz"); + 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) @@ -168,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/CheckTracksCA.C b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C index c00e0ccbfe016..e185be83a389f 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckTracksCA.C @@ -21,16 +21,21 @@ #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" @@ -41,6 +46,19 @@ 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; @@ -60,11 +78,15 @@ struct ParticleInfo { 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, +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", @@ -87,6 +109,7 @@ void CheckTracksCA(bool doFakeClStud = false, 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); @@ -115,12 +138,18 @@ void CheckTracksCA(bool doFakeClStud = false, 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(); @@ -130,6 +159,7 @@ void CheckTracksCA(bool doFakeClStud = false, 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; @@ -196,7 +226,6 @@ void CheckTracksCA(bool doFakeClStud = false, 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]; - Info("", "dcaxy=%f dcaz=%f bz=%f", ip[0], ip[1], bz); } fakes += fake; @@ -211,113 +240,108 @@ void CheckTracksCA(bool doFakeClStud = false, 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; @@ -357,10 +381,10 @@ void CheckTracksCA(bool doFakeClStud = false, 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) { @@ -402,5 +426,237 @@ void CheckTracksCA(bool doFakeClStud = false, 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/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/postprocessing/studies/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt index fd9074d5b6b6a..9794b69631d57 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt @@ -10,22 +10,26 @@ # 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/Helpers.cxx - PUBLIC_LINK_LIBRARIES O2::GlobalTracking - O2::GlobalTrackingWorkflowReaders - O2::GlobalTrackingWorkflowHelpers - O2::DataFormatsGlobalTracking - O2::DetectorsVertexing - O2::DetectorsBase) +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) \ No newline at end of file +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/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/ITSStudiesConfigParam.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h index 5884a0cadd815..85e114e0fb739 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h @@ -94,6 +94,13 @@ struct AnomalyStudyParamConfig : 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; diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h index ef1d9faf86b83..03f52aae380c5 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackCuts.h @@ -76,7 +76,7 @@ class TrackCuts 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++; } 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 index c3e6eadc979f4..55f92843cd14d 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackMethods.h @@ -53,7 +53,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++; } 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/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 index 70b9bfb64dfd5..a5b3495047934 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/Helpers.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include +#include // o2 includes #include "ITSStudies/Helpers.h" diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx index 3f9ea07bf1f51..c0b2d2863f3cc 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx @@ -22,12 +22,14 @@ 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 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h index 2ceae2ea981f6..d56d718390b47 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h @@ -19,11 +19,13 @@ #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 index 5ca1bf2bd5c8f..c0aaabddaca1b 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx @@ -358,7 +358,7 @@ void ImpactParameterStudy::process(o2::globaltracking::RecoContainer& recoData) auto trueID = trueVec_globID_contr[it]; const o2::track::TrackParCov& trc = recoData.getTrackParam(trueID); auto pt = trc.getPt(); - o2::gpu::gpustd::array dcaInfo{-999., -999.}; + 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)) { diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx index 4b0f553eb774b..9a7f6c218cd12 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx @@ -91,7 +91,7 @@ class PIDStudy : public Task std::shared_ptr gr, bool isMC, std::shared_ptr kineReader) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC), mKineReader(kineReader){}; - ~PIDStudy() final = default; + ~PIDStudy() override = default; void init(InitContext& ic) final; void run(ProcessingContext&) final; void endOfStream(EndOfStreamContext&) final; 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/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index 5a6975df35472..30fb39c77f235 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -22,7 +22,9 @@ #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; @@ -49,8 +51,10 @@ void customize(std::vector& workflowOptions) {"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"); + // o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); std::swap(workflowOptions, options); } @@ -75,8 +79,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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); - // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); - // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); specs.emplace_back(o2::its::study::getImpactParameterStudy(srcTrc, srcCls, useMC)); } if (configcontext.options().get("cluster-size-study")) { @@ -84,8 +86,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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); - // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); - // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); specs.emplace_back(o2::its::study::getAvgClusSizeStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); } if (configcontext.options().get("pid-study")) { @@ -93,8 +93,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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); - // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); - // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); specs.emplace_back(o2::its::study::getPIDStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); } if (configcontext.options().get("track-study")) { @@ -105,20 +103,34 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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)); - // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); - // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); } if (configcontext.options().get("anomaly-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); } - // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); - // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); 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"); } @@ -128,4 +140,4 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); return std::move(specs); -} \ No newline at end of file +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index 3e1544c65b9de..d2126be1da2c6 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -10,10 +10,7 @@ # or submit itself to any jurisdiction. o2_add_library(ITSReconstruction - SOURCES src/ClustererTask.cxx - src/CookedTracker.cxx - src/CookedConfigParam.cxx - src/RecoGeomHelper.cxx + SOURCES src/RecoGeomHelper.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase @@ -23,10 +20,6 @@ o2_add_library(ITSReconstruction 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/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 5c804f6705dfd..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 * (int)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 * (int)kNSectors; - int ds = (phi + dphi) / k2PI * (int)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 a55fafdf60409..c547996c6f356 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx @@ -125,7 +125,7 @@ float FastMultEst::processNoiseImposed(const std::array ncl) } 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 8f2efef0b34cd..712ec6a022d16 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/RecoGeomHelper.cxx @@ -229,9 +229,9 @@ void RecoGeomHelper::RecoLayer::print() const } //_____________________________________________________________________ -void RecoGeomHelper::init() +void RecoGeomHelper::init(int minLayer, int maxLayer) { - for (int il = int(layers.size()); il--;) { + for (int il = maxLayer; --il >= 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 c48f0f942d29c..57301ac4babd0 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/Detector.h @@ -159,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 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 e90f0cfeb0aed..7844f42601a47 100644 --- a/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h +++ b/Detectors/ITSMFT/ITS/simulation/include/ITSSimulation/V3Cage.h @@ -113,6 +113,20 @@ class V3Cage : public V11Geometry /// \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 @@ -244,6 +258,29 @@ class V3Cage : public V11Geometry 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/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index d005c9ea9858a..63d7a8ad8dfa2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -20,6 +20,7 @@ #include "ITSSimulation/V3Layer.h" #include "ITSSimulation/V3Services.h" #include "ITSSimulation/V3Cage.h" +#include "ITSSimulation/ITSSimParam.h" #include "DetectorsBase/Stack.h" #include "SimulationDataFormat/TrackReference.h" @@ -41,6 +42,7 @@ #include "TVirtualMC.h" // for gMC, TVirtualMC #include "TVirtualMCStack.h" // for TVirtualMCStack #include "TFile.h" // for TVirtualMCStack +#include "TGeoParallelWorld.h" #include // for NULL, snprintf #include @@ -59,6 +61,7 @@ using Segmentation = o2::itsmft::SegmentationAlpide; using namespace o2::its; #ifdef ENABLE_UPGRADES +#include "ITS3Simulation/DescriptorInnerBarrelITS3.h" using namespace o2::its3; #endif @@ -143,10 +146,10 @@ Detector::Detector(Bool_t active, TString name) 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()); + mDescriptorIB = std::make_shared(); #endif } else { LOG(fatal) << "Detector name not supported (options ITS and ITS3)"; @@ -188,7 +191,7 @@ Detector::Detector(Bool_t active, TString name) } else { mLayerName[j].Form("%s%d", GeometryTGeo::getITSSensorPattern(), j); // See V3Layer } - LOGP(info, "{}: mLayerName={}", j, mLayerName[j].Data()); + LOGP(debug, "{}: mLayerName={}", j, mLayerName[j].Data()); } if (mNumberLayers > 0) { // if not, we'll Fatal-ize in CreateGeometry @@ -475,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.}; @@ -721,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; @@ -1103,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; @@ -1114,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 @@ -1145,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 @@ -1174,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 @@ -1202,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 @@ -1233,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 @@ -1263,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, @@ -1308,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) 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 3b17d7afeef3d..bd9ce1cd333a2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/V3Cage.cxx @@ -167,6 +167,28 @@ 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() @@ -251,6 +273,9 @@ void V3Cage::createAndPlaceCage(TGeoVolume* mother, const TGeoManager* mgr) zpos = sBPSuppZPos + sBPSuppCollarBeamWid / 2; mother->AddNode(cageBPSupport, 1, new TGeoTranslation(0, ypos, zpos)); + // The MFT Rails inside the Cage + createAndPlaceMFTRailsInsideCage(mother, mgr); + return; } @@ -1648,3 +1673,240 @@ TGeoVolume* V3Cage::createCageClosingCross(const TGeoManager* mgr) // 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/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index d3667294d6c61..001ee537f50d2 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -14,11 +14,8 @@ o2_add_library(ITStracking SOURCES src/ClusterLines.cxx src/Cluster.cxx src/Configuration.cxx - src/ROframe.cxx src/TimeFrame.cxx src/IOUtils.cxx - src/Label.cxx - src/Road.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx @@ -35,13 +32,11 @@ o2_add_library(ITStracking O2::ITSBase O2::ITSReconstruction O2::ITSMFTReconstruction - O2::DataFormatsITS) - - -if (OpenMP_CXX_FOUND) - target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) - target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) -endif() + O2::DataFormatsITS + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) +# target_compile_options(${targetName} PRIVATE -O0 -g -fPIC -fno-omit-frame-pointer) o2_add_library(ITSTrackingInterface TARGETVARNAME targetName @@ -51,11 +46,6 @@ o2_add_library(ITSTrackingInterface O2::Framework O2::GPUTracking) -if (OpenMP_CXX_FOUND) - target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) - target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) -endif() - o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h include/ITStracking/Tracklet.h @@ -66,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 f4f73e715c305..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Array.h +++ /dev/null @@ -1,62 +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" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -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); - } -}; - -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 ecb25b7bc9d71..75d75e0f67700 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h @@ -57,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 bfc4c63756e0b..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Context.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file Context.h -/// \brief -/// - -#ifndef ITSTRACKINGGPU_CONTEXT_H_ -#define ITSTRACKINGGPU_CONTEXT_H_ - -#include -#include -#include "ITStracking/Definitions.h" - -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 a5e859475521c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/PrimaryVertexContextGPU.h +++ /dev/null @@ -1,144 +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 - -#include -#include - -#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 b6fbbe166cafe..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Stream.h +++ /dev/null @@ -1,44 +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" - -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 2a2fadb21caeb..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -13,259 +13,216 @@ #ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H #define TRACKINGITSGPU_INCLUDE_TIMEFRAMEGPU_H -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Configuration.h" - -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/Array.h" -#include "ITStrackingGPU/Vector.h" -#include "ITStrackingGPU/Stream.h" - #include +#include -namespace o2 -{ -namespace gpu -{ -class GPUChainITS; -} -namespace its -{ -template -struct gpuPair { - T1 first; - T2 second; -}; - -namespace gpu -{ - -class DefaultGPUAllocator : public ExternalAllocator -{ - void* allocate(size_t size) override; -}; -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 -}; +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "ITStrackingGPU/Utils.h" -template -class GpuTimeFrameChunk +namespace o2::its::gpu { - 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; -}; template -class TimeFrameGPU : public TimeFrame +class TimeFrameGPU final : public TimeFrame { - public: - friend class GpuTimeFrameChunk; + using typename TimeFrame::CellSeedN; + using typename TimeFrame::IndexTableUtilsN; - TimeFrameGPU(); - ~TimeFrameGPU(); + public: + 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 initialiseHybrid(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 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 loadTrackingFrameInfoDevice(const int); - void loadUnsortedClustersDevice(); - void loadClustersDevice(); + 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(std::vector&); - void createCellNeighboursDevice(const unsigned int& layer, std::vector>& neighbours); - void createTrackITSExtDevice(std::vector&); - void downloadTrackITSExtDevice(std::vector&); - 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 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& getTrackITSExt() { return mTrackITSExt; } - 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; } - gpuPair* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } + 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); - TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() { return mTrackingFrameInfoDeviceArray; } - Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } - Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } - Tracklet** getDeviceArrayTracklets() const { return mTrackletsDeviceArray; } - CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } - CellSeed* getDeviceTrackSeeds() { return mTrackSeedsDevice; } + 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; } - void setDevicePropagator(const o2::base::PropagatorImpl*) override; + 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: - void allocMemAsync(void**, size_t, Stream*, bool); // Abstract owned and unowned memory allocations - 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; - std::array mUsedClustersDevice; - Vertex* mVerticesDevice; - int* mROframesPVDevice; + IndexTableUtilsN* mIndexTableUtilsDevice; // Hybrid pref + uint8_t* mMultMaskDevice; + Vertex* mPrimaryVerticesDevice; + int* mROFramesPVDevice; std::array mClustersDevice; std::array mUnsortedClustersDevice; - Cluster** mClustersDeviceArray; - Cluster** mUnsortedClustersDeviceArray; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; + const Cluster** mClustersDeviceArray; + const Cluster** mUnsortedClustersDeviceArray; + const int** mClustersIndexTablesDeviceArray; + uint8_t** mUsedClustersDeviceArray; + const int** mROFramesClustersDeviceArray; std::array mTrackletsDevice; - Tracklet** mTrackletsDeviceArray; - std::array mCellsDevice; - CellSeed* mTrackSeedsDevice; - CellSeed** mCellsDeviceArray; + 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; @@ -273,50 +230,69 @@ class TimeFrameGPU : public TimeFrame Road* mRoadsDevice; TrackITSExt* mTrackITSExtDevice; - std::array*, nLayers - 2> mNeighboursDevice; + std::array*, nLayers - 2> mNeighbourPairsDevice; + std::array mNeighboursDevice; std::array mTrackingFrameInfoDevice; - TrackingFrameInfo** mTrackingFrameInfoDeviceArray; + 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 memory 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 - std::vector mTrackITSExt; + 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 + +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/TrackerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h index 076523261ff7e..7d26e74692aa5 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -13,39 +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 computeLayerCells() final; - void adoptTimeFrame(TimeFrame* tf) override; - void initialiseTimeFrame(const int iteration) override; void computeLayerTracklets(const int iteration, int, int) final; - void computeLayerCells(const int iteration) override; - void setBz(float) override; - void findCellsNeighbours(const int iteration) override; - void findRoads(const int iteration) override; + void computeLayerCells(const int iteration) final; + void findCellsNeighbours(const int iteration) final; + void findRoads(const int iteration) final; - // Methods to get CPU execution from traits - void initialiseTimeFrameHybrid(const int iteration) override { initialiseTimeFrame(iteration); }; - void computeTrackletsHybrid(const int iteration, int, int) override; - void computeCellsHybrid(const int iteration) override; - void findCellsNeighboursHybrid(const int iteration) override; + bool supportsExtendTracks() const noexcept final { return false; } + bool supportsFindShortPrimaries() const noexcept final { return false; } - void extendTracks(const int iteration) override; + 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; @@ -53,18 +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); - mTimeFrame = 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 index 3fea14af708a8..53992ccf3eb85 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -13,55 +13,242 @@ #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 -{ -namespace its +namespace o2::its { +template class CellSeed; -namespace gpu -{ -#ifdef GPUCA_GPUCODE // GPUg() global kernels must only when compiled by GPU compiler -GPUd() bool fitTrack(TrackITSExt& track, - int start, - int end, - int step, - float chi2clcut, - float chi2ndfcut, - float maxQoverPt, - int nCl, - float Bz, - TrackingFrameInfo** tfInfos, - const o2::base::Propagator* prop, - o2::base::PropagatorF::MatCorrType matCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE); +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 -GPUg() void fitTrackSeedsKernel( - CellSeed* trackSeeds, - TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - const size_t nSeeds, - const float Bz, - const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType = o2::base::PropagatorF::MatCorrType::USEMatCorrLUT); -#endif -} // namespace gpu +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); -void trackSeedHandler(CellSeed* trackSeeds, - TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - const size_t nSeeds, - const float Bz, - const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType); -} // namespace its -} // namespace o2 +} // 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 fda67bb619d37..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,48 +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; - - // Hybrid - void initialiseHybrid(const TrackingParameters& pars) override { VertexerTraits::initialise(pars); } - void adoptTimeFrameHybrid(TimeFrame* tf) override { VertexerTraits::adoptTimeFrame(tf); } - void computeTrackletsHybrid() override { VertexerTraits::computeTracklets(); } - void computeTrackletMatchingHybrid() override { VertexerTraits::computeTrackletMatching(); } - void computeVerticesHybrid() override { VertexerTraits::computeVertices(); } + 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 updateVertexingParameters(const VertexingParameters&, const TimeFrameGPUParameters&) override; - - 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); - mTimeFrame = 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 cb37f7fb1966e..e38dbb1ef20e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -11,31 +11,29 @@ # CUDA if(CUDA_ENABLED) -find_package(CUDAToolkit) -message(STATUS "Building ITS CUDA tracker") - -o2_add_library(ITStrackingCUDA - SOURCES ClusterLinesGPU.cu - Context.cu - Stream.cu - TrackerTraitsGPU.cxx - TimeFrameGPU.cu - TracerGPU.cu - TrackingKernels.cu - VertexerTraitsGPU.cu - Utils.cu - PUBLIC_INCLUDE_DIRECTORIES ../ - PUBLIC_LINK_LIBRARIES O2::ITStracking - O2::SimConfig - O2::SimulationDataFormat - O2::ReconstructionDataFormats - O2::GPUCommon - CUDA::nvToolsExt # TODO: change to CUDA::nvtx3 when CMake bump >= 3.25 - PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider - TARGETVARNAME targetName) - -set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) -target_compile_definitions(${targetName} PRIVATE $) -set_target_cuda_arch(${targetName}) + 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) + 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/Context.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu deleted file mode 100644 index f3bced9463020..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Context.cu +++ /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. - -#include -#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 885587d8d4544..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Stream.cu +++ /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. -/// - -#include -#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 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 e758bf4990c4f..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -9,611 +9,711 @@ // 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 +#include -#include "ITStrackingGPU/Utils.h" #include "ITStrackingGPU/TimeFrameGPU.h" -#include "ITStrackingGPU/TracerGPU.h" - -#include -#include +#include "ITStracking/Constants.h" +#include "ITStracking/BoundedAllocator.h" +#include "ITStrackingGPU/Utils.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" #include "GPUCommonLogger.h" +#include "GPUCommonHelpers.h" +#include "utils/strtag.h" -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2 +namespace o2::its::gpu { -namespace its + +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) { -using constants::GB; -using constants::MB; + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMallocAsync(reinterpret_cast(ptr), size, stream.get())); + } +} -namespace gpu +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) { -using utils::checkGPUError; + if (extAllocator) { + *ptr = (this->mExternalAllocator)->allocate(size, type); + } else { + GPULog("Calling default CUDA allocator"); + GPUChkErrS(cudaMalloc(reinterpret_cast(ptr), size)); + } +} -void* DefaultGPUAllocator::allocate(size_t size) +template +void TimeFrameGPU::loadIndexTableUtils(const int iteration) { - LOGP(info, "Called DefaultGPUAllocator::allocate with size {}", size); - return nullptr; // to be implemented + 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)); } -///////////////////////////////////////////////////////////////////////////////////////// -// GpuChunk -///////////////////////////////////////////////////////////////////////////////////////// template -GpuTimeFrameChunk::~GpuTimeFrameChunk() +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) { - 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])); - checkGPUError(cudaFree(mRoadsLookupTablesDevice[i])); - if (i < nLayers - 3) { - checkGPUError(cudaFree(mNeighboursCellLookupTablesDevice[i])); - checkGPUError(cudaFree(mNeighboursCellDevice[i])); - } - } + 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(mRoadsDevice)); - checkGPUError(cudaFree(mCUBTmpBufferDevice)); - checkGPUError(cudaFree(mFoundTrackletsDevice)); - checkGPUError(cudaFree(mNFoundCellsDevice)); - checkGPUError(cudaFree(mCellsDeviceArray)); - checkGPUError(cudaFree(mNeighboursCellDeviceArray)); - checkGPUError(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) { - 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(CellSeed) * mTFGPUParams->maxNeighboursSize * nrof, stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mRoadsLookupTablesDevice[i]), sizeof(int) * 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())); - // checkGPUError(cudaMallocAsync(reinterpret_cast(&mRoadsDevice), sizeof(Road) * mTFGPUParams->maxRoadPerRofSize * 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())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNeighboursCellDeviceArray), (nLayers - 3) * sizeof(int*), stream.get())); - checkGPUError(cudaMallocAsync(reinterpret_cast(&mNeighboursCellLookupTablesDeviceArray), (nLayers - 3) * sizeof(int*), stream.get())); +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())); + } +} - /// Copy pointers of allocated memory to regrouping arrays - checkGPUError(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, stream.get())); - checkGPUError(cudaMemcpyAsync(mNeighboursCellDeviceArray, mNeighboursCellDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); - checkGPUError(cudaMemcpyAsync(mNeighboursCellLookupTablesDeviceArray, mNeighboursCellLookupTablesDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); +template +void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) +{ + 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); + } + } + } +} - mAllocated = true; +template +void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) +{ + 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())); + } } template -void GpuTimeFrameChunk::reset(const Task task, Stream& stream) +void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) { - RANGE("buffer_reset", 0); - if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) - for (int i = 0; i < 2; i++) { - auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - checkGPUError(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - } - 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())); - checkGPUError(cudaMemsetAsync(mRoadsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * 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 used clusters flags"); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(nLayers); + if (!this->hasFrameworkAllocator()) { + for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + GPUChkErrS(cudaHostRegister(this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(uint8_t), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(iLayer); } } - checkGPUError(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); } } template -size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) +void TimeFrameGPU::createUsedClustersDevice(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(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 + 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())); + } +} - rofsize += (nLayers - 1) * sizeof(int); // total found tracklets - rofsize += (nLayers - 2) * sizeof(int); // total found cells +template +void TimeFrameGPU::loadUsedClustersDevice() +{ + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); + GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } +} - return rofsize * nrof; +template +void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) +{ + 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); + } + } + } } template -size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) +void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) { - size_t total = config.tmpCUBBufferSize; // CUB tmp buffers - total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once - return total; + 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 -size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) +void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) { - return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); + 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); + } + } + } } -/// Interface template -Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) { - return mClustersDevice[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 -// TrackingFrameInfo* GpuTimeFrameChunk::getDeviceTrackingFrameInfo(const int layer) -// { -// return mTrackingFrameInfoDevice[layer]; -// } +template +void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) +{ + 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 -int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) +void TimeFrameGPU::loadVertices(const int iteration) { - return mClusterExternalIndicesDevice[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::getDeviceIndexTables(const int layer) +void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { - return mIndexTablesDevice[layer]; + if (!iteration) { + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); + } } template -Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) { - return mTrackletsDevice[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::getDeviceTrackletsLookupTables(const int layer) +void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { - return mTrackletsLookupTablesDevice[layer]; + if (!iteration) { + GPUTimer timer("creating tracklet buffers array"); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); + } } template -CellSeed* GpuTimeFrameChunk::getDeviceCells(const int layer) +void TimeFrameGPU::createTrackletsBuffers(const int layer) { - return mCellsDevice[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::getDeviceCellsLookupTables(const int layer) +void TimeFrameGPU::loadTrackletsDevice() { - return mCellsLookupTablesDevice[layer]; + GPUTimer timer(mGpuStreams, "loading tracklets", nLayers - 1); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + GPULog("gpu-transfer: loading {} tracklets on layer {}, for {:.2f} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / constants::MB); + GPUChkErrS(cudaHostRegister(this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); + GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } } template -int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) +void TimeFrameGPU::loadTrackletsLUTDevice() { - return mNeighboursCellLookupTablesDevice[layer]; + GPUTimer timer("loading tracklets"); + for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPULog("gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {:.2f} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / constants::MB); + GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); + } + mGpuStreams.sync(); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } template -int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) +void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) { - return mNeighboursCellDevice[layer]; + 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 -int* GpuTimeFrameChunk::getDeviceRoadsLookupTables(const int layer) +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) { - return mRoadsLookupTablesDevice[layer]; + 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())); } -// Load data template -size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) +void TimeFrameGPU::loadCellsDevice() { - RANGE("load_clusters_data", 5); - auto nRofs = std::min(maxRof - startRof, mNRof); - mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); - for (int i = 0; i < maxLayers; ++i) { - mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); - mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); - if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { - LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); - } - checkGPUError(cudaMemcpyAsync(mClustersDevice[i], - mHostClusters[i].data(), - (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), - cudaMemcpyHostToDevice, stream.get())); - if (mHostIndexTables[i].data()) { - checkGPUError(cudaMemcpyAsync(mIndexTablesDevice[i], - mHostIndexTables[i].data(), - mHostIndexTables[i].size() * sizeof(int), - cudaMemcpyHostToDevice, stream.get())); - } + GPUTimer timer(mGpuStreams, "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())); } - return mNPopulatedRof; // return the number of ROFs we loaded the data for. } -///////////////////////////////////////////////////////////////////////////////////////// -// TimeFrameGPU -///////////////////////////////////////////////////////////////////////////////////////// template -TimeFrameGPU::TimeFrameGPU() +void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { - mIsGPU = true; - utils::getDeviceProp(0, true); + if (!iteration) { + GPUTimer timer("creating cells LUTs array"); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); + } } template -TimeFrameGPU::~TimeFrameGPU() = default; +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::allocMemAsync(void** ptr, size_t size, Stream* strPtr, bool extAllocator) +void TimeFrameGPU::createCellsBuffersArray(const int iteration) { - if (extAllocator) { - *ptr = mAllocator->allocate(size); - } else { - LOGP(info, "Calling default CUDA allocator"); - checkGPUError(cudaMallocAsync(reinterpret_cast(ptr), size, strPtr->get())); + 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)); } } template -void TimeFrameGPU::setDevicePropagator(const o2::base::PropagatorImpl* propagator) +void TimeFrameGPU::createCellsBuffers(const int layer) { - mPropagatorDevice = propagator; + 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::registerHostMemory(const int maxLayers) +void TimeFrameGPU::loadCellsLUTDevice() { - if (mHostRegistered) { - return; - } else { - mHostRegistered = true; - } - for (auto iLayer{0}; iLayer < maxLayers; ++iLayer) { - checkGPUError(cudaHostRegister(mClusters[iLayer].data(), mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mNClustersPerROF[iLayer].data(), mNClustersPerROF[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaHostRegister(mIndexTables[iLayer].data(), (mStaticTrackingParams.ZBins * mStaticTrackingParams.PhiBins + 1) * mNrof * sizeof(int), cudaHostRegisterPortable)); + 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(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::loadRoadsDevice() { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaHostUnregister(mTrackingFrameInfo[iLayer].data())); - } - checkGPUError(cudaHostUnregister(mTrackingFrameInfoDevice.data())); + 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)); } template -void TimeFrameGPU::initialise(const int iteration, - const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtils* utils, - const TimeFrameGPUParameters* gpuParam) +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) { - mGpuStreams.resize(mGpuParams.nTimeFrameChunks); - mHostNTracklets.resize((nLayers - 1) * mGpuParams.nTimeFrameChunks, 0); - mHostNCells.resize((nLayers - 2) * mGpuParams.nTimeFrameChunks, 0); - - 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(); + 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::initialiseHybrid(const int iteration, - const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtils* utils, - const TimeFrameGPUParameters* gpuParam) +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) { - mGpuStreams.resize(mGpuParams.nTimeFrameChunks); - o2::its::TimeFrame::initialise(iteration, trkParam, 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::wipe(const int maxLayers) +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { - unregisterHostMemory(maxLayers); + 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::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::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]); + 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()); } - 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)); - } - checkGPUError(cudaMalloc(reinterpret_cast(&mVerticesDevice), sizeof(Vertex) * mGpuParams.maxVerticesCapacity)); - checkGPUError(cudaMalloc(reinterpret_cast(&mROframesPVDevice), sizeof(int) * (mNrof + 1))); - - mFirstInit = false; + 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())); } - if (maxLayers < nLayers) { // Vertexer - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemcpy(mROframesClustersDevice[iLayer], mROframesClusters[iLayer].data(), mROframesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); - } - } else { // Tracker - checkGPUError(cudaMemcpy(mVerticesDevice, mPrimaryVertices.data(), sizeof(Vertex) * mPrimaryVertices.size(), cudaMemcpyHostToDevice)); - checkGPUError(cudaMemcpy(mROframesPVDevice, mROframesPV.data(), sizeof(int) * mROframesPV.size(), cudaMemcpyHostToDevice)); - if (!iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - checkGPUError(cudaMemset(mUsedClustersDevice[iLayer], 0, sizeof(unsigned char) * mGpuParams.clustersPerROfCapacity * mNrof)); - } - } + 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)); + + allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); } - checkGPUError(cudaMemcpy(mIndexTableUtilsDevice, &mIndexTableUtils, sizeof(IndexTableUtils), cudaMemcpyHostToDevice)); } template -void TimeFrameGPU::loadUnsortedClustersDevice() +void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} unsorted clusters on layer {}, for {} MB.", mUnsortedClusters[iLayer].size(), iLayer, mUnsortedClusters[iLayer].size() * sizeof(Cluster) / MB); - allocMemAsync(reinterpret_cast(&mUnsortedClustersDevice[iLayer]), mUnsortedClusters[iLayer].size() * sizeof(Cluster), nullptr, getExtAllocator()); - // Register and move data - checkGPUError(cudaHostRegister(mUnsortedClusters[iLayer].data(), mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mUnsortedClustersDevice[iLayer], mUnsortedClusters[iLayer].data(), mUnsortedClusters[iLayer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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()); } - allocMemAsync(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mUnsortedClustersDeviceArray, mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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::loadClustersDevice() +void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} clusters on layer {}, for {} MB.", mClusters[iLayer].size(), iLayer, mClusters[iLayer].size() * sizeof(Cluster) / MB); - allocMemAsync(reinterpret_cast(&mClustersDevice[iLayer]), mClusters[iLayer].size() * sizeof(Cluster), nullptr, getExtAllocator()); - // Register and move data - checkGPUError(cudaHostRegister(mClusters[iLayer].data(), mClusters[iLayer].size() * sizeof(Cluster), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mClustersDevice[iLayer], mClusters[iLayer].data(), mClusters[iLayer].size() * sizeof(Cluster), cudaMemcpyHostToDevice, mGpuStreams[0].get())); - } - allocMemAsync(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mClustersDeviceArray, mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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::loadTrackingFrameInfoDevice(const int iteration) +void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) { - if (!iteration) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { - LOGP(info, "gpu-transfer: loading {} tfinfo on layer {}, for {} MB.", mTrackingFrameInfo[iLayer].size(), iLayer, mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo) / MB); - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDevice[iLayer]), mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), nullptr, getExtAllocator()); - // Register and move data - checkGPUError(cudaHostRegister(mTrackingFrameInfo[iLayer].data(), mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackingFrameInfoDevice[iLayer], mTrackingFrameInfo[iLayer].data(), mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaMemcpyHostToDevice, mGpuStreams[0].get())); - } - allocMemAsync(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackingFrameInfoDeviceArray, mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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::loadTrackletsDevice() +void TimeFrameGPU::downloadCellsLUTDevice() { - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} tracklets on layer {}, for {} MB.", mTracklets[iLayer].size(), iLayer, mTracklets[iLayer].size() * sizeof(Tracklet) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mTracklets[iLayer].size() * sizeof(Tracklet), nullptr, getExtAllocator()); - // Register and move data - checkGPUError(cudaHostRegister(mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsDevice[iLayer], mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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())); } - allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); } template -void TimeFrameGPU::loadCellsDevice() +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) { - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading {} cell seeds on layer {}, for {} MB.", mCells[iLayer].size(), iLayer, mCells[iLayer].size() * sizeof(CellSeed) / MB); - allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), mCells[iLayer].size() * sizeof(CellSeed), nullptr, getExtAllocator()); - // Register and move data - checkGPUError(cudaHostRegister(mCells[iLayer].data(), mCells[iLayer].size() * sizeof(CellSeed), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mCellsDevice[iLayer], mCells[iLayer].data(), mCells[iLayer].size() * sizeof(CellSeed), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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); + } + }; + 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); } - allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + 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 -void TimeFrameGPU::loadRoadsDevice() +void TimeFrameGPU::pushMemoryStack(const int iteration) { - LOGP(debug, "gpu-transfer: loading {} roads, for {} MB.", mRoads.size(), mRoads.size() * sizeof(Road) / MB); - allocMemAsync(reinterpret_cast(&mRoadsDevice), mRoads.size() * sizeof(Road), &(mGpuStreams[0]), getExtAllocator()); - checkGPUError(cudaHostRegister(mRoads.data(), mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mRoadsDevice, mRoads.data(), mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + // mark the beginning of memory marked with MEMORY_STACK that can be discarded + // after doing one iteration + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); } template -void TimeFrameGPU::loadTrackSeedsDevice(std::vector& seeds) +void TimeFrameGPU::popMemoryStack(const int iteration) { - LOGP(debug, "gpu-transfer: loading {} track seeds, for {} MB.", seeds.size(), seeds.size() * sizeof(CellSeed) / MB); - allocMemAsync(reinterpret_cast(&mTrackSeedsDevice), seeds.size() * sizeof(CellSeed), &(mGpuStreams[0]), getExtAllocator()); - checkGPUError(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeed), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackSeedsDevice, seeds.data(), seeds.size() * sizeof(CellSeed), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + // pop all memory on the stack from this iteration + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); } template -void TimeFrameGPU::createCellNeighboursDevice(const unsigned int& layer, std::vector>& neighbours) +void TimeFrameGPU::initialise(const int iteration, + const TrackingParameters& trkParam, + const int maxLayers, + IndexTableUtilsN* utils, + const TimeFrameGPUParameters* gpuParam) { - mCellsNeighbours[layer].clear(); - mCellsNeighbours[layer].resize(neighbours.size()); - LOGP(debug, "gpu-allocation: reserving {} neighbours, for {} MB.", neighbours.size(), neighbours.size() * sizeof(gpuPair) / MB); - allocMemAsync(reinterpret_cast(&mNeighboursDevice[layer]), neighbours.size() * sizeof(gpuPair), &(mGpuStreams[0]), getExtAllocator()); - checkGPUError(cudaMemsetAsync(mNeighboursDevice[layer], 0, neighbours.size() * sizeof(gpuPair), mGpuStreams[0].get())); - checkGPUError(cudaHostRegister(neighbours.data(), neighbours.size() * sizeof(std::pair), cudaHostRegisterPortable)); + mGpuStreams.resize(nLayers); + o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); } template -void TimeFrameGPU::createTrackITSExtDevice(std::vector& seeds) +void TimeFrameGPU::syncStream(const size_t stream) { - mTrackITSExt.clear(); - mTrackITSExt.resize(seeds.size()); - LOGP(debug, "gpu-allocation: reserving {} tracks, for {} MB.", seeds.size(), seeds.size() * sizeof(o2::its::TrackITSExt) / MB); - allocMemAsync(reinterpret_cast(&mTrackITSExtDevice), seeds.size() * sizeof(o2::its::TrackITSExt), &(mGpuStreams[0]), getExtAllocator()); - checkGPUError(cudaMemsetAsync(mTrackITSExtDevice, 0, seeds.size() * sizeof(o2::its::TrackITSExt), mGpuStreams[0].get())); - checkGPUError(cudaHostRegister(mTrackITSExt.data(), seeds.size() * sizeof(o2::its::TrackITSExt), cudaHostRegisterPortable)); + mGpuStreams[stream].sync(); } template -void TimeFrameGPU::downloadTrackITSExtDevice(std::vector& seeds) +void TimeFrameGPU::syncStreams(const bool device) { - LOGP(debug, "gpu-transfer: downloading {} tracks, for {} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / MB); - checkGPUError(cudaMemcpyAsync(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); - checkGPUError(cudaHostUnregister(mTrackITSExt.data())); - checkGPUError(cudaHostUnregister(seeds.data())); - discardResult(cudaDeviceSynchronize()); + mGpuStreams.sync(device); } template -unsigned char* TimeFrameGPU::getDeviceUsedClusters(const int layer) +void TimeFrameGPU::waitEvent(const int stream, const int event) { - return mUsedClustersDevice[layer]; + mGpuStreams.waitEvent(stream, event); } template -gsl::span TimeFrameGPU::getHostNTracklets(const int chunkId) +void TimeFrameGPU::recordEvent(const int event) { - return gsl::span(mHostNTracklets.data() + (nLayers - 1) * chunkId, nLayers - 1); + 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 +} // 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 0bca6360d268c..7c42658242231 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu @@ -13,7 +13,7 @@ #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); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index ef444cdc79b92..42d2227de60f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -11,413 +11,365 @@ /// #include -#include -#include +#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 +namespace o2::its { -namespace its -{ -constexpr int UnusedIndex{-1}; template void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { - mTimeFrameGPU->initialiseHybrid(iteration, mTrkParams[iteration], nLayers); - mTimeFrameGPU->loadTrackingFrameInfoDevice(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::computeLayerTracklets(const int iteration, int, int) +void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) { - // 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()); - - // 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); - // //////////////////// - // /// Tracklet finding - - // 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())); - // } - // } - - // //////////////// - // /// Cell finding - // 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()); - // } - // } - // } - - // ///////////////////// - // /// Neighbour finding - // 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); - // // } - // } - // // Download cells into vectors - - // for (int iLevel{nLayers - 2}; iLevel >= mTrkParams[iteration].CellMinimumLevel(); --iLevel) { - // const int minimumLevel{iLevel - 1}; - // for (int iLayer{nLayers - 3}; iLayer >= minimumLevel; --iLayer) { - // // gpu::computeLayerRoadsKernel<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLevel, // const int level, - // // iLayer, // const int layerIndex, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayCells(), // const CellSeed** cells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), // const int* nCells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCell(), // const int** neighbours, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCellLUT(), // const int** neighboursLUT, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoads(), // Road* roads, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoadsLookupTables(iLayer)); // int* roadsLookupTable - // } - // } - - // // End of tracking for this chunk - // offset += rofs; - // } - // }; - // threads[chunkId] = std::thread(doTrackReconstruction); - // } - // for (auto& thread : threads) { - // thread.join(); - // } - - // mTimeFrameGPU->wipe(nLayers); + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); } template -void TrackerTraitsGPU::computeLayerCells(const int iteration) +void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) { -} - -template -void TrackerTraitsGPU::findCellsNeighbours(const int iteration) -{ -} + 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); + } -template -void TrackerTraitsGPU::extendTracks(const int iteration) -{ + 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::setBz(float bz) +void TrackerTraitsGPU::computeLayerCells(const int iteration) { - mBz = bz; - mTimeFrameGPU->setBz(bz); -} + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); -template -int TrackerTraitsGPU::getTFNumberOfClusters() const -{ - return mTimeFrameGPU->getNumberOfClusters(); -} + // 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); + } -template -int TrackerTraitsGPU::getTFNumberOfTracklets() const -{ - return mTimeFrameGPU->getNumberOfTracklets(); -} + for (int iLayer{this->mTrkParams[iteration].CellsPerRoad()}; iLayer--;) { + if (iLayer) { + mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer - 1); + mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer - 1); + mTimeFrameGPU->recordEvent(iLayer - 1); + } -template -int TrackerTraitsGPU::getTFNumberOfCells() const -{ - return mTimeFrameGPU->getNumberOfCells(); -} + // 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; + } -//////////////////////////////////////////////////////////////////////////////// -// Hybrid tracking -template -void TrackerTraitsGPU::computeTrackletsHybrid(const int iteration, int iROFslice, int iVertex) -{ - TrackerTraits::computeLayerTracklets(iteration, iROFslice, iVertex); + 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::computeCellsHybrid(const int iteration) -{ - TrackerTraits::computeLayerCells(iteration); -}; - -template -void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) +void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { - TrackerTraits::findCellsNeighbours(iteration); - // for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { - // const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getCells()[iLayer + 1].size())}; - // mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].clear(); - // mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].resize(nextLayerCellsNum, 0); - // if (mTimeFrameGPU->getCells()[iLayer + 1].empty() || - // mTimeFrameGPU->getCellsLookupTable()[iLayer].empty()) { - // mTimeFrameGPU->getCellsNeighbours()[iLayer].clear(); - // continue; - // } - - // int layerCellsNum{static_cast(mTimeFrameGPU->getCells()[iLayer].size())}; - // std::vector> cellsNeighbours; - // cellsNeighbours.reserve(nextLayerCellsNum); - // mTimeFrameGPU->createCellNeighboursDevice(iLayer, cellsNeighbours); - - // // // [...] - // // cellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbours(iLayer)); - // // // // 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())); + const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // // cellsNeighboursHandler(mTimeFrameGPU->getDeviceNeighbours(iLayer)); - // // // [...] - - // std::sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const std::pair& a, const std::pair& b) { - // return a.second < b.second; - // }); - // mTimeFrameGPU->getCellsNeighbours()[iLayer].clear(); - // mTimeFrameGPU->getCellsNeighbours()[iLayer].reserve(cellsNeighbours.size()); - // for (auto& cellNeighboursIndex : cellsNeighbours) { - // mTimeFrameGPU->getCellsNeighbours()[iLayer].push_back(cellNeighboursIndex.first); - // } - // std::inclusive_scan(mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].begin(), mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].end(), mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].begin()); - // } -}; + 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) { - for (int startLevel{mTrkParams[iteration].CellsPerRoad()}; startLevel >= mTrkParams[iteration].CellMinimumLevel(); --startLevel) { + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; - std::vector trackSeeds; - for (int startLayer{mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { - std::vector lastCellId, updatedCellId; - std::vector lastCellSeed, updatedCellSeed; - - processNeighbours(startLayer, startLevel, mTimeFrame->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); - - int level = startLevel; - for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { - lastCellSeed.swap(updatedCellSeed); - lastCellId.swap(updatedCellId); - updatedCellSeed.clear(); - updatedCellId.clear(); - processNeighbours(iLayer, --level, lastCellSeed, lastCellId, updatedCellSeed, updatedCellId); - } - for (auto& seed : updatedCellSeed) { - if (seed.getQ2Pt() > 1.e3 || seed.getChi2() > mTrkParams[0].MaxChi2NDF * ((startLevel + 2) * 2 - 5)) { - continue; - } - trackSeeds.push_back(seed); + 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]); } - if (!trackSeeds.size()) { - LOGP(info, "No track seeds found, skipping track finding"); + // 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->createTrackITSExtDevice(trackSeeds); mTimeFrameGPU->loadTrackSeedsDevice(trackSeeds); - trackSeedHandler( - mTimeFrameGPU->getDeviceTrackSeeds(), // CellSeed* trackSeeds, - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** foundTrackingFrameInfo, - mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* tracks, - trackSeeds.size(), // const size_t nSeeds, - mBz, // const float Bz, - startLevel, // const int startLevel, - mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment, - mTrkParams[0].MaxChi2NDF, // float maxChi2NDF, - mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator - mCorrType); // o2::base::PropagatorImpl::MatCorrType - - mTimeFrameGPU->downloadTrackITSExtDevice(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(); - std::sort(tracks.begin(), tracks.end(), [](const TrackITSExt& a, const TrackITSExt& b) { - return a.getChi2() < b.getChi2(); - }); for (auto& track : tracks) { if (!track.getChi2()) { @@ -425,25 +377,25 @@ void TrackerTraitsGPU::findRoads(const int iteration) } int nShared = 0; bool isFirstShared{false}; - for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == UnusedIndex) { + for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } - nShared += int(mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); - isFirstShared |= !iLayer && mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); + isFirstShared |= !iLayer && mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); } - if (nShared > mTrkParams[0].ClusterSharing) { + if (nShared > this->mTrkParams[0].ClusterSharing) { continue; } std::array rofs{INT_MAX, INT_MAX, INT_MAX}; - for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == UnusedIndex) { + for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } - mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); - int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); + 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; @@ -459,14 +411,38 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (rofs[1] != INT_MAX) { track.setNextROFbit(); } - mTimeFrame->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); } + mTimeFrameGPU->loadUsedClustersDevice(); } - if (iteration == 2) { - mTimeFrameGPU->unregisterHostMemory(0); // FIXME this needs to work also with sync - } + // 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 its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 080c7aafaf4e5..eacf514c7a91d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -12,13 +12,11 @@ #include #include -#include -#include #include -#include #include #include +#include #include #include #include @@ -26,93 +24,89 @@ #include #include "ITStracking/Constants.h" -#include "ITStracking/Configuration.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/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif +#include "ITStrackingGPU/Utils.h" +#include "utils/strtag.h" // O2 track model #include "ReconstructionDataFormats/Track.h" #include "DetectorsBase/Propagator.h" using namespace o2::track; -#define gpuCheckError(x) \ - { \ - gpuAssert((x), __FILE__, __LINE__); \ - } -inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = true) +namespace o2::its { - if (code != cudaSuccess) { - LOGF(error, "GPUassert: %s %s %d", cudaGetErrorString(code), file, line); - if (abort) { - throw std::runtime_error("GPU assert failed."); - } - } -} - -namespace o2 -{ -namespace its -{ -using namespace constants::its2; - namespace gpu { -GPUd() bool fitTrack(TrackITSExt& track, - int start, - int end, - int step, - float chi2clcut, - float chi2ndfcut, - float maxQoverPt, - int nCl, - float Bz, - TrackingFrameInfo** tfInfos, - const o2::base::Propagator* prop, - o2::base::PropagatorF::MatCorrType matCorrType) + +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::its::UnusedIndex) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } const TrackingFrameInfo& trackingHit = tfInfos[iLayer][track.getClusterIndex(iLayer)]; - 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 (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)) { - if (matCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - track.setChi2(track.getChi2() + track.getPredictedChi2(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)); - if (!track.TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { return false; } - const float xx0 = (iLayer > 2) ? 1.e-2f : 5.e-3f; // Rough layer thickness - constexpr float radiationLength = 9.36f; // Radiation length of Si [cm] - constexpr float density = 2.33f; // Density of Si [g/cm^3] - if (!track.correctForMaterial(xx0, xx0 * radiationLength * density, true)) { + 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; } @@ -120,36 +114,199 @@ GPUd() bool fitTrack(TrackITSExt& track, 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::GPUCommonMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); + 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 -GPUg() void fitTrackSeedsKernel( - CellSeed* trackSeeds, - TrackingFrameInfo** foundTrackingFrameInfo, +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, - const size_t nSeeds, - const float Bz, + maybe_const* seedLUT, + const float* layerRadii, + const float* minPts, + const unsigned int nSeeds, + const float bz, const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, + 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) { - auto& seed = trackSeeds[iCurrentTrackSeedIndex]; - TrackITSExt temporaryTrack{seed}; - - temporaryTrack.resetCovariance(); - temporaryTrack.setChi2(0); - int* clusters = seed.getClusters(); - for (int iL{0}; iL < 7; ++iL) { - temporaryTrack.setExternalClusterIndex(iL, clusters[iL], clusters[iL] != constants::its::UnusedIndex); + 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, @@ -158,17 +315,20 @@ GPUg() void fitTrackSeedsKernel( maxChi2NDF, // float maxChi2NDF, o2::constants::math::VeryBig, // float maxQoverPt, 0, // nCl, - Bz, // float Bz, + bz, // float bz, foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, propagator, // const o2::base::Propagator* propagator, - matCorrType); // o2::base::PropagatorF::MatCorrType matCorrType + 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, @@ -177,395 +337,331 @@ GPUg() void fitTrackSeedsKernel( maxChi2NDF, // float maxChi2NDF, 50.f, // float maxQoverPt, 0, // nCl, - Bz, // float Bz, + bz, // float bz, foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, propagator, // const o2::base::Propagator* propagator, - matCorrType); // o2::base::PropagatorF::MatCorrType matCorrType - if (!fitSuccess) { + matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType + &linRef, + shifRefToCluster); + if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { continue; } - tracks[iCurrentTrackSeedIndex] = temporaryTrack; + 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 // Version for new tracker to supersede the old one -GPUg() void computeLayerCellNeighboursKernel( - CellSeed* cellsCurrentLayer, - CellSeed* cellsNextLayer, +template +GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( + CellSeed** cellSeedArray, int* neighboursLUT, - const int* cellsNextLayerLUT, + int* neighboursIndexTable, + int** cellsLUTs, gpuPair* cellNeighbours, + const Tracklet** tracklets, + const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, - const int* nCells, + const unsigned int nCells, const int maxCellNeighbours = 1e2) { - for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells[layerIndex]; iCurrentCellIndex += blockDim.x * gridDim.x) { - const auto& currentCellSeed{cellsCurrentLayer[iCurrentCellIndex]}; + 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{cellsNextLayerLUT[nextLayerTrackletIndex]}; - const int nextLayerLastCellIndex{cellsNextLayerLUT[nextLayerTrackletIndex + 1]}; + 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) { - CellSeed nextCellSeed{cellsNextLayer[iNextCell]}; // Copy + 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 { - if (foundNeighbours >= maxCellNeighbours) { - printf("its-gpu-neighbours-finder: data loss on layer: %d: number of neightbours exceeded the threshold!\n"); - continue; - } - cellNeighbours[neighboursLUT[iNextCell] + foundNeighbours++] = {iCurrentCellIndex, iNextCell}; - - // FIXME: this is prone to race conditions: check on level is not atomic + cellNeighbours[neighboursIndexTable[iCurrentCellIndex] + foundNeighbours] = {iCurrentCellIndex, iNextCell}; + foundNeighbours++; const int currentCellLevel{currentCellSeed.getLevel()}; if (currentCellLevel >= nextCellSeed.getLevel()) { - atomicExch(cellsNextLayer[iNextCell].getLevelPtr(), currentCellLevel + 1); // Update level on corresponding cell + atomicMax(cellSeedArray[layerIndex + 1][iNextCell].getLevelPtr(), currentCellLevel + 1); } } } } } -//////////////////////////////////////////////////////////////////////////////// -// Legacy Kernels, to possibly take inspiration from -//////////////////////////////////////////////////////////////////////////////// -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; -} - -// Functors to sort tracklets -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); - } -}; - -// Print layer buffer -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"); - } -} - -// 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) +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) { - 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); + 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; } } - } -} - -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]) { + 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 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; + int foundCells{0}; + for (int iNextTrackletIndex{nextLayerFirstTrackletIndex}; iNextTrackletIndex < nextLayerLastTrackletIndex; ++iNextTrackletIndex) { + if (tracklets[layer + 1][iNextTrackletIndex].firstClusterIndex != nextLayerClusterIndex) { + break; } - 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{o2::gpu::CAMath::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) { + const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; + if (deltaROF && currentTracklet.getSpanRof(nextTracklet) > deltaROF) { 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 + const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - if (!(roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { // number of clusters on next layer > 0 + 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; } - 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 constexpr (!initRun) { + new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; } + ++foundCells; } } - // 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); + if constexpr (initRun) { + cellsLUTs[layer][iCurrentTrackletIndex] = foundCells; } } } -template -GPUg() void computeLayerTrackletsKernelMultipleRof( +template +GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, + const uint8_t* multMask, 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 float meanDeltaR = -42.f, + const float MSAngle = -42.f) { 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 = o2::gpu::GPUCommonMath::Min(roFrameClustersCurrentLayer[rof0 + 1] - roFrameClustersCurrentLayer[rof0], (int)maxClustersPerRof); - // 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); - // } - 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) { + 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 Cluster& currentCluster{clustersCurrentLayerRof[currentClusterIndex]}; - const int currentSortedIndex{roFrameClustersCurrentLayer[rof0] + currentClusterIndex}; - const int currentSortedIndexChunk{currentSortedIndex - roFrameClustersCurrentLayer[startRofId]}; - if (usedClustersLayer[currentSortedIndex]) { + 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; + } + } - 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 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; + } - 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 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 / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{o2::gpu::CAMath::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)}; - + 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 += trkPars->PhiBins; + phiBinsNum += 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 + + 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) % trkPars->PhiBins; + 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 = indexTablesNext[(rof1 - startRofId) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= nClustersNext) { + 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; } - auto nextClusterIndex{roFrameClustersNextLayer[rof1] - roFrameClustersNextLayer[startRofId] + iNextCluster}; const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { + if (usedClusters[layerIndex + 1][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)}; + 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}; } - // 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; } } @@ -576,169 +672,852 @@ GPUg() void computeLayerTrackletsKernelMultipleRof( } } -// Decrease LUT entries corresponding to duplicated tracklets. NB: duplicate tracklets are removed separately (see const Tracklets*). -GPUg() void removeDuplicateTrackletsEntriesLUTKernel( - int* trackletsLookUpTable, +GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( 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, - CellSeed* cells, - int* cellsLUT, - const StaticTrackingParameters* trkPars) + int* trackletsLookUpTable, + const int nTracklets) { - 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)}; - - if (deltaTanLambda / trkPars->CellDeltaTanLambdaSigma < trkPars->NSigmaCut) { - if constexpr (!initRun) { - new (cells + cellsLUT[iCurrentTrackletIndex] + foundCells) Cell{currentTracklet.firstClusterIndex, nextTracklet.firstClusterIndex, - nextTracklet.secondClusterIndex, - iCurrentTrackletIndex, - iNextTrackletIndex}; - } - ++foundCells; - } - } - if constexpr (initRun) { - // Fill cell Lookup table - cellsLUT[iCurrentTrackletIndex] = foundCells; - } + 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 computeLayerRoadsKernel( +GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( + const int layer, const int level, - const int layerIndex, - CellSeed** cells, - const int* nCells, - int** neighbours, - int** neighboursLUT, - Road* roads, - int* roadsLookupTable) + 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) { - for (int iCurrentCellIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentCellIndex < nCells[layerIndex]; iCurrentCellIndex += blockDim.x * gridDim.x) { - auto& currentCell{cells[layerIndex][iCurrentCellIndex]}; + 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; } - int nRoadsCurrentCell{0}; - if constexpr (dryRun) { - roadsLookupTable[iCurrentCellIndex]++; - } else { - roads[roadsLookupTable[iCurrentCellIndex] + nRoadsCurrentCell++] = Road{layerIndex, iCurrentCellIndex}; - } - if (level == 1) { + 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]}; - const auto currentCellNeighOffset{neighboursLUT[layerIndex - 1][iCurrentCellIndex]}; - const int cellNeighboursNum{neighboursLUT[layerIndex - 1][iCurrentCellIndex + 1] - currentCellNeighOffset}; - bool isFirstValidNeighbour{true}; - for (int iNeighbourCell{0}; iNeighbourCell < cellNeighboursNum; ++iNeighbourCell) { - const int neighbourCellId = neighbours[layerIndex - 1][currentCellNeighOffset + iNeighbourCell]; - const CellSeed& neighbourCell = cells[layerIndex - 1][neighbourCellId]; - if (level - 1 != neighbourCell.getLevel()) { + 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 (isFirstValidNeighbour) { - isFirstValidNeighbour = false; - } else { - if constexpr (dryRun) { - roadsLookupTable[iCurrentCellIndex]++; // dry run we just count the number of roads - } else { - roads[roadsLookupTable[iCurrentCellIndex] + nRoadsCurrentCell++] = Road{layerIndex, iCurrentCellIndex}; + 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; } } - // traverseCellsTreeDevice(neighbourCellId, layerIndex - 1, iCurrentCellIndex, nRoadsCurrentCell, roadsLookupTable, cells, roads); + + 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 cellNeighboursHandler(CellSeed* cellsCurrentLayer, - CellSeed* cellsNextLayer, - int* neighboursLUT, - const int* cellsNextLayerLUT, - gpuPair* cellNeighbours, - const float maxChi2ClusterAttachment, +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 layerIndex, - const int* nCells, - const int maxCellNeighbours = 1e2) + 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) { - gpu::computeLayerCellNeighboursKernel<<<20, 512>>>( - cellsCurrentLayer, // CellSeed* cellsCurrentLayer, - cellsNextLayer, // CellSeed* cellsNextLayer, - neighboursLUT, // int* neighboursLUT, - cellsNextLayerLUT, // const int* cellsNextLayerLUT, - cellNeighbours, // gpuPair* cellNeighbours, - maxChi2ClusterAttachment, // const float maxChi2ClusterAttachment, - bz, // const float bz, - layerIndex, // const int layerIndex, - nCells, // const int* nCells, - maxCellNeighbours); // const int maxCellNeighbours = 1e2 + // 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); } -void trackSeedHandler(CellSeed* trackSeeds, - TrackingFrameInfo** foundTrackingFrameInfo, - o2::its::TrackITSExt* tracks, - const size_t nSeeds, - const float Bz, - const int startLevel, - float maxChi2ClusterAttachment, - float maxChi2NDF, - const o2::base::Propagator* propagator, - const o2::base::PropagatorF::MatCorrType matCorrType) +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) { - gpu::fitTrackSeedsKernel<<<20, 256>>>( - trackSeeds, // CellSeed* trackSeeds, - foundTrackingFrameInfo, // TrackingFrameInfo** foundTrackingFrameInfo, - tracks, // o2::its::TrackITSExt* tracks, - nSeeds, // const size_t nSeeds, - Bz, // const float Bz, - startLevel, // const int startLevel, - maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, - maxChi2NDF, // float maxChi2NDF, - propagator, // const o2::base::Propagator* propagator - matCorrType); // o2::base::PropagatorF::MatCorrType matCorrType - - gpuCheckError(cudaPeekAtLastError()); - gpuCheckError(cudaDeviceSynchronize()); + 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()); } -} // namespace its -} // namespace o2 + +/// 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 99a24f347bd48..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/Utils.cu +++ /dev/null @@ -1,289 +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 "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/Context.h" -#include "ITStracking/Constants.h" - -#include -#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__); -} -} // 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 9a1ed507ae5a4..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cu +++ /dev/null @@ -1,787 +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 -#include - -#include - -#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() float smallestAngleDifference(float a, float b) -{ - float diff = fmod(b - a + constants::math::Pi, constants::math::TwoPi) - constants::math::Pi; - return (diff < -constants::math::Pi) ? diff + constants::math::TwoPi : ((diff > constants::math::Pi) ? diff - constants::math::TwoPi : diff); -} - -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() -{ -} - -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(smallestAngleDifference(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(smallestAngleDifference(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::GpuTimer timer{offset, mTimeFrameGPU->getStream(chunkId).get()}; - // timer.Start("vtTrackletFinder"); - 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 - - 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, - 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/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index 3c5614b172040..a40aac491a386 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -12,16 +12,17 @@ if(HIP_ENABLED) message(STATUS "Building ITS HIP tracker") 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/Context.cu ../cuda/TimeFrameGPU.cu - ../cuda/Stream.cu ../cuda/TrackerTraitsGPU.cxx ../cuda/TracerGPU.cu ../cuda/TrackingKernels.cu - ../cuda/VertexerTraitsGPU.cu - ../cuda/Utils.cu + ../cuda/VertexingKernels.cu + ../cuda/VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUTracking 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 482bb38b19bad..902092a510eb0 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -16,24 +16,15 @@ #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); - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; @@ -44,44 +35,22 @@ class Cell final GPUhd() int* getLevelPtr() { return &mLevel; } private: - const int mFirstClusterIndex; - const int mSecondClusterIndex; - const int mThirdClusterIndex; - const int mFirstTrackletIndex; - const int mSecondTrackletIndex; - 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}, - mLevel{0} -{ - // Nothing to do -} - -GPUdi() Cell::Cell(const int firstClusterIndex, const int secondClusterIndex, const int thirdClusterIndex, - const int firstTrackletIndex, const int secondTrackletIndex) - : mFirstClusterIndex{firstClusterIndex}, - mSecondClusterIndex{secondClusterIndex}, - mThirdClusterIndex{thirdClusterIndex}, - mFirstTrackletIndex{firstTrackletIndex}, - mSecondTrackletIndex{secondTrackletIndex}, - mLevel{1} -{ - // Nothing to do -} - +template class CellSeed final : public o2::track::TrackParCovF { public: GPUhdDefault() CellSeed() = default; - GPUhdDefault() CellSeed(const CellSeed&) = default; - GPUd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2) : o2::track::TrackParCovF{tpc}, mLevel{1}, mChi2{chi2} + 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; @@ -89,6 +58,12 @@ class CellSeed final : public o2::track::TrackParCovF 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; + GPUhd() int getFirstClusterIndex() const { return mClusters[getUserField()]; }; GPUhd() int getSecondClusterIndex() const { return mClusters[getUserField() + 1]; }; GPUhd() int getThirdClusterIndex() const { return mClusters[getUserField() + 2]; }; @@ -96,21 +71,32 @@ class CellSeed final : public o2::track::TrackParCovF GPUhd() void setFirstTrackletIndex(int trkl) { mTracklets[0] = trkl; }; GPUhd() int getSecondTrackletIndex() const { return mTracklets[1]; }; GPUhd() void setSecondTrackletIndex(int trkl) { mTracklets[1] = trkl; }; - GPUhd() int getChi2() const { return mChi2; }; + 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() int* getClusters() { return mClusters; } + 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: - int mClusters[7] = {-1, -1, -1, -1, -1, -1, -1}; - int mTracklets[2] = {-1, -1}; - int mLevel = 0; - float mChi2 = 0.f; + float mChi2 = -999.f; + int mLevel = constants::UnusedIndex; + std::array mTracklets = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); }; -} // namespace its -} // namespace o2 +} // namespace o2::its + #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 0f136edfebfb3..b96f0558943a6 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -16,73 +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 "GPUCommonArray.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 -{ -#if !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT)) - printf("Cluster: %f %f %f %f %f %d %d\n", xCoordinate, yCoordinate, zCoordinate, phi, radius, clusterId, indexTableBinIndex); -#endif -} - -struct TrackingFrameInfo { - TrackingFrameInfo() = default; - TrackingFrameInfo(float x, float y, float z, float xTF, float alpha, o2::gpu::gpustd::array&& posTF, o2::gpu::gpustd::array&& covTF); - - float xCoordinate; - float yCoordinate; - float zCoordinate; - float xTrackingFrame; - float alphaTrackingFrame; - o2::gpu::gpustd::array positionTrackingFrame = {-1., -1.}; - o2::gpu::gpustd::array covarianceTrackingFrame = {999., 999., 999.}; - GPUdi() void print() const - { -#if !defined(GPUCA_GPUCODE_DEVICE) || (!defined(__OPENCL__) && defined(GPUCA_GPU_DEBUG_PRINT)) - printf("x: %f y: %f z: %f xTF: %f alphaTF: %f posTF: %f %f covTF: %f %f %f\n", - xCoordinate, yCoordinate, zCoordinate, xTrackingFrame, alphaTrackingFrame, - positionTrackingFrame[0], positionTrackingFrame[1], - covarianceTrackingFrame[0], covarianceTrackingFrame[1], covarianceTrackingFrame[2]); -#endif - } +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; + + GPUhd() void print() const; + + 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 550b8405770dc..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]) @@ -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 a700dc1e806c0..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,43 +26,15 @@ #include "DetectorsBase/Propagator.h" #include "ITStracking/Constants.h" -namespace o2 +namespace o2::its { -namespace its -{ - -enum class TrackingMode { - Sync, - Async, - Cosmics, - Unset, // Special value to leave a default in case we want to override via Configurable Params -}; - -std::string asString(TrackingMode mode); -std::ostream& operator<<(std::ostream& os, TrackingMode v); - -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; @@ -79,6 +51,7 @@ struct TrackingParameters { float Diamond[3] = {0.f, 0.f, 0.f}; /// General parameters + bool AllowSharingFirstCluster = false; int ClusterSharing = 0; int MinTrackLength = 7; float NSigmaCut = 5; @@ -91,27 +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; @@ -127,12 +117,19 @@ 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; + std::string asString() const; size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes size_t maxTrackletsPerCluster = 1e2; @@ -151,7 +148,24 @@ struct TimeFrameGPUParameters { int maxGPUMemoryGB = -1; }; -} // namespace its -} // namespace o2 +namespace TrackingMode +{ +enum Type : int8_t { + Unset = -1, // Special value to leave a default in case we want to override via Configurable Params + Sync = 0, + Async = 1, + Cosmics = 2, + Off = 3, +}; + +Type fromString(std::string_view str); +std::string toString(Type mode); + +std::vector getTrackingParameters(Type mode); +std::vector getVertexingParameters(Type mode); + +}; // namespace TrackingMode + +} // 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 6324b03cb8ca6..22642f2e23229 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -16,109 +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 index 9bdb2905ba9ba..7d1e98736db2c 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ExternalAllocator.h @@ -16,13 +16,69 @@ #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 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/IOUtils.h index 57cc44291ba09..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 ed4027f77f360..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[8] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - float mInverseZBinSize[8] = {0.f, 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 9093609144283..c5c1e4a8ce220 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -13,92 +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 o2::its::math_utils { -namespace math_utils +GPUhdi() float computePhi(float x, float y) { -GPUhdni() float computePhi(const float, const float); -GPUhdni() float hypot(const float, const float); -GPUhdni() constexpr float getNormalizedPhi(const float); -GPUhdni() constexpr float3 crossProduct(const float3&, const float3&); -GPUhdni() float computeCurvature(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeCurvatureCentreX(float x1, float y1, float x2, float y2, float x3, float y3); -GPUhdni() float computeTanDipAngle(float x1, float y1, float x2, float y2, float z1, float z2); - -} // namespace math_utils - -GPUhdi() float math_utils::computePhi(const float x, const float y) -{ - //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) -{ - 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)); - const float den2 = (d * x1 - a) * (d * x1 - a) + (d * y1 - b) * (d * y1 - b); - return den2 > 0.f ? -1.f * d / o2::gpu::CAMath::Sqrt(den2) : 0.f; + 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) { + // in case the triangle is degenerate we return set the centre to infinity. float dx21 = x2 - x1, dx32 = x3 - x2; - if (dx21 == 0.f || dx32 == 0.f) { // add small offset + 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; } - float k1 = (y2 - y1) / dx21, k2 = (y3 - y2) / dx32; - return (k1 != k2) ? 0.5f * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1) : 1e5; + 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) { - return (z1 - z2) / o2::gpu::CAMath::Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + // 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; } -} // namespace its -} // namespace o2 +GPUhdi() float smallestAngleDifference(float a, float b) +{ + return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); +} -#endif /* TRACKINGITSU_INCLUDE_CAUTILS_H_ */ +GPUhdi() float Sq(float v) +{ + return v * v; +} + +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 d35e5bc545904..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; - const gsl::span getClusterLabels(int layerId, const int clId) const; - 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; - const 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 const gsl::span ROframe::getClusterLabels(int layerId, const int clId) const -{ - return mMClabels->getLabels(getClusterExternalIndex(layerId, clId)); -} - -inline const 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 = 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 bc3786ba612b9..009f3a1b5b146 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h @@ -16,44 +16,45 @@ #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 +template class Road final { public: - GPUhd() Road() : mCellIds{}, mRoadSize{}, mIsFakeRoad{} { resetRoad(); } + GPUhdDefault() Road() = default; GPUhd() Road(int cellLayer, int cellId) : Road() { addCell(cellLayer, cellId); } - GPUhd() int getRoadSize() const; - int getLabel() const; - void setLabel(const int); - GPUhd() bool isFakeRoad() const; - void setFakeRoad(const bool); - GPUhd() int& operator[](const int&); - GPUhd() int operator[](const int&) const; + 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::its::UnusedIndex; + mCellIds[i] = constants::UnusedIndex; } mRoadSize = 0; } GPUhd() void addCell(int cellLayer, int cellId) { - if (mCellIds[cellLayer] == constants::its::UnusedIndex) { + if (mCellIds[cellLayer] == constants::UnusedIndex) { ++mRoadSize; } @@ -61,42 +62,11 @@ class Road final } private: - int mCellIds[maxRoadSize]; - // int mLabel; - unsigned char mRoadSize; - bool mIsFakeRoad; + std::array mCellIds = constants::helpers::initArray(); + unsigned char mRoadSize{0}; + bool mIsFakeRoad{false}; }; -template -GPUhdi() int Road::getRoadSize() const -{ - return mRoadSize; -} - -template -GPUhdi() int& Road::operator[](const int& i) -{ - return mCellIds[i]; -} - -template -GPUhdi() int Road::operator[](const int& i) const -{ - return mCellIds[i]; -} - -template -GPUhdi() bool Road::isFakeRoad() const -{ - return mIsFakeRoad; -} - -template -inline void Road::setFakeRoad(const bool isFakeRoad) -{ - mIsFakeRoad = isFakeRoad; -} -} // namespace its -} // namespace o2 +} // namespace o2::its -#endif \ No newline at end of file +#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 5d222e58f2586..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" @@ -35,7 +32,7 @@ #include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/ExternalAllocator.h" - +#include "ITStracking/BoundedAllocator.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -59,50 +56,54 @@ 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: - friend class TimeFrameGPU; - 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; - bool isGPU() const { return mIsGPU; } - 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) @@ -111,128 +112,146 @@ class TimeFrame 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; - int getClusterSize(int clusterId) const; - void setClusterSize(const std::vector& v) { mClusterSize = v; }; + 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(); + auto& getCells() { return mCells; } - std::vector>& getCellsLookupTable(); - std::vector>& getCellsNeighbours(); - std::vector>& getCellsNeighboursLUT(); - 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& 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; - int getNumberOfCells() const; - int getNumberOfTracklets() const; - int getNumberOfNeighbours() 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; } - void setExternalAllocator(ExternalAllocator* allocator) - { - if (mIsGPU) { - LOGP(debug, "Setting timeFrame allocator to external"); - mAllocator = allocator; - mExtAllocator = true; // to be removed - } else { - LOGP(debug, "External allocator is currently only supported for GPU"); - } - } + /// 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(); } - virtual void setDevicePropagator(const o2::base::PropagatorImpl*){}; + // 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); } - void resizeVectors(int nLayers); - - void setExtAllocator(bool ext) { mExtAllocator = ext; } - bool getExtAllocator() const { return mExtAllocator; } /// Debug and printing void checkTrackletLUTs(); void printROFoffsets(); @@ -242,436 +261,437 @@ class TimeFrame void printCellLUTonLayer(int i); void printTrackletLUTs(); void printCellLUTs(); + void printSliceInfo(const int, const int); - IndexTableUtils mIndexTableUtils; - - bool mIsGPU = false; + IndexTableUtilsN mIndexTableUtils; - 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; - - // State if memory will be externally managed. - bool mExtAllocator = false; - ExternalAllocator* mAllocator = nullptr; - std::vector> mUnsortedClusters; - std::vector> mTracklets; - std::vector> mCells; - std::vector> mCellSeeds; - std::vector> mCellSeedsChi2; - std::vector> mRoads; - std::vector> mTracks; - std::vector> mCellsNeighbours; + 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 - protected: - template - void deepVectorClear(std::vector& vec) - { - std::vector().swap(vec); - } - private: + 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 mClusterSize; - std::vector mMultiplicityCutMask; - std::vector> mPValphaX; /// PV x and alpha for track propagation - std::vector> mTrackletLabels; - std::vector> mCellLabels; - std::vector> mCellsLookupTable; - std::vector> mCellsNeighboursLUT; - std::vector> mTracksLabel; - std::vector mBogusClusters; /// keep track of clusters with wild coordinates - - 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)}; -} - -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::getClusterSize(int clusterId) const -{ - return mClusterSize[clusterId]; + int startIdx{mROFramesClusters[layerId][rofId]}; + return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -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) +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) { - mClusterExternalIndices[layer].push_back(idx); + return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; } -inline bool TimeFrame::hasMCinformation() const -{ - return mClusterLabels; -} - -inline bool TimeFrame::isClusterUsed(int layer, int clusterId) const -{ - return mUsedClusters[layer][clusterId]; -} - -inline void TimeFrame::markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } - -inline std::vector>& TimeFrame::getTracklets() -{ - return mTracklets; -} - -inline std::vector>& TimeFrame::getTrackletsLookupTable() -{ - return mTrackletsLookupTable; -} - -inline void TimeFrame::initialiseRoadLabels() +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; -} + if (rofId < 0 || rofId >= mNrof) { + return {}; + } + auto clusStartIdx{mROFramesClusters[1][rofId]}; -inline std::vector>& TimeFrame::getUnsortedClusters() -{ - return mUnsortedClusters; + return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; } -inline std::vector>& TimeFrame::getCells() { return mCells; } - -inline std::vector>& TimeFrame::getCellsLookupTable() +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - return mCellsLookupTable; + 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::getCellsNeighbours() { return mCellsNeighbours; } -inline std::vector>& TimeFrame::getCellsNeighboursLUT() { return mCellsNeighboursLUT; } - -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::getNumberOfNeighbours() const +template +inline int TimeFrame::getNumberOfNeighbours() const { int n{0}; - for (auto& l : mCellsNeighbours) { + for (const auto& l : mCellsNeighbours) { n += l.size(); } return n; } -inline size_t TimeFrame::getNumberOfTracks() const +template +inline size_t TimeFrame::getNumberOfTracks() const { int nTracks = 0; - for (auto& t : mTracks) { + for (const auto& t : mTracks) { nTracks += t.size(); } return nTracks; } -inline size_t TimeFrame::getNumberOfUsedClusters() const +template +inline size_t TimeFrame::getNumberOfUsedClusters() const { size_t nClusters = 0; - for (auto& layer : mUsedClusters) { + 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 70de43d83d8d2..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,92 +50,85 @@ 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; }); - void clustersToTracksHybrid( - 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; - std::uint32_t mTimeFrameCounter = 0; + 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, int& iROFslice, int& iVertex); - void computeCells(int& iteration); - void findCellsNeighbours(int& iteration); - void findRoads(int& iteration); - - void initialiseTimeFrameHybrid(int& iteration); - void computeTrackletsHybrid(int& iteration, int& iROFslice, int& iVertex); - void computeCellsHybrid(int& iteration); - void findCellsNeighboursHybrid(int& iteration); - void findRoadsHybrid(int& iteration); - void findTracksHybrid(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; 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"; @@ -139,20 +136,17 @@ float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, s logger(sstream.str()); if (mTrkParams[0].SaveTimeBenchmarks) { - std::stringstream str2file; 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(), ' ', '_'); - str2file << taskNameStr << "\t" << diff; - std::ofstream file; - file.open("its_time_benchmarks.txt", std::ios::app); - file << str2file.str() << std::endl; - file.close(); + 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 1930bc8761c66..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 @@ -46,118 +38,97 @@ namespace its { class TrackITSExt; +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 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 initialiseTimeFrameHybrid(const int iteration) { LOGP(error, "initialiseTimeFrameHybrid: this method should never be called with CPU traits"); } - virtual void computeTrackletsHybrid(const int iteration, int, int) { LOGP(error, "computeTrackletsHybrid: this method should never be called with CPU traits"); } - virtual void computeCellsHybrid(const int iteration) { LOGP(error, "computeCellsHybrid: this method should never be called with CPU traits"); } - virtual void findCellsNeighboursHybrid(const int iteration) { LOGP(error, "findCellsNeighboursHybrid: this method should never be called with CPU traits"); } - virtual void findRoadsHybrid(const int iteration) { LOGP(error, "findRoadsHybrid: this method should never be called with CPU traits"); } - virtual void findTracksHybrid(const int iteration) { LOGP(error, "findTracksHybrid: this method should never be called with CPU traits"); } - virtual void findTracks() { LOGP(error, "findTracks: this method is deprecated."); } + + 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 setBz(float bz); + virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); - virtual void processNeighbours(int iLayer, int iLevel, const std::vector& currentCellSeed, const std::vector& currentCellId, std::vector& updatedCellSeed, std::vector& updatedCellId); + 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); + 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(); } o2::gpu::GPUChainITS* getChain() const { return mChain; } // TimeFrame information forwarding - virtual int getTFNumberOfClusters() const; - virtual int getTFNumberOfTracklets() const; - virtual int getTFNumberOfCells() const; - - float mBz = 5.f; + virtual int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } + virtual int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } + virtual int getTFNumberOfCells() const { return mTimeFrame->getNumberOfCells(); } private: - track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3); - 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); + 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; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; protected: - o2::base::PropagatorImpl::MatCorrType mCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; o2::gpu::GPUChainITS* mChain = nullptr; - TimeFrame* mTimeFrame; + TimeFrame* mTimeFrame; std::vector mTrkParams; - bool mIsGPU = false; -}; - -inline float TrackerTraits::getBz() const -{ - return mBz; -} -inline void TrackerTraits::UpdateTrackingParameters(const std::vector& trkPars) -{ - mTrkParams = trkPars; -} - -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z, float maxdeltaz) -{ - return getBinsRect(layerIndex, phi, maxdeltaphi, z, z, maxdeltaz); -} - -inline const int4 TrackerTraits::getBinsRect(const Cluster& currentCluster, int layerIndex, float z1, float z2, float maxdeltaz, float maxdeltaphi) -{ - return getBinsRect(layerIndex, currentCluster.phi, maxdeltaphi, z1, z2, maxdeltaz); -} - -inline void TrackerTraits::initialiseTimeFrame(const int iteration) -{ - mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); - 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 36a5fd63b12d1..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,26 +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; - int deltaRof = 0; - 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; @@ -66,44 +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; - int nOrbitsPerIterations = 0; - int nROFsPerIterations = 0; - bool perPrimaryVertexProcessing = false; - bool saveTimeBenchmarks = false; - bool overrideBeamEstimation = false; // used by gpuwf only - int trackingMode = -1; // -1: unset, 0=sync, 1=async, 2=cosmics used by gpuwf only + 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 maxRoadPerRofSize = 5e2; // pp! - 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 index 1c4d4871ab574..a882ca9b779c4 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -19,27 +19,35 @@ #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 "GPUDataTypes.h" -#include "GPUO2Interface.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} - { - } + mOverrideBeamEstimation{overrBeamEst} {} void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } void setMeanVertex(const o2::dataformats::MeanVertexObject* v) @@ -54,35 +62,41 @@ class ITSTrackingInterface } // Task callbacks void initialise(); - template void run(framework::ProcessingContext& pc); + void printSummary() const; - void updateTimeDependentParams(framework::ProcessingContext& pc); - void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj); + virtual void updateTimeDependentParams(framework::ProcessingContext& pc); + virtual void finaliseCCDB(framework::ConcreteDataMatcher& matcher, void* obj); // Custom - void setTraitsFromProvider(VertexerTraits*, TrackerTraits*, TimeFrame*); - void setTrackingMode(TrackingMode mode = TrackingMode::Unset) - { - if (mode == TrackingMode::Unset) { - LOGP(fatal, "ITS Tracking mode Unset is meant to be a default. Specify the mode"); - } - mMode = mode; - } + 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 mMode = TrackingMode::Unset; + TrackingMode::Type mMode = TrackingMode::Unset; bool mOverrideBeamEstimation = false; const o2::itsmft::TopologyDictionary* mDict = nullptr; - std::unique_ptr mTracker = nullptr; - std::unique_ptr mVertexer = nullptr; - TimeFrame* mTimeFrame = 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 \ No newline at end of file +#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 eb46c60305bda..d66bcd6ee2358 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -21,143 +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" - -class TTree; +#include "ITStracking/BoundedAllocator.h" -namespace o2 +namespace o2::its { -namespace 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 clustersToVerticesHybrid(std::function = [](std::string s) { std::cout << s << std::endl; }); + float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); - void validateTracklets(); - void validateTrackletsHybrid(); template - void findTracklets(T&&... args); + void findTracklets(T&&... args) + { + mTraits->computeTracklets(std::forward(args)...); + } template - void findTrackletsHybrid(T&&... args); + void validateTracklets(T&&... args) + { + mTraits->computeTrackletMatching(std::forward(args)...); + } + template + void findVertices(T&&... args) + { + mTraits->computeVertices(std::forward(args)...); + } - void findTrivialMCTracklets(); - void findVertices(); - void findVerticesHybrid(); - 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); - template - void initialiseVertexerHybrid(T&&... args); - template - void initialiseTimeFrameHybrid(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(); -} - -inline void Vertexer::findVertices() -{ - mTraits->computeVertices(); -} - -template -void Vertexer::initialiseVertexerHybrid(T&&... args) -{ - mTraits->initialiseHybrid(std::forward(args)...); -} - -template -void Vertexer::findTrackletsHybrid(T&&... args) -{ - mTraits->computeTrackletsHybrid(std::forward(args)...); -} - -inline void Vertexer::validateTrackletsHybrid() -{ - mTraits->computeTrackletMatchingHybrid(); -} - -inline void Vertexer::findVerticesHybrid() -{ - mTraits->computeVerticesHybrid(); -} - -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(); @@ -166,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)...); } @@ -179,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 365a24cfaee62..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,109 +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); - // Hybrid - virtual void initialiseHybrid(const TrackingParameters& trackingParams) { initialise(trackingParams); }; - virtual void computeTrackletsHybrid() { computeTracklets(); }; - virtual void computeTrackletMatchingHybrid() { computeTrackletMatching(); }; - virtual void computeVerticesHybrid() { computeVertices(); }; - virtual void adoptTimeFrameHybrid(TimeFrame* tf) { adoptTimeFrame(tf); }; - - void computeVerticesInRof(int, - gsl::span&, - std::vector&, - std::vector&, - std::array&, - std::vector&, - std::vector&, - TimeFrame*, - std::vector*); - - 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; } - VertexingParameters getVertexingParameters() const { 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; @@ -147,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